Metodo per la creazione di una sottoquery mediante JDatabase


31

A http://docs.joomla.org/Selecting_data_using_JDatabase , non esiste un metodo documentato per scrivere una sottoquery usando JDatabase.

https://gist.github.com/gunjanpatel/8663333 esemplifica un modo per ottenere ciò con (alcuni bit omessi):

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

Sembra un approccio valido e plausibile, ma ce n'è uno migliore?


4
Puoi omettere di chiamare toString () su $ subQuery. Joomla! lo gestirà automaticamente per te. A parte questo, io uso questo stesso metodo e funziona bene per me.
Zachary Draper,

È anche lo stesso metodo che stiamo usando in com_content nel core github.com/joomla/joomla-cms/blob/staging/components/…
George Wilson,

@ZacharyDraper interessante. Puoi mostrare il codice responsabile?
Dmitry Rekun,

3
@ZacharyDraper: PHP (piuttosto che Joomla! In sé) lo gestisce per te ( __toString()) è un metodo "magico".
MrWhite,

Sì, grazie w3d.
Zachary Draper,

Risposte:


16

Sì, per quanto mi riguarda, il modo in cui hai creato la subquery è quello adottato dalla maggior parte degli sviluppatori di estensioni di joomla.

Uso lo stesso metodo su alcune delle mie estensioni ed estensioni personalizzate create per i clienti.

Non esiste un modo "ufficiale" per farlo, ma farlo come hai mostrato ti consente di utilizzare il generatore di query e mantenere comunque una buona quantità di leggibilità


10

AFAIK non esiste un modo integrato per eseguire semplici subquery, il che è probabilmente un difetto del sistema e dovrebbe essere corretto tramite PR.

Tuttavia, non vedo alcun problema con il tuo esempio: sembra abbastanza ragionevole.

~~~

Ecco un esempio in risposta al commento di @ DavidFritsch di seguito. Più ci penso, meglio mi piace l'approccio più semplice visualizzato nel PO. È più chiaro cosa sta succedendo.

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

1
Hai idea di come potrebbe essere fatto funzionare? Sto cercando di immaginare il formato che useresti per far funzionare questo su un oggetto query e niente sembra più facile di questo metodo.
David Fritsch,

1
Potrebbe essere utile creare un subQuerySelectmetodo in cui ti consenta di farlo in modo più "pulito". Modificherò la mia risposta per fornire ed esempio.
Don Gilbert,

Mi piacerebbe vederlo in Joomla
fruppel,

3

C'è anche un modo per eseguire query che contengono sottoquery utilizzando l'API della piattaforma Joomla. L'idea di base su come utilizzare le subquery si basa su gunjanpatel .

Ecco un esempio per l'esecuzione di query su modelli di set nidificati :

Query 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;

e la query trasformata che verrà eseguita da 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>";

1
Sembra buono ma è assolutamente lo stesso dell'esempio del PO: crea prima la sottoquery e poi usala nella query principale. La domanda era se esiste un modo migliore.
fruppel,

1

Offrirò la mia versione dello snippet, poi spiegherò la mia giustificazione e includerò le citazioni dal manuale degli standard di codifica Joomla (che sarà formattato con il blocco delle virgolette).

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

Utilizzare il concatenamento di query per connettere una serie di metodi di query, uno dopo l'altro, con ciascun metodo che restituisce un oggetto in grado di supportare il metodo successivo. Ciò migliora la leggibilità e semplifica il codice risultante.

  • Scrivo prima le query più interne e procedo alla query più esterna. Questo mi consente di concatenare tutti i metodi di creazione delle query direttamente al getQuery()metodo. In effetti, il nome della variabile viene scritto una sola volta durante la creazione della singola query.
    Ecco un fantastico esempio di nidificazione di query pesanti (quando ho pensato che fosse carino allineare le frecce incatenate).

  • Cerco di evitare di effettuare più select()e / o where()chiamate all'interno della stessa query perché l'ho visto portare alla confusione di sviluppatori meno esperti . Poiché questi metodi accettano array, trovo più leggibile e una migliore pratica di codifica impiegarli.

  • e infine l'argomento più controverso ...

    I nomi delle tabelle e i nomi delle colonne della tabella devono sempre essere racchiusi nel metodo quoteName () per evitare il nome della tabella e le colonne della tabella. I valori dei campi controllati in una query devono sempre essere racchiusi nel metodo quote () per evitare il valore prima di passarlo al database. I valori dei campi interi controllati in una query devono anche essere digitati su (int).

    Sono molto in conflitto su questa posizione. Quando sono arrivato a Joomla l'anno scorso, ho pensato, non ho intenzione di fare chiamate inutili (nessun vantaggio per la stabilità, la sicurezza, la leggibilità della query) su valori statici! Tuttavia, il mio datore di lavoro piace l'idea di toeing linea di Joomla, e devo ammettere che mi hanno generalmente un alto apprezzamento per le regole, così mi è stato innaffiando le mie domande con quote(), (int)e quoteName()che significa anche cumuli di concatenazione di stringhe (tutti correttamente distanziati). I risultati finali del mio lavoro sono blocchi di query orrendamente gonfiati che anche io ho difficoltà a guardare negli occhi. Le linee peggiori / più lunghe che non si prestano allo stacking verticale sono le join()chiamate a causa del nome della scheda, dell'alias ON, quindi di una o più condizioni che possono o meno richiedere quotazioni.Posso apprezzare che questa politica è implementata pensando alla sicurezza per gli sviluppatori alle prime armi, ma mi piacerebbe sicuramente che questa politica fosse in qualche modo mitigata dalla sensibilità che non tutti i programmatori di Joomla sono ignoranti. Voglio dire, guarda come appare pulito e breve il codice senza le chiamate inutili.

  • Per quanto riguarda il rastrellamento:

    • Non uso quasi mai *nelle mie clausole SELECT
    • Non chiamo mai __toString()
    • Non cito numeri interi, li lancio come numeri interi
    • Non scrivo ASCperché questa è la direzione di ordinamento predefinita
    • Faccio ogni sforzo per non usare le parole chiave mysql durante la creazione di nuovi nomi di tabelle e nomi di colonne
    • Per preferenza personale, tendo a usare la doppia virgoletta sugli argomenti di stringa del mio metodo per mantenere l'uniformità, distinguermi dalla virgoletta singola di mysql, e in modo da poter godere dell'interpolazione variabile che scrivo tipicamente con " sintassi complessa ".
    • Uso nomi di variabili informativi e commenti per facilitare la leggibilità delle mie query nidificate e il mio codice in generale
    • Provo il mio codice prima che lasci la mia custodia
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.