Using the union methods in database queries/nl: Difference between revisions

From Joomla! Documentation

Created page with "==Veel manieren om UNION te gebruiken=="
No edit summary
 
(21 intermediate revisions by 3 users not shown)
Line 55: Line 55:
==Veel manieren om UNION te gebruiken==
==Veel manieren om UNION te gebruiken==


The <tt>union</tt> (and <tt>unionAll</tt>) methods are quite flexible in what they will accept as arguments. You can pass an "old-style" string query, a <tt>JDatabaseQuery</tt> object, or an array of <tt>JDatabaseQuery</tt> objects. For example, suppose you have three tables, similar to the example above:
De <tt>union</tt> (en <tt>unionAll</tt>) methodes zijn heel flexibel in wat ze als argument accepteren. U kunt een "oude stijl" string-query doorgeven, een <tt>JDatabaseQuery</tt> object, of een array van <tt>JDatabaseQuery</tt> objecten. Stel dat u bijvoorbeeld drie tabellen heeft, net zoals het voorbeeld hierboven:


<source lang="php">
<source lang="php">
Line 63: Line 63:
</source>
</source>


Then all of these queries will produce the same results:
Dan zullen al deze query's hetzelfde resultaat opleveren:


<source lang="php">
<source lang="php">
Line 81: Line 81:
==union, unionAll en unionDistinct==
==union, unionAll en unionDistinct==


There are actually three union methods available.
Er zijn in feite drie UNION methodes beschikbaar.


* <tt>union</tt> produces a true set union of the individual result sets; that is, duplicates are removed. The process of eliminating duplicates may or may not incur a performance hit, depending on the data sets and database structures involved.
* <tt>union</tt> produceert een echte union set van de individuele resultaat-sets; wat betekent dat duplicaten verwijderd zijn. Het proces van elimineren van duplicaten kan wel of niet een performance vermindering veroorzaken, afhankelijk van de betrokken datasets en database structuren.


* <tt>unionAll</tt> produces a union of the individual result sets but duplicates are not removed.
* <tt>unionAll</tt> produceert een union van de individuele resultaat-sets maar duplicaten worden niet verwijderd.


* <tt>unionDistinct</tt> is identical in behaviour to <tt>union</tt> and is merely a proxy for the <tt>union</tt> method.
* <tt>unionDistinct</tt> is identiek aan het gedrag van <tt>union</tt> en is slechts een proxy voor de <tt>union</tt> methode.


==UNION gebruiken in plaats van OR==
==UNION gebruiken in plaats van OR==


There are some instances where using <tt>union</tt> can give a significant performance boost instead of the more commonly used alternative of an '''OR''' or an '''IN''' in a <tt>where</tt> clause.
Er zijn enkele gevallen waar het gebruik van <tt>union</tt> een aanzienlijke performance verbetering levert in plaats van het meer algemeen gebruikte alternatief van een '''OR''' of een '''IN''' in een <tt>where</tt> clausule.


For example, suppose you have a table of products and you want to extract just those products that belong to two particular categories. Typically this would be coded something like this:
Stel bijvoorbeeld dat u een tabel heeft met producten en u wilt alleen die producten ophalen die tot twee categorieën behoren. Meestal zal dit gecodeerd worden zoals dit:


<source lang="php">
<source lang="php">
Line 105: Line 105:
</source>
</source>


However, it is likely that you will see a useful increase in performance using <tt>union</tt> instead:
Het is echter waarschijnlijk dat u een aanzienlijke verbetering van de performance ziet bij het gebruik van <tt>union</tt>:


<source lang="php">
<source lang="php">
Line 122: Line 122:
</source>
</source>


Of course, if you want to select products from more than just two categories, you can combine more individual queries together.
U kunt natuurlijk, als u producten uit meer dan slechts twee categorieën wilt selecteren, meer individuele query's combineren.


You should see a similar performance boost when replacing a <tt>where</tt> clause containing an '''IN''' statement.
U zou een vergelijkbare performance verbetering zien door het vervangen van de <tt>where</tt> clausule door een '''IN''' statement.


==Resultaten sorteren==
==Resultaten sorteren==


If you want your results to be ordered then you need to be aware of how the database deals with '''ORDER BY''' clauses. The following comments apply to MySQL but probably apply to other databases too.
Indien u het resultaat gesorteerd wilt hebben moet u weten hoe de database omgaat met de '''ORDER BY''' clausules. De volgende opmerkingen gelden voor MySQL maar gelden waarschijnlijk ook voor andere databases.


