web-dev-qa-db-ja.com

JDatabaseを使用してサブクエリを作成する方法

http://docs.joomla.org/Selecting_data_using_JDatabase には、JDatabaseを使用してサブクエリを作成するための文書化された方法はありません。

https://Gist.github.com/gunjanpatel/86633 は、これを実現する1つの方法を示しています(いくつかのビットは省略されています)。

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

これは良い、もっともらしいアプローチのようですが、もっと良い方法はありますか?

31
betweenbrain

はい、私に関する限り、サブクエリの作成方法は、joomlaの拡張機能開発者の大多数が採用している方法です。

一部の拡張機能とクライアント用に作成されたカスタム拡張機能でも同じ方法を使用しています。

これを行う「公式の」方法はありませんが、あなたが示したようにそれを行うと、クエリビルダーを使用しながら、十分な可読性を維持できます。

16
Skullbock

AFAIKには簡単なサブクエリを実行するための組み込みの方法はありません。これはおそらくシステムの欠陥であり、PRを介して修正する必要があります。

しかし、私はあなたの例に問題がないと思います-十分に合理的であるようです。

~~~

以下は、@ DavidFritschのコメントに対する応答の例です。しかし、私がそれについて考えるほど、OPに表示されるより単純なアプローチが好きになります。何が起こっているのかはより明確です。

$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

JoomlaプラットフォームAPIを使用してサブクエリを含むクエリを実行する方法もあります。サブクエリの使用方法に関する基本的な考え方は、 gunjanpatel に基づいています。

ネストされたセットモデル でクエリを実行する例を次に示します。

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;

そして、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

スニペットの私のバージョンを提供し、私の正当化を説明し、 Joomlaコーディング標準マニュアル (quoteblock形式)からの引用を含めます。

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

クエリチェーンを使用して、多数のクエリメソッドを次々に接続し、各メソッドが次のメソッドをサポートできるオブジェクトを返すようにします。これにより、読みやすさが向上し、結果のコードが簡略化されます。

  • 最も内側のクエリを最初に記述し、最も外側のクエリに進みます。これにより、すべてのクエリ作成メソッドをgetQuery()メソッドに直接チェーンできます。実際には、変数名は、個々のクエリを作成するときに一度だけ書き込まれます。
    これが 重いクエリネストのすばらしい例 (連鎖矢印を並べるのがかわいいと思ったとき)です。

  • 同じクエリ内で複数のselect()およびwhere()呼び出しを行わないようにします。なぜなら、私はそれを見たからです 経験の浅い開発者の混乱を招く 。これらのメソッドは配列を受け入れるので、それらを採用する方が読みやすく、コーディング方法が優れていると思います。

  • そして最後に最も物議を醸すトピック...

    テーブル名とテーブル列をエスケープするには、テーブル名とテーブル列名を常にquoteName()メソッドで囲む必要があります。クエリでチェックされるフィールド値は、値をデータベースに渡す前にエスケープするために、常にquote()メソッドで囲む必要があります。クエリでチェックされる整数フィールド値も(int)に型キャストする必要があります。

    私はこのスタンスで非常に対立しています。昨年初めてJoomlaに来たとき、静的な値に対して無用な呼び出し(クエリの安定性、セキュリティ、読みやすさへのメリットはありません)を行うつもりはないと思いました!しかし、私の雇用主はJoomlaラインを利用するアイデアが好きであり、私は一般的にルールに高い評価を持っていることを認めなければならないので、quote()、_(int)_、およびquoteName()は、文字列連結のヒープも意味します(すべて適切な間隔で配置されます)。私の仕事の最終結果は、ひどく肥大化したクエリブロックであり、目を見張るのに苦労しています。垂直スタッキングに適さない最悪/最長の行は、テーブル名、エイリアス、ONのためにjoin()呼び出しであり、次に必要となる場合と必要とされない場合がある1つ以上の条件引用。 このポリシーは初心者の開発者向けのセキュリティを念頭に置いて実装されていることを認めることができますが、すべてのJoomlaコーダーが無知なコ​​ピーペーストであるわけではないという感性でこのポリシーをなんとか緩和したい場合は、是非ともお願いします。つまり、不要な呼び出しをせずにコードがどの程度簡潔で簡潔に見えるかを確認してください。

  • 掃討に関しては:

    • SELECT句で_*_を使用することはほとんどありません
    • __toString()を呼び出さない
    • 整数は引用しません。整数としてキャストします
    • ASCはデフォルトの並べ替え方向なので、書きません
    • 新しいテーブル名と列名を作成するときは、mysqlキーワードを使用しないように全力を尽くします
    • 個人的な好みの問題として、均一性を維持し、mysqlの単一引用符と区別するために、メソッドの文字列引数に二重引用符を使用する傾向があります。これにより、 " complex syntax 」.
    • ネストされたクエリとコード全体を読みやすくするために、有益な変数名とコメントを使用しています
    • 保管前にコードをテストします
1
mickmackusa