Methode zum Erstellen einer Unterabfrage mit JDatabase


31

Unter http://docs.joomla.org/Selecting_data_using_JDatabase gibt es keine dokumentierte Methode zum Schreiben einer Unterabfrage mit JDatabase.

https://gist.github.com/gunjanpatel/8663333 zeigt beispielhaft einen Weg, um dies zu erreichen (einige Bits weggelassen):

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

Dies scheint ein guter, plausibler Ansatz zu sein, aber gibt es einen besseren?


4
Sie können den Aufruf von toString () für $ subQuery weglassen. Joomla! erledigt das automatisch für Sie. Abgesehen davon benutze ich die gleiche Methode und es funktioniert gut für mich.
Zachary Draper

Es ist auch das gleiche Verfahren wir in com_content in Kern mit github.com/joomla/joomla-cms/blob/staging/components/...
George Wilson

@ ZacharyDraper interessant. Können Sie den dafür verantwortlichen Code anzeigen?
Dmitry Rekun

3
@ZacharyDraper: PHP (und nicht Joomla! Per se) erledigt dies für Sie ( __toString()) ist eine "magische" Methode.
MrWhite

Ja, danke w3d.
Zachary Draper

Antworten:


16

Ja, soweit es mich betrifft, ist die Art und Weise, wie Sie die Unterabfrage erstellt haben, von der Mehrheit der Entwickler von Joomla-Erweiterungen übernommen worden.

Ich verwende dieselbe Methode für einige meiner Erweiterungen und benutzerdefinierten Erweiterungen, die für Clients erstellt wurden.

Es gibt keine "offizielle" Methode, aber Sie können den Abfrage-Generator verwenden und trotzdem eine gute Lesbarkeit beibehalten, wenn Sie dies wie gezeigt tun


10

AFAIK, es gibt keine eingebaute Möglichkeit, einfache Unterabfragen durchzuführen. Dies ist wahrscheinlich ein Mangel im System und sollte über PR korrigiert werden.

Ich sehe jedoch kein Problem mit Ihrem Beispiel - scheint vernünftig genug.

~~~

Hier ist ein Beispiel als Antwort auf @ DavidFritschs Kommentar unten. Je mehr ich darüber nachdenke, desto besser gefällt mir der einfachere Ansatz, der im OP angezeigt wird. Es ist klarer, was los ist.

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

1
Haben Sie eine Idee, wie das funktionieren könnte? Ich versuche mir das Format vorzustellen, das Sie verwenden würden, um diese Funktion für ein Abfrageobjekt auszuführen, und nichts fühlt sich tatsächlich einfacher an als diese Methode.
David Fritsch

1
Es könnte sich lohnen, eine subQuerySelectMethode zu erstellen , mit der Sie es ein bisschen "sauberer" machen können. Ich werde meine Antwort bearbeiten, um ein Beispiel zu liefern.
Don Gilbert

Ich würde gerne , dass in Joomla zu sehen
fruppel

3

Es gibt auch eine Möglichkeit, Abfragen, die Unterabfragen enthalten, mithilfe der Joomla Platform-API auszuführen. Die Grundidee zur Verwendung von Unterabfragen basiert auf gunjanpatel .

Hier ist ein Beispiel für die Ausführung von Abfragen in verschachtelten Gruppenmodellen :

SQL-Abfrage:

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

und die von Joomla auszuführende transformierte Abfrage:

// 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
Sieht gut aus, ist aber genauso wie im Beispiel des OP: Erstellen Sie zuerst die Unterabfrage und verwenden Sie sie dann in der Hauptabfrage. Die Frage war, ob es einen besseren Weg gibt.
Fruppel

1

Ich biete meine Version des Snippets an, erkläre dann meine Rechtfertigung und füge Zitate aus dem Joomla Coding Standards-Handbuch hinzu (das mit Quoteblock formatiert wird).

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