Suppose you want to output the names and email addresses in alphabetical order in the mailshot example above. Then you would simply do this:
Stel dat u de namen en e-mailadressen in alfabetische volgorde wilt uitvoeren in bovenstaande mailing voorbeeld. Dan doet u gewoon dit:


<source lang="php">
<source lang="php">
Line 146: Line 146:
</source>
</source>


But suppose, for some reason, you wanted to output the names and email addresses in alphabetical order with all customers first, then all suppliers. This query will '''not''' give the expected result:
Maar stel dat u om een reden de namen en e-mailadressen wilt uitvoeren in alfabetische volgorde van klant en daarna alle leveranciers. Deze query zal '''niet''' het verwachte resultaat geven:


<source lang="php">
<source lang="php">
Line 163: Line 163:
</source>
</source>


This is because an '''ORDER BY''' clause in the individual '''SELECT''' statements implies nothing about the order in which the rows appear in the final result. A '''UNION''' produces an unordered set of rows. The query above will be syntactically correct, but the MySQL optimiser will simply ignore the '''ORDER BY''' clause on the suppliers '''SELECT''' statement and the '''ORDER BY''' clause on the customers '''SELECT''' statement will be applied to final result set rather than the individual result set.
Dit is omdat een '''ORDER BY''' clausule in de individuele '''SELECT''' statements niet zegt over de volgorde waarin de rijen in het uiteindelijke resultaat komen. Een '''UNION''' produceert een ongesorteerde set met rijen. Bovenstaande query is syntactisch correct, maar de MySQL-optimalisatie zal eenvoudigweg de '''ORDER BY''' clausule negeren op het leveranciers '''SELECT''' statement en de '''ORDER BY''' clausule op het klanten '''SELECT''' statement zal worden toegepast op de uiteindelijke resultaat-set en niet op de individuele resultaat-set.


The way around this is to add an additional column to the result set and sort on that in such a way that the results will have the desired ordering. Here's one way to do that:
De manier om dit te omzeilen is een extra kolom toevoegen aan de resultaat-set en daar zodanig op sorteren dat het resultaat de gewenste sortering heeft. Hier is een manier om dat te doen:


<source lang="php">
<source lang="php">
Line 183: Line 183:
==Geavanceerde sortering==
==Geavanceerde sortering==


