2013-09-12, sfugcgn: css-selektoren für datenbankabfragen nutzen

35
1 C. Hetzel, 12. Sept. 2013 Datenbankabfragen über CSS- Selektoren Unter Verwendung der CssSelector-Komponente von Symfony2 Datenbankabfragen gestalten. Von Carsten Hetzel.

Upload: carsten-hetzel

Post on 26-Jun-2015

229 views

Category:

Technology


1 download

DESCRIPTION

In diesem Vortrag vom Symfony User Group Cologne Treffen in Köln zeige ich, wie man mit Hilfe von CSS-Selektoren und der CssSelector-Komponente des Symfony2 Frameworks Datenbankabfragen generieren kann. Diese Technik ermöglicht auch Laien einfache Abfragen von komplexen Datenbeständen effizient durchzuführen.

TRANSCRIPT

Page 1: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

1 C. Hetzel, 12. Sept. 2013

Datenbankabfragen über CSS-Selektoren

Unter Verwendung der CssSelector-Komponente von Symfony2 Datenbankabfragen gestalten.

Von Carsten Hetzel.

Page 2: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

2 C. Hetzel, 12. Sept. 2013

Zur Person

Carsten Hetzel

Seit 2000 in der IT als Softwareengineer tätig, seit 10 Jahren als Freelancer

Likes: Softwaredesign, SF2 (ach was), BDD mit Behat, Oracle-Datenbanken

Hobbies: Joggen, Reiten, Klavierspielen

http://www.coding-inquiries.de

Page 3: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

3 C. Hetzel, 12. Sept. 2013

Die Anforderung

Kundenfreundliche Abfragengroßer, strukturierter

Datenmengen

Kundenfreundliche Abfragengroßer, strukturierter

Datenmengen

Page 4: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

4 C. Hetzel, 12. Sept. 2013

Die Anforderung

Für einen Kunden war in einer Browser basierten Software eine Suchfunktion bereitzustellen, die beliebigeObjekte einer Objekthierarchie abfragen und darstellen können sollte.

Probleme:- Ca. 1 Mio. Objekte.- Ca. 60 Objekttypen.- Ca. 16 Mio. Parameter.- Die Suchanfragen mussten für Laien möglich sein.

Page 5: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

5 C. Hetzel, 12. Sept. 2013

Die Anforderung

Beispiel:

Wir wollen alle Objekte X angezeigt bekommen, die irgendwo unterhalb eines Objekts A hängen bei dem der Parameter "a" den Wert 10 hat.

Page 6: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

6 C. Hetzel, 12. Sept. 2013

Die Idee:CSS-Selektoren

Die Idee:CSS-Selektoren

Page 7: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

7 C. Hetzel, 12. Sept. 2013

Aufbau von CSS-Selektoren

* -> Alle Elemente.

E -> Alle Elemente des Typs "E"

E[foo] -> Alle Elemente des Typs "E", die ein Attribut "foo" besitzen.

E[foo="bar"] -> Alle Elemente des Typs "E", deren Attribut "foo" den Wert "bar" hat.

E#10 -> Alle Elemente des Typs "E", deren Attribut "ID" den Wert 10 hat (entspricht E[id=10]).

E.myClass -> Alle Elemente des Typs "E", deren Attribut "class" mindestens "myClass" enthält (entspricht E[class~="myClass"]).

E F -> Alle Elemente des Typs "F", die Nachkommen von "E"-Elementen sind, sich also irgendwo unterhalb eines E befinden.

E > F -> Alle Elemente des Typs "F", die ein direktes Kind von einem "E"-Element sind.

E, F -> Alle Elemente des Typs "E" und "F".

Siehe: http://www.w3schools.com/cssref/css_selectors.asp oder http://www.w3.org/TR/2009/CR-CSS2-20090908/selector.html

Page 8: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

8 C. Hetzel, 12. Sept. 2013

Die Idee: CSS-SelektorenUm bestimmte Elemente in einem HTML-Dokument auszuwählen und zu bearbeiten werden sogenannte CSS-Selektoren verwendet.

Ein solcher Selektor beschreibt den Pfad und die Eigenschaften der Elemente, welche ausgewählt und durch Zuweisung von Eigenschaftswerten verändert werden sollen.

CSS-Selektoren sind eine einfache Notation für die Auswahl von Elementen in einem HTML-Dokument.

Ein HTML-Dokument ist eine konkrete hierarchische Datenstruktur.

Schlussfolgerung: CSS-Selektoren können für beliebige hierarchische Datenstrukturen verwendet werden.

