有效地从子查询中选择多个列(放置在 SELECT 线索中)

Posted

技术标签:

【中文标题】有效地从子查询中选择多个列(放置在 SELECT 线索中)【英文标题】:SELECT Multiple Columns FROM a subquery (placed in the SELECT Clues) Efficiently 【发布时间】:2019-10-03 04:55:15 【问题描述】:

我有 3 张桌子:

    表A 表B 表C

我需要对其中的一些列执行几个聚合函数(COUNT 和 SUM)。

我没有找到将它们连接在一起的方法(即使使用下面的 ClientId 列)所以我尝试了:

SELECT
(SELECT COUNT(*) FROM TableA 
WHERE ClientId = 2 AND IsDisabled != 1 AND IsDeleted != 1) AS ColumnName1, 
(SELECT COUNT(*) FROM TableB 
WHERE ClientId = 2 AND DisabledWhen IS NULL) AS ColumnName2,
(SELECT COUNT(*) FROM TableC
WHERE ClientId = 2 AND TDate >= CURRENT_TIMESTAMP -30) AS ColumnName3,
(SELECT SUM(TPrice + COALESCE (PPrice,0)) FROM TableC
WHERE ClientId = 2 AND TDate >= CURRENT_TIMESTAMP -30) AS ColumnName4; 

它正在工作,结果是:

+--------------+-------------+-------------+--------------+
| ColumnName1  | ColumnName2 | ColumnName3 | ColumnName4  |
+--------------+-------------+-------------+--------------+
| 202          | 86          | 25          | 4574.0000    |    
+--------------+-------------+-------------+--------------+

但我想将最后两个子查询合并为一个子查询。

所以我尝试这样做:

(SELECT COUNT(*) AS ColumnName3, SUM(TPrice + COALESCE (PPrice,0)) AS ColumnName4 FROM TableC
WHERE ClientId = 2 AND TDate >= CURRENT_TIMESTAMP -30);

但我收到此错误:

不引入子查询EXISTS时,选择列表中只能指定一个表达式

如何从一个子查询中获取多个列?

进行上述查询的有效方法是什么?

【问题讨论】:

如果您为单个客户端 ID 执行此操作,那么问题已得到解答。如果您想要跨多个客户端进行查询,那么使用表之间的连接可能有更好的方法。但是您必须向我们提供有关架构的一些信息,以便我们回答。 @TOMC 这是针对单个客户端 ID 【参考方案1】:

您可以将TableC 上的查询移动到最外层的选择,保持前两个子查询不变:

SELECT
    (SELECT COUNT(*) FROM TableA 
     WHERE ClientId = 2 AND IsDisabled != 1 AND IsDeleted != 1) AS ColumnName1, 
    (SELECT COUNT(*) FROM TableB 
    WHERE ClientId = 2 AND DisabledWhen IS NULL) AS ColumnName2,
    COUNT(*) AS ColumnName3,
    SUM(TPrice + COALESCE (PPrice, 0)) AS ColumnName4
FROM TableC
WHERE
    ClientId = 2 AND
    TDate >= CURRENT_TIMESTAMP - 30;

之所以有效,是因为前两个子查询基本上被视为常量。查询的其余部分仅在 TableC 上运行,让我们选择多个聚合。

【讨论】:

非常感谢,它运行良好!在大表中效率高吗? 其实这不是一个很有效的方法,你可以在count函数中只使用主键列,比如COUNT(Id)。但是此时您必须考虑为什么需要它,请检查整个架构,如果这是一种报告或类似的东西,这是您唯一拥有的有效方法。 @Dogan 相反,如果 OP 想要从 3 个不同的表中进行 4 个计数,这种方法是有效的,只需要扫描三个表中的每一个。 @tim-biegeleisen,乍一看你是对的,但后来我认为 tableC 末尾的条件看起来像返回很多行,所以这不意味着子查询将被执行得尽可能多作为行数?所以这会导致性能下降? @Dogan 我敢打赌,SQL Server 优化器足够聪明,可以识别出TableATableB 表上的子查询与外部查询不相关TableC 表上。因此,它只会运行一次子查询,然后在某处缓存计数。所以,我的回答比你想象的更有效率。

以上是关于有效地从子查询中选择多个列(放置在 SELECT 线索中)的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 从子查询顺序中选择

MySQL 从子查询顺序中选择

MySQL 从子查询顺序中选择

MySQL 从子查询顺序中选择

如何使用 laravel 5 中的查询生成器从子查询中进行选择

如何从子查询中返回两个字段