However, there may be occasions when it is important to have an '''ORDER BY''' clause on the individual queries that won't be dropped by the query optimiser. Suppose you want to send a special offer to your top 10 customers and your top 5 suppliers. You will therefore want to apply a '''LIMIT''' clause in combination with the '''ORDER BY''' clause and in that case the query optimiser will not ignore the ordering. This is how you could do it:
Echter, er zijn omstandigheden dat het belangrijk is om een '''ORDER BY''' clausule op de individuele query's te hebben die niet door de query optimiser worden gedropt. Stel u wilt een speciaal aanbod zenden aan uw 10 top-klanten en uw 5 top-leveranciers. U wilt daarom een '''LIMIT''' clausule toepassen in combinatie met de '''ORDER BY''' clausule en in dat geval zal de query optimiser de sortering niet negeren. Zo zou u dit kunnen doen:


<source lang="php">
<source lang="php">
Line 209: Line 209:
</source>
</source>


It's necessary to use a dummy query in this case because otherwise the <tt>order</tt> and <tt>setLimit</tt> methods would be applied to the final result set rather that the individual one.
Het is in dit geval nodig een dummy query te gebruiken omdat anders de <tt>order</tt> en <tt>setLimit</tt> methodes uitgevoerd zouden worden op de eind resultaat-set in plaats van de individuele.
 
Als u fouten ondervindt bij dit voorbeeld, controleer dan het probleem aan de hand van deze gerelateerde patch: [https://issues.joomla.org/tracker/joomla-cms/4127 4127]


[[Category:Database/nl]]
[[Category:Database/nl]]

Latest revision as of 11:41, 12 October 2016

Joomla! 
≥ 3.3
Deze pagina heeft alleen betrekking op Joomla! 3.3 en later.

Hoewel de UNION methodes aanwezig zijn in Joomla! 2.5 en 3.x, werken ze niet in releases voor 3.3.

Het gebruik van UNION in a database-query is een handige manier om het resultaat van twee of meer database SELECT query's, die niet noodzakelijk via een database relatie gekoppeld zijn, te combineren. Het kan ook een nuttige performance optimalisatie zijn. Het gebruik van een UNION om het resultaat van twee verschillende query's te combineren kan soms aanzienlijk sneller zijn dan één enkele query met een WHERE clausule zeker als de query JOINS naar andere grote tabellen heeft.

Voor degenen die bekend zijn met de set theorie doet de UNION precies wat je verwacht; het voegt de set met resultaten uit de ene query samen met de set uit de andere query om zo een set met resultaten samen te stellen wat de verzameling is van de individuele resultaat sets. Als u wilt dat de set wordt gesorteerd, dan moet u bijzondere aandacht besteden aan de manier waarop dat gedaan wordt, zoals later wordt uitgelegd.

De basis

Om een UNION te gebruiken moet u zich bewust zijn van de basis eisen van de SQL-server die u gebruikt. Deze worden niet afgedwongen door Joomla maar, als u ze niet naleeft, krijgt u database fouten. In het bijzonder, elke SELECT query moet hetzelfde aantal velden teruggeven in dezelfde volgorde en met compatibele gegevenstypes om de UNION succesvol te laten zijn.

Een eenvoudig voorbeeld

Stel, u wilt een mailing sturen aan aan groep mensen maar de database is zodanig dat de namen en de e-mailadressen die u wilt versturen niet allemaal in dezelfde tabel zitten. Stel, om een willekeurig voorbeeld te maken, dat u de mail wilt versturen aan alle klanten en alle leveranciers en dat de namen en e-mailadressen, verrassend, respectievelijk zitten in de tabellen customers (klanten) en suppliers (leveranciers).

Deze query haalt alle klant-informatie op die we nodig hebben in de mailing:

$query
    ->select('name, email')
    ->from('customers')
    ;
$mailshot = $db->setQuery($query)->loadObjectList();

Terwijl deze query hetzelfde doet voor alle leveranciers:

$query
    ->select('name, email')
    ->from('suppliers')
    ;
$mailshot = $db->setQuery($query)->loadObjectList();

U kunt het resultaat op deze manier combineren in één enkele query:

$query
    ->select('name, email')
    ->from('customers')
    ->union($q2->select('name , email')->from('suppliers'))
    ;
$mailshot = $db->setQuery($query)->loadObjectList();

De resultaat-set uit de UNION-query zal uiteindelijk iets anders zijn als bij het uitvoeren van de afzonderlijke query's apart omdat de UNION-query automatisch de duplicaten verwijderd. Indien u het niet erg vindt dat de resultaat-set duplicaten bevat (wat wiskundig gezien betekent dat het geen set is), dan zal het gebruik van unionAll in plaats van union de performance verbeteren.

Veel manieren om UNION te gebruiken

De union (en unionAll) methodes zijn heel flexibel in wat ze als argument accepteren. U kunt een "oude stijl" string-query doorgeven, een JDatabaseQuery object, of een array van JDatabaseQuery objecten. Stel dat u bijvoorbeeld drie tabellen heeft, net zoals het voorbeeld hierboven:

$q1->select('name, email')->from('customers');
$q2->select('name, email')->from('suppliers');
$q3->select('name, email')->from('shareholders');

Dan zullen al deze query's hetzelfde resultaat opleveren:

// The union method can be chained.
$q1->union($q2)->union($q3);

// The union method will accept string queries.
$q1->union($q2)->union('SELECT name, email FROM shareholders');

// The union method will accept an array of JDatabaseQuery objects.
$q1->union(array($q2, $q3));

// It doesn't matter which query object is at the "root" of the query.  In this case the actual query that is produced will be different but the result set will be the same.
$q2->union(array($q1, $q3));

union, unionAll en unionDistinct

Er zijn in feite drie UNION methodes beschikbaar.

  • union produceert een echte union set van de individuele resultaat-sets; wat betekent dat duplicaten verwijderd zijn. Het proces van elimineren van duplicaten kan wel of niet een performance vermindering veroorzaken, afhankelijk van de betrokken datasets en database structuren.
  • unionAll produceert een union van de individuele resultaat-sets maar duplicaten worden niet verwijderd.
  • unionDistinct is identiek aan het gedrag van union en is slechts een proxy voor de union methode.

UNION gebruiken in plaats van OR

Er zijn enkele gevallen waar het gebruik van union een aanzienlijke performance verbetering levert in plaats van het meer algemeen gebruikte alternatief van een OR of een IN in een where clausule.

Stel bijvoorbeeld dat u een tabel heeft met producten en u wilt alleen die producten ophalen die tot twee categorieën behoren. Meestal zal dit gecodeerd worden zoals dit:

$query
    ->select('*')
    ->from('products')
    ->where('category = ' . $db->q('catA'), 'or')
    ->where('category = ' . $db->q('catB'))
    ;
$products = $db->setQuery($query)->loadObjectList();

Het is echter waarschijnlijk dat u een aanzienlijke verbetering van de performance ziet bij het gebruik van union:

$query
    ->select('*')
    ->from('products')
    ->where('category = ' . $db->q('catA'))
    ;
$q2
    ->select('*')
    ->from('products')
    ->where('category = ' . $db->q('catB'))
    ;
$query->union($q2);
$products = $db->setQuery($query)->loadObjectList();

U kunt natuurlijk, als u producten uit meer dan slechts twee categorieën wilt selecteren, meer individuele query's combineren.

U zou een vergelijkbare performance verbetering zien door het vervangen van de where clausule door een IN statement.

Resultaten sorteren

Indien u het resultaat gesorteerd wilt hebben moet u weten hoe de database omgaat met de ORDER BY clausules. De volgende opmerkingen gelden voor MySQL maar gelden waarschijnlijk ook voor andere databases.

Stel dat u de namen en e-mailadressen in alfabetische volgorde wilt uitvoeren in bovenstaande mailing voorbeeld. Dan doet u gewoon dit:

$q2
    ->select('name , email')
    ->from('suppliers')
    ;
$query
    ->select('name, email')
    ->from('customers')
    ->union($q2)
    ->order('name')
    ;
$mailshot = $db->setQuery($query)->loadObjectList();

Maar stel dat u om een reden de namen en e-mailadressen wilt uitvoeren in alfabetische volgorde van klant en daarna alle leveranciers. Deze query zal niet het verwachte resultaat geven:

$q2
    ->select('name , email')
    ->from('suppliers')
    ->order('name')
    ;
$query
    ->select('name, email')
    ->from('customers')
    ->order('name')
    ->union($q2)
    ;
$mailshot = $db->setQuery($query)->loadObjectList();

Dit is omdat een ORDER BY clausule in de individuele SELECT statements niet zegt over de volgorde waarin de rijen in het uiteindelijke resultaat komen. Een UNION produceert een ongesorteerde set met rijen. Bovenstaande query is syntactisch correct, maar de MySQL-optimalisatie zal eenvoudigweg de ORDER BY clausule negeren op het leveranciers SELECT statement en de ORDER BY clausule op het klanten SELECT statement zal worden toegepast op de uiteindelijke resultaat-set en niet op de individuele resultaat-set.

De manier om dit te omzeilen is een extra kolom toevoegen aan de resultaat-set en daar zodanig op sorteren dat het resultaat de gewenste sortering heeft. Hier is een manier om dat te doen:

$q2
    ->select('name , email, 1 as sort_col')
    ->from('suppliers')
    ;
$query
    ->select('name, email, 2 as sort_col')
    ->from('customers')
    ->union($q2)
    ->order('sort_col, name')
    ;
$mailshot = $db->setQuery($query)->loadObjectList();

Geavanceerde sortering

Echter, er zijn omstandigheden dat het belangrijk is om een ORDER BY clausule op de individuele query's te hebben die niet door de query optimiser worden gedropt. Stel u wilt een speciaal aanbod zenden aan uw 10 top-klanten en uw 5 top-leveranciers. U wilt daarom een LIMIT clausule toepassen in combinatie met de ORDER BY clausule en in dat geval zal de query optimiser de sortering niet negeren. Zo zou u dit kunnen doen:

$q2
    ->select('name , email, 1 as sort_col')
    ->from('suppliers')
    ->order('turnover DESC')
    ->setLimit(5)
    ;
$q1
    ->select('name, email, 2 as sort_col')
    ->from('customers')
    ->order('turnover DESC')
    ->setLimit(10)
    ;
$query
    ->select('name, email, 0 as sort_col')
    ->from('customers')
    ->where('1 = 0')
    ->union($q1)
    ->union($q2)
    ->order('sort_col, name')
    ;
$mailshot = $db->setQuery($query)->loadObjectList();

Het is in dit geval nodig een dummy query te gebruiken omdat anders de order en setLimit methodes uitgevoerd zouden worden op de eind resultaat-set in plaats van de individuele.

Als u fouten ondervindt bij dit voorbeeld, controleer dan het probleem aan de hand van deze gerelateerde patch: 4127