Page 9: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

9 C. Hetzel, 12. Sept. 2013

Die Idee: CSS-Selektoren

Was könnte problematisch sein?

Datenbankmodelle sind nicht notwendigerweise hierarchisch.

Es können nicht (einfach) alle Abfragen, die über SQL möglich sind, über CSS-Selektoren abgebildet werden.

Beispiel: Selektiere alle Äpfel, die schwerer sind, als die schwerste Birne.

Oder doch(?): Apfel[gewicht>Birne:max(gewicht)]

Page 10: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

10

C. Hetzel, 12. Sept. 2013

Hierarchische Datenstrukturenund Datenmodelle

Ziel ist, Datenbankabfragen über die Formulierung von Selektoren zu generieren. Beispiele:

Author

select * from author

Author#1

select * from author where id = 1

Author[lastname=Martin]

select * from author where lastname = 'Martin'

Author[lastname=Martin] Books

select b.* from author a join books b on b.author_id = a.id where a.lastname = 'Martin'

Books[title^=Clean] Author

select a.* from books b join author a on a.id = b.author_id where b.title like 'Clean%'

Page 11: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

11

C. Hetzel, 12. Sept. 2013

Hierarchische Datenstrukturenund Datenmodelle

Wie den einzelnen Beispielen zu entnehmen ist, können Referenzen in Datenbankmodellen in beiden Richtungen genutzt werden, selbst wenn die Entitäten, wie in diesem vereinfachten Beispiel, nicht über eine 1:N-, sondern über eine N:M-Relation verbunden sind. Die "logische" Hierarchie ergibt sich durch die Wahl der Selektoren.ACHTUNG: Die "natürliche" Verknüpfung von zwei Tabellen findet über ihre gegenseitigen Referenzen statt. Trotz dieser Einschränkung kann es zu großen Ergebnismengen und mehreren gleichen Ergebnisdatensätzen kommen!

Page 12: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

12

C. Hetzel, 12. Sept. 2013

Die Komponente CssSelector

CssSelector stellt eine statische Hilfsfunktion zur Verfügung. Diese baut lediglich einen Translator zusammen, der die eigentliche Arbeit der Konvertierung des CSS-Ausdrucks in einen XPath-Ausdruck durchführt.

class CssSelector{ public static function toXPath($cssExpr, $prefix = 'descendant-or-self::') { $translator = new Translator(); if (self::$html) { $translator->registerExtension(new HtmlExtension($translator)); } $translator ->registerParserShortcut(new EmptyStringParser()) ->registerParserShortcut(new ElementParser()) ->registerParserShortcut(new ClassParser()) ->registerParserShortcut(new HashParser()) ; return $translator->cssToXPath($cssExpr, $prefix); } ...}

class CssSelector{ public static function toXPath($cssExpr, $prefix = 'descendant-or-self::') { $translator = new Translator(); if (self::$html) { $translator->registerExtension(new HtmlExtension($translator)); } $translator ->registerParserShortcut(new EmptyStringParser()) ->registerParserShortcut(new ElementParser()) ->registerParserShortcut(new ClassParser()) ->registerParserShortcut(new HashParser()) ; return $translator->cssToXPath($cssExpr, $prefix); } ...}

Page 13: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

13

C. Hetzel, 12. Sept. 2013

Die Komponente CssSelector

Da die Klasse CssSelector primär zur Konvertierung von CSS-Selektoren zu XPath-Ausdrücken gedacht ist, stelltsie eine entsprechende Objekthierarchie zusammen. Sie agiert damit als Fassade zum dahinterliegenden, komplexen Objektstruktur.Der Translator ist dementsprechend ein XPath-Translator und verwendet für seine Arbeit die dazugehörigenExtensions und den eigentlichen Parser.

Page 14: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

14

C. Hetzel, 12. Sept. 2013

Intermezzo!

Never use „new“(unless you‘re supposed to)

Never use „new“(unless you‘re supposed to)

Page 15: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

15

C. Hetzel, 12. Sept. 2013

Never use „new“(unless you‘re supposed to)

Hilft auch ohne Kenntnisse von OO-Prinzipien und Entwurfsmustern bessere Lösungen hervor zu bringen

Erzwingt Dependency Injection

Hilft in Komponenten zu denken

Fördert flexibilität und Testbarkeit von Code

Statische Methoden sind KEIN Ersatz!

Page 16: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

16

C. Hetzel, 12. Sept. 2013

Die Komponente CssSelector