Verwenden Sie die Abfrageverkettung, um eine Reihe von Abfragemethoden nacheinander zu verbinden, wobei jede Methode ein Objekt zurückgibt, das die nächste Methode unterstützen kann. Dies verbessert die Lesbarkeit und vereinfacht den resultierenden Code.

  • Ich schreibe zuerst die innersten Abfragen und fahre mit der äußersten Abfrage fort. Dadurch kann ich alle Methoden zur Abfrageerstellung direkt mit der getQuery()Methode verketten. Tatsächlich wird der Variablenname beim Erstellen der einzelnen Abfrage nur einmal geschrieben.
    Hier ist ein großartiges Beispiel für eine umfangreiche Verschachtelung von Abfragen (als ich dachte, es wäre nett, die verketteten Pfeile in einer Reihe anzuordnen).

  • Ich versuche zu vermeiden, mehrere select()und / oder where()Anrufe innerhalb derselben Abfrage zu tätigen, da dies zu Verwirrung bei weniger erfahrenen Entwicklern geführt hat . Da diese Methoden Arrays akzeptieren, finde ich es lesbarer und praktischer, sie zu verwenden.

  • und schließlich das umstrittenste Thema ...

    Tabellennamen und Tabellenspaltennamen sollten immer in die quoteName () -Methode eingeschlossen werden, um den Tabellennamen und die Tabellenspalten zu umgehen. In einer Abfrage überprüfte Feldwerte sollten immer in die quote () - Methode eingeschlossen werden, um den Wert zu umgehen, bevor er an die Datenbank übergeben wird. Ganzzahlige Feldwerte, die in einer Abfrage überprüft wurden, sollten ebenfalls in (int) umgewandelt werden.

    Ich bin sehr widersprüchlich in dieser Haltung. Als ich letztes Jahr zum ersten Mal zu Joomla kam, dachte ich, ich werde keine unbrauchbaren Anrufe (kein Vorteil für die Stabilität, Sicherheit und Lesbarkeit der Abfrage) bei statischen Werten tätigen! Allerdings mag mein Arbeitgeber die Idee , die Joomla Linie toeing, und ich muss zugeben , dass ich in der Regel eine hohe Wertschätzung für die Regeln, so habe ich meine Fragen mit Abspritzen quote(), (int)und quoteName()das bedeutet auch , Haufen von String - Verkettung (alle richtig beabstandet). Das Endergebnis meiner Arbeit sind schrecklich aufgeblähte Abfrageblöcke, die selbst mir schwer fallen, in die Augen zu blicken. Die schlechtesten / längsten Zeilen, die sich nicht für vertikales Stapeln eignen, sind die join()Aufrufe aufgrund des Tabellennamens, des Alias ON, dann einer oder mehrerer Bedingungen, die möglicherweise ein Zitieren erfordern oder nicht.Ich kann zu schätzen wissen, dass diese Richtlinie unter Sicherheitsgesichtspunkten für unerfahrene Entwickler implementiert wird, aber ich würde es sicher begrüßen, wenn diese Richtlinie irgendwie mit der Sensibilität gemildert würde, dass nicht alle Joomla-Codierer unwissende Copy-Paster sind. Ich meine, sehen Sie sich an, wie sauber und kurz der Code ohne unnötige Aufrufe aussieht.

  • Zum Aufwischen:

    • Ich verwende fast nie *in meinen SELECT-Klauseln
    • Ich rufe nie an __toString()
    • Ich zitiere keine ganzen Zahlen, ich spreche sie als ganze Zahlen
    • Ich schreibe nicht, ASCweil das die Standardsortierrichtung ist
    • Ich bemühe mich, beim Erstellen neuer Tabellen- und Spaltennamen keine mysql-Schlüsselwörter zu verwenden
    • Aus persönlichen Gründen neige ich dazu, die String-Argumente meiner Methode in doppelte Anführungszeichen zu setzen, um die Einheitlichkeit zu gewährleisten, mich von den einfachen Anführungszeichen in mysql zu unterscheiden und eine variable Interpolation zu genießen, die ich normalerweise mit " komplexer Syntax " schreibe .
    • Ich verwende informative Variablennamen und Kommentare, um die Lesbarkeit meiner verschachtelten Abfragen und meines Codes im Allgemeinen zu verbessern
    • Ich teste meinen Code, bevor er meine Verwahrung verlässt
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.