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

From Joomla! Documentation

Created page with "Usar los métodos de unión en consultas de base de datos"
 
Created page with "Si estás experimentando los errores que reproducen este ejemplo, chequea el tema y parche relacionado: [https://issues.joomla.org/tracker/joomla-cms/4127 4127]"
 
(39 intermediate revisions by 2 users not shown)
Line 6: Line 6:
{{amboxNew
{{amboxNew
| type  = serious
| type  = serious
| title = This page covers Joomla! 3.3 or later only.
| title = Esta página cubre Joomla! 3.3 o posterior.
| image = content
| image = content
| text  = '''Although the union methods are present in Joomla! 2.5 and 3.x, they ''do not work'' in any release prior to 3.3.}}
| text  = '''Aunque los métodos ''union'' están presentes en Joomla! 2.5 y 3.x, ''no funcionan'' en cualquier versión antes de 3.3.'''}}


Using a '''UNION''' in a database query is a useful way to combine the results of two or more database '''SELECT''' queries that are not necessarily linked by a database relationship. It can also be a useful performance optimisation. Using a '''UNION''' to combine the results of different queries can sometimes be significantly faster than a single query with a '''WHERE''' clause especially when the query involves joins to other large tables.
El uso de  '''UNION''' en una consulta de base de datos es una forma útil para combinar los resultados de dos o más consultas '''SELECT''' a la base de datos, que no están necesariamente vinculadas por una relación de la base de datos. También puede ser útil en la optimización del rendimiento. El uso de un '''UNION''' para combinar los resultados de diferentes consultas a veces puede ser mucho más rápido que en una sola consulta con una cláusula '''WHERE''' sobre todo cuando la consulta implica unir otras tablas grandes.


For those familiar with a little set theory a '''UNION''' does exactly what you would expect; it merges the set of results from one query with the set of results from another query to produce a set of results which is the set union of both of the individual result sets. If you want the set to be ordered then you will need to pay some particular attention to the way that is done, as will be explained later.
Para aquellos poco familiarizados con la teoría de un '''UNION''', hace exactamente lo que se espera; fusiona el conjunto de resultados de una consulta con el conjunto de los resultados de otra consulta para producir un conjunto de resultados, que es el conjunto de la unión de los dos conjuntos de resultados individuales. Si desea que el conjunto se ordene entonces tendrás que poner especial atención a la forma en que se realiza, como se explica más adelante.


==The basics==
==Los fundamentos==
In order to use a '''UNION''' you need to be aware of the basic requirements established by the SQL server you are using. These are not enforced by Joomla, but if you don't comply with them you will get database errors. In particular, each '''SELECT''' query must return the same number of fields in the same order and with compatible data types in order for the '''UNION''' to be successful.
Con el fin de utilizar un '''UNION''', necesitas estar consciente de los requisitos básicos establecidos por el servidor SQL que estás utilizando. Estas no son impuestas por Joomla!, pero si no cumples con ellas obtendrás errores de la base de datos. En particular, cada consulta '''SELECT''' debe devolver el mismo número de campos en el mismo orden y con los tipos de datos compatibles para que el '''UNION''' sea exitoso.


==A simple example==
==Un ejemplo sencillo==
Suppose you have want to send out a mailshot to a group of people but the database is such that the names and email addresses you want to send to are not all in the same table. To invent an arbitrary example, suppose that you want to send the mail to all customers and all suppliers and that that the names and email addresses are stored in tables called, unsurprisingly, ''customers'' and ''suppliers'', respectively.
Supongamos que deseas enviar una circular a un grupo de personas, pero la base de datos es tal que los nombres y direcciones de correo electrónico que desea enviar a no todos están en la misma tablaa. Por inventar una ejemplo arbitrario, supongamos que deseas enviar el correo a todos los clientes y todos los proveedores y que los nombres y direcciones de correo electrónico se almacenan en tablas llamadas, como era de esperar, ''customers'' y ''suppliers'', respectivamente.


This query will retrieve all the customer information that we need for the mailshot:
Esta consulta va a recuperar toda la información del cliente que se necesitan para la circular:


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


while this query will do the same for all suppliers:
mientras que esta consulta hace lo mismo para todos los proveedores:


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


Then you can combine the results into a single query like this:
Entonces puedes combinar los resultados en una sola consulta como esta:


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


The result set obtained from the union query will actually be a little different from running the individual queries separately because the union query will automatically eliminate duplicates. If you don't mind that the result set may contain duplicates (which mathematically speaking means it isn't a set), then using <tt>unionAll</tt> instead of <tt>union</tt> will improve performance.
El conjunto de resultados obtenidos a partir de la consulta de unión, realmente será un poco diferente de la gestión de las consultas individuales por separado porque la consulta de unión, automáticamente elimina los duplicados. Si no te importa que el conjunto de resultados pueda contener duplicados (que matemáticamente significa que no es un conjunto), utilizando <tt>unionAll</tt> en lugar de <tt>unión</tt> mejorará el rendimiento.


==Lots of ways to use union==
==Muchas maneras para uso de union==


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:
Los métodos <tt>union</tt> (y <tt>unionAll</tt>), son bastantes flexibles en lo acepta como argumentos. Se puede pasar de un "viejo estilo" de cadena de consulta, a un objeto <tt>JDatabaseQuery</tt>, o un array de objetos <tt>JDatabaseQuery</tt>. Por ejemplo, supón que tienes tres tablas, de forma similar al ejemplo anterior:


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


Then all of these queries will produce the same results:
Entonces todas estas consultas producen el mismo resultado:


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


==union, unionAll and unionDistinct==
==union, unionAll y unionDistinct==


There are actually three union methods available.
En realidad, hay tres métodos ''union'' disponibles.


* <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> produce un verdadero conjunto de la unión de los conjuntos de resultados individuales; es decir, los duplicados se eliminan. El proceso de eliminación de duplicados puede o no puede incurrir en un impacto en el rendimiento, dependiendo de los conjuntos de datos y las estructuras de la base de datos involucradas.


* <tt>unionAll</tt> produces a union of the individual result sets but duplicates are not removed.
* <tt>unionAll</tt> produce una unión de los conjuntos de resultados individuales, pero no se quitan los duplicados.


* <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> es idéntico en el comportamiento de la <tt>unión</tt> y es simplemente un alias para el método <tt>unión</tt>.


==Using UNION instead of OR==
==Usar UNION en lugar de 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.
Existen algunos casos en donde a través del uso de <tt>union</tt> puede darse un impulso significativo en el rendimiento en lugar de la alternativa más comúnmente utilizada de un '''OR''' o '''IN''' en la cláusula <tt>where</tt>.


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:
Por ejemplo, supón que tienes una tabla de productos y deseas extraer sólo aquellos productos que pertenecen a dos categorías particulares. Normalmente este sería el código para algo como esto:


<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:
Sin embargo, es probable que vas a ver útil aumentar el rendimiento mediante el uso de <tt>union</tt> en su lugar:


<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.
Por supuesto, si deseas seleccionar los productos de más de dos categorías, se puede combinar más consultas individuales juntas.


You should see a similar performance boost when replacing a <tt>where</tt> clause containing an '''IN''' statement.
Debes ver un aumento de rendimiento similar a la hora de sustituir una cláusula <tt>where</tt> que contiene una declaración '''IN'''.


==Ordering results==
==Ordenar los resultados==


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.
Si deseas que tus resultados se ordenen entonces necesitas estar consciente que ofrece la base de datos con las lcausulas '''ORDER BY'''. Los siguientes comentarios se aplican a MySQL pero, probablemente, se aplican a otras bases de datos.


Suppose you want to output the names and email addresses in alphabetical order in the mailshot example above. Then you would simply do this:
Supongamos que deseas la salida de los nombres y direcciones de correo electrónico en orden alfabético en la circular del ejemplo anterior. Entonces sólo tienes que hacer esto:


<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:
Pero supongamos que, por alguna razón, querías la salida de los nombres y direcciones de correo electrónico en orden alfabético con todos los clientes en primer lugar, a continuación, todos los proveedores. Esta consulta '''no''' va a dar el resultado esperado:


<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.
Esto es debido a que un cláusula '''ORDER BY''' en las declaraciones '''SELECT''' individuales no implica nada sobre el orden en que aparecen las filas en el resultado final. Un '''UNION''' produce un conjunto desordenado de filas. La consulta anterior es sintácticamente correcta, pero el optimizador de MySQL simplemente ignora la cláusula '''ORDER BY''' del '''SELECT''' proveedores y la cláusula '''ORDER BY''' del '''SELECT''' de clientes y la declaración '''ORDER BY''' se aplica al conjunto final de resultados en lugar de cada conjunto de resultados.


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:
La forma de evitar esto es agregar una columna adicional al conjunto de resultados y ordenarla, en tal manera que los resultados se ordenen de la forma deseada. Aquí hay una manera de hacerlo:


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


==Advanced ordering==
==Ordenamiento Avanzado==


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:
Sin embargo, puede haber ocasiones en las que es importante tener una cláusula '''ORDER BY''' en las consultas individuales que no se pueden quitar de la consulta por el optimizador de conversiones. Supón que deseas enviar una oferta especial para tu clientes top 10 y proveedores top 5. Por lo tanto, será conveniente aplicar una cláusula '''LIMIT''' en combinación con la cláusula '''ORDER BY''' y en este caso que el optimizador de la consulta no ignore el ordenamiento. Esta es la forma en que podrías hacerlo:


<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.
Es necesario el uso de una consulta ficticia, en este caso, porque de lo contrario los métodos <tt>order</tt> y <tt>setLimit</tt> serán aplicados al conjunto de resultados finales, en lugar de los individuales.


[[Category:Database]]
Si estás experimentando los errores que reproducen este ejemplo, chequea el tema y parche relacionado: [https://issues.joomla.org/tracker/joomla-cms/4127 4127]
 
[[Category:Database/es]]

Latest revision as of 18:33, 25 September 2016

Joomla! 
≥ 3.3
Esta página cubre Joomla! 3.3 o posterior.

Aunque los métodos union están presentes en Joomla! 2.5 y 3.x, no funcionan en cualquier versión antes de 3.3.

El uso de UNION en una consulta de base de datos es una forma útil para combinar los resultados de dos o más consultas SELECT a la base de datos, que no están necesariamente vinculadas por una relación de la base de datos. También puede ser útil en la optimización del rendimiento. El uso de un UNION para combinar los resultados de diferentes consultas a veces puede ser mucho más rápido que en una sola consulta con una cláusula WHERE sobre todo cuando la consulta implica unir otras tablas grandes.

Para aquellos poco familiarizados con la teoría de un UNION, hace exactamente lo que se espera; fusiona el conjunto de resultados de una consulta con el conjunto de los resultados de otra consulta para producir un conjunto de resultados, que es el conjunto de la unión de los dos conjuntos de resultados individuales. Si desea que el conjunto se ordene entonces tendrás que poner especial atención a la forma en que se realiza, como se explica más adelante.

Los fundamentos

Con el fin de utilizar un UNION, necesitas estar consciente de los requisitos básicos establecidos por el servidor SQL que estás utilizando. Estas no son impuestas por Joomla!, pero si no cumples con ellas obtendrás errores de la base de datos. En particular, cada consulta SELECT debe devolver el mismo número de campos en el mismo orden y con los tipos de datos compatibles para que el UNION sea exitoso.

Un ejemplo sencillo

Supongamos que deseas enviar una circular a un grupo de personas, pero la base de datos es tal que los nombres y direcciones de correo electrónico que desea enviar a no todos están en la misma tablaa. Por inventar una ejemplo arbitrario, supongamos que deseas enviar el correo a todos los clientes y todos los proveedores y que los nombres y direcciones de correo electrónico se almacenan en tablas llamadas, como era de esperar, customers y suppliers, respectivamente.

Esta consulta va a recuperar toda la información del cliente que se necesitan para la circular:

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

mientras que esta consulta hace lo mismo para todos los proveedores:

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

Entonces puedes combinar los resultados en una sola consulta como esta:

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

El conjunto de resultados obtenidos a partir de la consulta de unión, realmente será un poco diferente de la gestión de las consultas individuales por separado porque la consulta de unión, automáticamente elimina los duplicados. Si no te importa que el conjunto de resultados pueda contener duplicados (que matemáticamente significa que no es un conjunto), utilizando unionAll en lugar de unión mejorará el rendimiento.

Muchas maneras para uso de union

Los métodos union (y unionAll), son bastantes flexibles en lo acepta como argumentos. Se puede pasar de un "viejo estilo" de cadena de consulta, a un objeto JDatabaseQuery, o un array de objetos JDatabaseQuery. Por ejemplo, supón que tienes tres tablas, de forma similar al ejemplo anterior:

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

Entonces todas estas consultas producen el mismo resultado:

// 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 y unionDistinct

En realidad, hay tres métodos union disponibles.

  • union produce un verdadero conjunto de la unión de los conjuntos de resultados individuales; es decir, los duplicados se eliminan. El proceso de eliminación de duplicados puede o no puede incurrir en un impacto en el rendimiento, dependiendo de los conjuntos de datos y las estructuras de la base de datos involucradas.
  • unionAll produce una unión de los conjuntos de resultados individuales, pero no se quitan los duplicados.
  • unionDistinct es idéntico en el comportamiento de la unión y es simplemente un alias para el método unión.

Usar UNION en lugar de OR

Existen algunos casos en donde a través del uso de union puede darse un impulso significativo en el rendimiento en lugar de la alternativa más comúnmente utilizada de un OR o IN en la cláusula where.

Por ejemplo, supón que tienes una tabla de productos y deseas extraer sólo aquellos productos que pertenecen a dos categorías particulares. Normalmente este sería el código para algo como esto:

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

Sin embargo, es probable que vas a ver útil aumentar el rendimiento mediante el uso de union en su lugar:

$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();

Por supuesto, si deseas seleccionar los productos de más de dos categorías, se puede combinar más consultas individuales juntas.

Debes ver un aumento de rendimiento similar a la hora de sustituir una cláusula where que contiene una declaración IN.

Ordenar los resultados

Si deseas que tus resultados se ordenen entonces necesitas estar consciente que ofrece la base de datos con las lcausulas ORDER BY. Los siguientes comentarios se aplican a MySQL pero, probablemente, se aplican a otras bases de datos.

Supongamos que deseas la salida de los nombres y direcciones de correo electrónico en orden alfabético en la circular del ejemplo anterior. Entonces sólo tienes que hacer esto:

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

Pero supongamos que, por alguna razón, querías la salida de los nombres y direcciones de correo electrónico en orden alfabético con todos los clientes en primer lugar, a continuación, todos los proveedores. Esta consulta no va a dar el resultado esperado:

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

Esto es debido a que un cláusula ORDER BY en las declaraciones SELECT individuales no implica nada sobre el orden en que aparecen las filas en el resultado final. Un UNION produce un conjunto desordenado de filas. La consulta anterior es sintácticamente correcta, pero el optimizador de MySQL simplemente ignora la cláusula ORDER BY del SELECT proveedores y la cláusula ORDER BY del SELECT de clientes y la declaración ORDER BY se aplica al conjunto final de resultados en lugar de cada conjunto de resultados.

La forma de evitar esto es agregar una columna adicional al conjunto de resultados y ordenarla, en tal manera que los resultados se ordenen de la forma deseada. Aquí hay una manera de hacerlo:

$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();

Ordenamiento Avanzado

Sin embargo, puede haber ocasiones en las que es importante tener una cláusula ORDER BY en las consultas individuales que no se pueden quitar de la consulta por el optimizador de conversiones. Supón que deseas enviar una oferta especial para tu clientes top 10 y proveedores top 5. Por lo tanto, será conveniente aplicar una cláusula LIMIT en combinación con la cláusula ORDER BY y en este caso que el optimizador de la consulta no ignore el ordenamiento. Esta es la forma en que podrías hacerlo:

$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();

Es necesario el uso de una consulta ficticia, en este caso, porque de lo contrario los métodos order y setLimit serán aplicados al conjunto de resultados finales, en lugar de los individuales.

Si estás experimentando los errores que reproducen este ejemplo, chequea el tema y parche relacionado: 4127