Da CssSelector als Fassade dient und die Komplexität der interagierenden Objekte versteckt, ist die Verwendung von „new“ hier akzeptabel.

Insgesamt ist die Implementierung nicht gut, weil sie keine Einflussnahme auf den Parsingprozess und die Konfiguration der Objekte erlaubt.

class CssSelector{ public static function toXPath($cssExpr, $prefix = 'descendant-or-self::') { $translator = new Translator(); if (self::$html) { $translator->registerExtension(new HtmlExtension($translator)); } $translator ->registerParserShortcut(new EmptyStringParser()) ->registerParserShortcut(new ElementParser()) ->registerParserShortcut(new ClassParser()) ->registerParserShortcut(new HashParser()) ; return $translator->cssToXPath($cssExpr, $prefix); } ...}

class CssSelector{ public static function toXPath($cssExpr, $prefix = 'descendant-or-self::') { $translator = new Translator(); if (self::$html) { $translator->registerExtension(new HtmlExtension($translator)); } $translator ->registerParserShortcut(new EmptyStringParser()) ->registerParserShortcut(new ElementParser()) ->registerParserShortcut(new ClassParser()) ->registerParserShortcut(new HashParser()) ; return $translator->cssToXPath($cssExpr, $prefix); } ...}

Page 17: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

17

C. Hetzel, 12. Sept. 2013

Die Komponente CssSelector

Probleme, die sich aus der gegebenen Implementierung der CssSelector-Komponente ergeben:

CssSelector::toXPath() erzeugt eine direkte Abhängigkeit im Client-Code.

Jeder Aufruf von CssSelector::toXPath() erzeugt alle verwendeten Objekte neu.

CssSelector ist weder wiederverwendbar, noch erweiterbar.

Aber: toXPath() macht was es soll. ;-)

Page 18: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

18

C. Hetzel, 12. Sept. 2013

Die Komponente XPath\Translator

Offensichtlich ist die Komponente XPath\Translator diejenige, die die eigentliche Arbeit macht.

Verwendet einen Parser, um den CSS-Selektor in eine Node-Struktur umzuwandeln.

Benutzt Extensions um die verschiedenen NodeTypen (ElementNode, AttributeNode etc.) in einen XPath-Ausdruck umzuwandeln.

Page 19: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

19

C. Hetzel, 12. Sept. 2013

Der Parser

Der Parser zerteilt über einen Tokenizer zunächst den CSS-Selektor in einzelne Tokens.

Die Tokens werden anschließend in entsprechende Node-Instanzen umgewandelt und zurück geliefert.

Page 20: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

20

C. Hetzel, 12. Sept. 2013

Die Node-Klassen

Jeder Bestandteil eines CSS-Selektors wird durch eine gleichlautende Klasse repräsentiert.

Der Parser wandelt also den Selektor in eine Objekthierarchie um und erzeugt dazu die jeweiligen Instanzen.

Page 21: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

21

C. Hetzel, 12. Sept. 2013

XPath\Translator im DetailAuch hier wird ähnlich wie bei der XssSelector-Klasse eine Reihe von zusätzlichen Hilfsklassen instanziiert, um die Verarbeitung in separate Aufgabenbereiche aufzutrennen.

Zusätzlilch zur Konfiguration durch CssSelector nimmt also auch noch Translator selber seine Konfiguration in die Hand.

class Translator implements TranslatorInterface{ ... public function __construct(ParserInterface $parser = null) { $this->mainParser = $parser ?: new Parser(); $this ->registerExtension(new Extension\NodeExtension($this)) ->registerExtension(new Extension\CombinationExtension()) ->registerExtension(new Extension\FunctionExtension()) ->registerExtension(new Extension\PseudoClassExtension()) ->registerExtension(new Extension\AttributeMatchingExtension()) ; } ...}

class Translator implements TranslatorInterface{ ... public function __construct(ParserInterface $parser = null) { $this->mainParser = $parser ?: new Parser(); $this ->registerExtension(new Extension\NodeExtension($this)) ->registerExtension(new Extension\CombinationExtension()) ->registerExtension(new Extension\FunctionExtension()) ->registerExtension(new Extension\PseudoClassExtension()) ->registerExtension(new Extension\AttributeMatchingExtension()) ; } ...}

Page 22: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

22

C. Hetzel, 12. Sept. 2013

XPath\Translator im Detail

In cssToXPath() wird der Selektor zunächst in Node-Instanzen (SelectorNode) und anschließend über selectorToXPath() in einen XPath-Ausdruck umgewandelt.

