it-swarm-es.tech

Método para crear una subconsulta utilizando JDatabase

En http://docs.joomla.org/Selecting_data_using_JDatabase , no hay un método documentado para escribir una subconsulta utilizando JDatabase.

https://Gist.github.com/gunjanpatel/86633 ejemplifica una forma de lograr esto con (se omiten algunos bits):

$subQuery = $db->getQuery(true);
$query    = $db->getQuery(true);

// Create the base subQuery select statement.
$subQuery->select('*')
    ->from($db->quoteName('#__sub_table'))
    ->where($db->quoteName('subTest') . ' = ' . $db->quote('1'));

// Create the base select statement.
$query->select('*')
    ->from($db->quoteName('#__table'))
    ->where($db->quoteName('state') . ' = ' . $db->quote('1'))
    ->where($db->quoteName('subCheckIn') . ' IN (' . $subQuery->__toString() . ')')
    ->order($db->quoteName('ordering') . ' ASC');

// Set the query and load the result.
$db->setQuery($query);

Este parece ser un enfoque bueno y plausible, pero ¿hay uno mejor?

31
betweenbrain

Sí, en lo que a mí respecta, la forma en que construiste la subconsulta es la adoptada por la mayoría de los desarrolladores de extensiones de joomla.

Utilizo el mismo método en algunas de mis extensiones y extensiones personalizadas para clientes.

No existe una forma "oficial" de hacerlo, pero hacerlo como se muestra le permite usar el generador de consultas y aún así mantener una buena legibilidad

16
Skullbock

AFAIK no hay una forma integrada de hacer subconsultas fáciles, lo que probablemente sea una deficiencia en el sistema y debe corregirse a través de PR.

Sin embargo, no veo ningún problema con su ejemplo, parece bastante razonable.

~~~

Aquí hay un ejemplo en respuesta al comentario de @ DavidFritsch a continuación. Sin embargo, cuanto más lo pienso, mejor me gusta el enfoque más simple que se muestra en el OP. Está más claro lo que está pasando.

$query = $this->db->getQuery(true)
  ->select('a.*')
  ->subQuery()
    ->select('b.*')
    ->from('#__table_b AS b')
    ->as('subQueryResult')
  ->endSubQuery()
  ->from('#__table_a AS a');
10
Don Gilbert

También hay una forma de ejecutar consultas que contienen subconsultas utilizando la API de la plataforma Joomla. La idea básica sobre cómo usar subconsultas se basa en gunjanpatel .

Aquí hay un ejemplo para ejecutar consultas en Modelos de conjuntos anidados :

Consulta SQL:

