使用JDatabase创建子查询的方法


31

http://docs.joomla.org/Selecting_data_using_JDatabase上,没有使用JDatabase编写子查询的书面方法。

https://gist.github.com/gunjanpatel/8663333举例说明了一种实现此目的的方式(省略了一些位):

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

这似乎是一种不错的方法,但是有没有更好的方法呢?


4
您可以省略对$ subQuery的toString()调用。Joomla!会自动为您处理。除此之外,我使用相同的方法,对我来说效果很好。
Zachary Draper 2014年


@ZacharyDraper有趣。您可以显示对此负责的代码吗?
德米特里·雷昆2014年

3
@ZacharyDraper:PHP(而不是Joomla!本身)为您处理它(__toString())是一种“魔术”方法。
MrWhite

是的,谢谢你。
Zachary Draper 2014年

Answers:


16

是的,就我而言,构建子查询的方式是大多数joomla扩展开发人员所采用的方式。

我在某些扩展和为客户端创建的自定义扩展上使用了相同的方法。

没有“正式”的方法可以执行此操作,但是如您所显示的,它使您可以使用查询生成器并仍然保留大量的可读性


10

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

1
您是否知道如何使它起作用?我正在尝试想象将用于对一个查询对象进行此工作的格式,实际上没有什么比这种方法更容易实现的了。
David Fritsch 2014年

1
创建一种subQuerySelect使您可以更“干净”地进行操作的方法可能是值得的。我将编辑答案以提供示例。
Don Gilbert 2014年

我很想在Joomla
fruppel'9

3

还有一种使用Joomla Platform 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>";

1
看起来不错,但与OP示例完全相同:首先进行子查询,然后在主查询中使用它。问题是是否有更好的方法。
fruppel

1

我将提供该代码段的版本,然后说明我的理由,并包括《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()这也意味着字符串串联(全部适当间隔)。我的工作的最终结果是令人blocks肿的查询块,即使我也很难察觉。不能进行垂直堆叠的最差/最长的行是join()调用,因为表名,别名ON,然后是一个或多个条件,可能需要也可能不需要引用。我可以理解,对于新手开发人员来说,在执行此策略时会考虑到安全性,但是如果此策略由于某种原因而不是所有Joomla编码人员都是无知的复制粘贴者而受到一定程度的调节,我肯定会喜欢它。 我的意思是,看看没有不必要的调用,代码的外观如何简洁。

  • 至于扫荡:

    • 我几乎从不*在SELECT子句中使用
    • 我从不打电话 __toString()
    • 我不引用整数,而是将它们转换为整数
    • 我不写,ASC因为这是默认的排序方向
    • 我尽一切努力在创建新表名和列名时不使用mysql关键字
    • 出于个人喜好,我倾向于在方法的字符串参数上使用双引号来保持一致性,区别于mysql的单引号,这样我就可以享受变量插值,我通常使用“ 复杂语法 ” 编写。
    • 我使用信息量大的变量名和注释来提高嵌套查询的可读性,通常我的代码也是如此
    • 我在离开托管之前测试代码
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.