class Translator implements TranslatorInterface{ ... public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') { $selectors = $this->parseSelectors($cssExpr); /** @var SelectorNode $selector */ foreach ($selectors as $selector) { if (null !== $selector->getPseudoElement()) { throw new ExpressionErrorException('Pseudo-elements are not supported.'); } } $translator = $this; return implode(' | ', array_map(function (SelectorNode $selector) use ($translator, $prefix) { return $translator->selectorToXPath($selector, $prefix); }, $selectors)); } ...}

class Translator implements TranslatorInterface{ ... public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') { $selectors = $this->parseSelectors($cssExpr); /** @var SelectorNode $selector */ foreach ($selectors as $selector) { if (null !== $selector->getPseudoElement()) { throw new ExpressionErrorException('Pseudo-elements are not supported.'); } } $translator = $this; return implode(' | ', array_map(function (SelectorNode $selector) use ($translator, $prefix) { return $translator->selectorToXPath($selector, $prefix); }, $selectors)); } ...}

Page 23: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

23

C. Hetzel, 12. Sept. 2013

Die Extension-Klassen

Jede Extension-Klasse ist dafür zuständig, die für sie relevanten Node-Instanzen in einen XPath-Ausdruck umzuwnadeln.

Dabei werden vom Translator die dazu registrierten Methoden aufgerufen.

Page 24: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

24

C. Hetzel, 12. Sept. 2013

XPath\Extensions im DetailDer Vorgang lässt sich anschaulich an der Umsetzung der „HashNode“ zeigen: Ein Ausdruck wie „Author#1“ soll das Element mit der ID „1“.

Die HashNode hat eine SelectorNode und die gewünschte ID. Beide Bestandteile werden vom Translator in den finalen XPath-Ausdruck konvertiert.

class NodeExtension extends AbstractExtension{ ... public function translateHash(Node\HashNode $node) { $xpath = $this->translator->nodeToXPath($node->getSelector()); return $this->translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); } ...}

class NodeExtension extends AbstractExtension{ ... public function translateHash(Node\HashNode $node) { $xpath = $this->translator->nodeToXPath($node->getSelector()); return $this->translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); } ...}

class AttributeMatchingExtension extends AbstractExtension{ ... public function translateEquals(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); } ...}

class AttributeMatchingExtension extends AbstractExtension{ ... public function translateEquals(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); } ...}

Page 25: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

25

C. Hetzel, 12. Sept. 2013

CSS-Selektoren für Datenbankstatements

Als Datenbankschicht wurde das ORM Propel verwendet - es lassen sich aber auch andere ORMs oder DBALs wie z.B. Doctrine verwenden.

Die Realisierung des Translators wurde an XPath\Translator angelehnt.

Das Datenmodell besteht aus Authoren und Büchern mit einer 1:N-Beziehung.

Page 26: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

26

C. Hetzel, 12. Sept. 2013

Die Implementierung auf einen Blick

Es wurde eine einfache Symfony2 Anwendung generiert mit Controllern zu Book und Author.

Der DbQuery\Translator wandelt den CSS-Selektor in ein ModelCriteria-Objekt um - entweder vom Typ AuthorQuery oder BookQuery.

Die Extensions sind dafür zuständig die API der Propel-Query-Klassen anzusprechen.

Page 27: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

27

C. Hetzel, 12. Sept. 2013

DbQuery\Translator im Detail

Der DbQuery\Translator orientiert sich am XPath\Translator (und macht die gleichen Fehler).

Damit die NodeExtension die richtigen Model-Klassen finden kann, benötigt sie deren Namespace.

Der Rest funktioniert analog: Parsen des Selektors und Umwandeln der Nodes in ein ModelCriteria (bzw. AuthorQuery oder BookQuery).

class Translator{ public function __construct( ParserInterface $parser = null ) { $this->mainParser = $parser ? : new Parser();

$modelNS = '\\App\\DbModelSelectorBundle\\Model\\';$this->registerExtension(

new Extension\NodeExtension( $this, null, $modelNS ) ) ->registerExtension(

new Extension\CombinationExtension() )->registerExtension(

new Extension\AttributeMatchingExtension() );} public function cssToDbQuery( $cssExpr ) {$selectors = $this->parseSelectors( $cssExpr );

$query = null; foreach( $selectors as $selector ){$query = $this->selectorToDbQuery( $selector, $query );} return $query; }...}