-- Find the Immediate Subordinates of a Node
SELECT node.title, (COUNT(parent.id) - (sub_tree.depth + 1)) AS depth
FROM lubd3_usergroups AS node,
        lubd3_usergroups AS parent,
        lubd3_usergroups AS sub_parent,
        (
                SELECT node.id, (COUNT(parent.id) - 1) AS depth
                FROM lubd3_usergroups AS node,
                        lubd3_usergroups AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                        AND node.id = 1
                GROUP BY node.id
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.id = sub_tree.id
GROUP BY node.id
-- not showing the parent node
HAVING depth = 1
-- showing the parent node
-- HAVING depth <= 1
ORDER BY node.lft;

y la consulta transformada para ser ejecutada por Joomla:

// Create the subQuery select statement.
// Nested Set Queries: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
// CROSS JOIN: http://www.informit.com/articles/article.aspx?p=30875&seqNum=5
$subQuery->select(array('node.id', '(COUNT(parent.id) - 1) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt') . ' AND ' . $db->quoteName('node.id') . ' = ' . $db->quote('1'))
    ->group($db->quoteName('node.id'))
    ->order($db->quoteName('node.lft'));

// Create the base select statement.
$query->select(array('node.title', '(COUNT(parent.id) - (sub_tree.depth + 1)) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->join('CROSS', $db->quoteName('#__usergroups', 'sub_parent'))
    ->join('CROSS', '(' . $subQuery .') AS sub_tree')
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt')
    . ' AND ' . $db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('sub_parent.lft') . ' AND ' . $db->quoteName('sub_parent.rgt')
    . ' AND ' . $db->quoteName('sub_parent.id') . ' = ' . $db->quoteName('sub_tree.id'))
    ->group($db->quoteName('node.id'))
    ->having($db->quoteName('depth') . ' = ' . $db->quote('1'))
    ->order($db->quoteName('node.lft'));

// Set the query and load the result.
$db->setQuery($query);
$rowList = $db->loadAssocList();

echo "<pre>";
print_r($rowList);
echo "</pre>";
3
Mario Neubauer

Ofreceré mi versión del fragmento y luego explicaré mi justificación e incluiré citas del Manual de estándares de codificación de Joomla (que tendrá el formato de bloque de comillas).

$subquery = $db->getQuery(true)
    ->select("checkin")
    ->from("#__sub_table")
    ->where("subTest = 1");

$query = $db->getQuery(true)
    ->select("*")
    ->from("#__table")
    ->where([
        "state = 1",
        "subCheckIn IN ({$subQuery})"
    ])
    ->order("ordering");

$db->setQuery($query);

Use el Encadenamiento de consultas para conectar varios métodos de consulta, uno tras otro, y cada método devuelve un objeto que puede admitir el siguiente método. Esto mejora la legibilidad y simplifica el código resultante.

  • Escribo las consultas más internas primero y avanzo a la consulta más externa. Esto me permite encadenar todos los métodos de creación de consultas directamente al método getQuery(). Efectivamente, el nombre de la variable solo se escribe una vez mientras se crea la consulta individual.
    Aquí está n excelente ejemplo de una gran anidación de consultas (cuando pensé que era lindo alinear las flechas de encadenamiento).

  • Intento evitar hacer múltiples llamadas a select() y/o where() dentro de la misma consulta porque la he visto lo que confunde a los desarrolladores menos experimentados . Debido a que estos métodos aceptan matrices, me parece más legible y una mejor práctica de codificación emplearlos.

  • y finalmente el tema más controvertido ...

    Los nombres de tabla y los nombres de columna de tabla siempre deben incluirse en el método quoteName () para escapar del nombre de tabla y las columnas de tabla. Los valores de campo marcados en una consulta siempre deben incluirse en el método quote () para escapar del valor antes de pasarlo a la base de datos. Los valores de los campos enteros marcados en una consulta también deben escribirse a (int).

    Estoy muy en conflicto con esta postura. Cuando llegué por primera vez a Joomla el año pasado, pensé, ¡no voy a hacer llamadas inútiles (sin beneficio para la estabilidad, seguridad, legibilidad de la consulta) en valores estáticos! Sin embargo, a mi empleador le gusta la idea de seguir la línea de Joomla, y tengo que admitir que generalmente aprecio mucho las reglas, por lo que he estado limpiando mis consultas con quote(), (int), y quoteName() que también significa montones de concatenación de cadenas (todos correctamente espaciados). Los resultados finales de mi trabajo son bloques de consulta horriblemente inflados que incluso a mí me cuesta mucho mirar. Las líneas peores/más largas que no se prestan al apilamiento vertical son las llamadas join() debido al nombre de la tabla, el alias, ON, luego una o más condiciones que pueden requerir o no citando Puedo apreciar que esta política se implementa teniendo en cuenta la seguridad para los desarrolladores novatos, pero seguro que me gustaría si esta política se atenuara de alguna manera con la sensibilidad de que no todos los codificadores de Joomla son ignorantes . Quiero decir, eche un vistazo a lo limpio y breve que se ve el código sin las llamadas innecesarias.

  • En cuanto a la limpieza:

    • Casi nunca uso * En mis cláusulas SELECT
    • Nunca llamo a __toString()
    • No cito enteros, los lanzo como enteros
    • No escribo ASC porque esa es la dirección de clasificación predeterminada
    • Hago todo lo posible para no usar palabras clave mysql al crear nuevos nombres de tablas y columnas
    • Como una cuestión de preferencia personal, tiendo a utilizar comillas dobles en los argumentos de cadena de mi método para mantener la uniformidad, distinguirme de las comillas simples de mysql y para poder disfrutar de la interpolación variable que escribo típicamente con " sintaxis compleja ".
    • Utilizo nombres informativos de variables y comentarios para ayudar en la legibilidad de mis consultas anidadas, y mi código en general
    • Pruebo mi código antes de que salga de mi custodia
1
mickmackusa