class Translator{ public function __construct( ParserInterface $parser = null ) { $this->mainParser = $parser ? : new Parser();

$modelNS = '\\App\\DbModelSelectorBundle\\Model\\';$this->registerExtension(

new Extension\NodeExtension( $this, null, $modelNS ) ) ->registerExtension(

new Extension\CombinationExtension() )->registerExtension(

new Extension\AttributeMatchingExtension() );} public function cssToDbQuery( $cssExpr ) {$selectors = $this->parseSelectors( $cssExpr );

$query = null; foreach( $selectors as $selector ){$query = $this->selectorToDbQuery( $selector, $query );} return $query; }...}

Page 28: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

28

C. Hetzel, 12. Sept. 2013

Extensions im Detail

Basis für alle weiteren Operationen ist ein Query-Objekt des gewünschten Models.

Dazu wird der Name der ElementNode ermittelt und im Namespace der Models geprüft, ob es eine passende Klasse gibt, also AuthorQuery oder BookQuery gesucht.

Query-Klassen werden bei Propel über die Statische „create()“-Methode erzeugt.

class NodeExtension extends AbstractExtension{ public function translateElement( Node\ElementNode $node ) {$element = $node->getElement(); $queryClassName = $this->modelNamespace . $element . 'Query'; if( !class_exists( $queryClassName ) ) throw new \RuntimeException( sprintf( 'Model %s not supported!', $queryClassName ) ); return $queryClassName::create(); }

...}

class NodeExtension extends AbstractExtension{ public function translateElement( Node\ElementNode $node ) {$element = $node->getElement(); $queryClassName = $this->modelNamespace . $element . 'Query'; if( !class_exists( $queryClassName ) ) throw new \RuntimeException( sprintf( 'Model %s not supported!', $queryClassName ) ); return $queryClassName::create(); }

...}

Page 29: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

29

C. Hetzel, 12. Sept. 2013

Extensions im Detail

Hier als Beispiel noch die Implementierung für AttributeNodes, mit denen where-Bedingungen für die Datenbank erzeugt werden.

Die SelectorNode kapselt dabei wieder den Zugriff auf die Datenbanktabelle, die AttributeNode die Bedingungen für einzelne Spalten der Tabelle.

class NodeExtension extends AbstractExtension{... public function translateAttribute( Node\AttributeNode

$node ) { $attribute = $node->getAttribute();$operator = $node->getOperator(); $value = $node->getValue();

$query = $this->translator->nodeToDbQuery( $node->getSelector() ); return $this->translator->addAttributeMatching(

$query,$operator,$attribute,$value ); } ...}

class NodeExtension extends AbstractExtension{... public function translateAttribute( Node\AttributeNode

$node ) { $attribute = $node->getAttribute();$operator = $node->getOperator(); $value = $node->getValue();

$query = $this->translator->nodeToDbQuery( $node->getSelector() ); return $this->translator->addAttributeMatching(

$query,$operator,$attribute,$value ); } ...}

Page 30: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

30

C. Hetzel, 12. Sept. 2013

Die Anwendung in Bildern

Über den CssSelector „Author“ werden alle Authoren angezeigt. Er kann alternativ auch weggelassen werden.

In den Beispieldaten hat Robert Martin zwei Bücher und Martin Fowler eines.

Page 31: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

31

C. Hetzel, 12. Sept. 2013

Die Anwendung in Bildern

Durch den Join mit Books wird somit Robert Martin zweimal angezeigt.

Der Translator wäre nützlicherweise so anzupassen, dass er einen Pseudoknoten „distinct“ erlaubt, um diesen Effekt zu vermeidenn.

Page 32: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

32

C. Hetzel, 12. Sept. 2013

Die Anwendung in Bildern

Wie man dem Profiler entnehmen kann, werden die korrekten Datenbankabfragen generiert.

Der Selector „Book[title*=Coder] Author[Lastname=Martin]“ wurde umgesetzt.

Page 33: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

33

C. Hetzel, 12. Sept. 2013

Ausblick

Zusätzliche Funktionalitäten wie „distinct“ oder Aggregatfunktionen sind denkbar.

Virtuelle Attribute wie „Book.count()“ können realisiert werden.

Bei bestimmten Zugriffen können Hints für den Ausführungsplan ergänzt werden.

...

Page 34: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

34

C. Hetzel, 12. Sept. 2013

Fragen? ;-)

Page 35: 2013-09-12, sfugcgn: CSS-Selektoren für Datenbankabfragen nutzen

35

C. Hetzel, 12. Sept. 2013

Vielen Dank für Ihre Aufmerksamkeit!

Vielen Dank für Ihre Aufmerksamkeit!