【问题标题】:Returning multiple aggregated columns from Subquery从子查询返回多个聚合列
【发布时间】:2021-02-23 02:01:38
【问题描述】:

我正在尝试通过聚合另一个表中的一些行来扩展现有查询。 当我只返回这样的一列时它可以工作:

Select DISTINCT 
   Contracts.id, 
   Contracts.beginTime, 
   Contracts.endTime, 
   Suppliers.name
   (SELECT COUNT(p.id) from production as p where p.id_contract = Contracts.id)
FROM Contracts 
LEFT JOIN Suppliers on Contracts.id = Suppliers.id_contract

然后我尝试为聚合卷添加另一列:

Select DISTINCT 
   Contracts.id, 
   Contracts.beginTime, 
   Contracts.endTime, 
   Suppliers.name
   (SELECT COUNT(p.id), SUM(p.volume) from production as p where p.id_contract = Contracts.id)
FROM Contracts 
LEFT JOIN Suppliers on Contracts.id = Suppliers.id_contract

但是,这会返回以下错误:

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

我对@9​​87654323@ 关键字进行了一些试验,但不知道如何使它起作用。我也不确定这是否适合我的情况。

想要的输出应该是这样的:

contract1Id, supplierInfoContract1, nrItemsContract1, sumVolumeContract1
contract2Id, supplierInfoContract2, nrItemsContract2, sumVolumeContract2

【问题讨论】:

  • exists 是一个子查询,您可以在其中将父表与子查询中的子表连接起来。如果子查询中有结果,则返回 true。你想要的输出是什么?
  • 添加了所需的输出。我希望它很清楚
  • 您的代码需要两个子查询,每个子查询只返回一个结果,则不会出现错误消息。错误消息告诉您,您的子查询为一个变量返回两个结果。

标签: sql sql-server tsql


【解决方案1】:

不使用DISTINCT 和子查询,使用GROUP BY 和普通连接 来获取聚合。并且总是使用别名,它会让你的生活更轻松:

SELECT
    c.id,
    c.beginTime,
    c.endTime,
    s.name,
    COUNT(p.id) prod_count,
    SUM(p.volume) prod_vol
FROM Contracts c
LEFT JOIN production p on p.id_contract = c.id
LEFT JOIN Suppliers s on c.id = s.id_contract
GROUP BY c.id, c.beginTime, c.endTime, s.name;

另一个选项是APPLY 分组子查询

SELECT DISTINCT
    c.id,
    c.beginTime,
    c.endTime,
    s.name,
    p.prod_count,
    p.prod_vol
FROM Contracts c
LEFT JOIN Suppliers s on c.id = s.id_contract
OUTER APPLY (
    SELECT
        COUNT(p.id) prod_count,
        SUM(p.volume) prod_vol
    FROM production p WHERE p.id_contract = c.id
    GROUP BY ()
) p;

您也可以使用CROSS APPLY 并省略GROUP BY ()this uses a scalar aggregate 并返回0 而不是null 以表示没有行。

最后一点:联合查询中的DISTINCTa bit of a code smell,这通常表明查询编写者并没有过多考虑联合表返回的内容,而只是想得到删除重复的行。

【讨论】:

  • 实际上,现有查询比我的示例大得多,并且选择包含大约 20 列。所以我可能需要将所有这些添加到 group by 子句中。不是很优雅,但我会试试看。
  • 更新了另一个可能更适合您的选项
  • 谢谢,第二个选项效果很好。我们使用一个自写的 C++ 库来生成我们的查询,它只支持一个简单的查询结构,第二个选项也适用于它。实际上我删除了 DISTINCT 关键字,因为它不是必需的。
【解决方案2】:

你应该像下面这样使用它:

Select DISTINCT Contracts.id, Contracts.beginTime, Contracts.endTime, Suppliers.name
(SELECT COUNT(p.id) from production as p where p.id_contract = Contracts.id) as CNT,
(SELECT SUM(p.volume) from production as p where p.id_contract = Contracts.id) as VOLUME
FROM Contracts 
LEFT JOIN Suppliers on Contracts.id = Suppliers.id_contract

【讨论】:

  • 我正在考虑这个问题,但我将来可能想添加多个额外的聚合列。我想这会非常低效,因为随着时间的推移,桌子也会变大。
  • 是的,如果您的聚合列太多,那么 CharlieFace 的回复将是理想的选择。确保在对较大的表应用联接时具有适当的主键、索引和 where 条件。
【解决方案3】:

我想您可以尝试将您的查询修改为

 SELECT X.ID,X.beginTime,X.endTime,X.name,CR.CNTT,CR.TOTAL_VOLUME
 FROM
 (
     Select DISTINCT Contracts.id, Contracts.beginTime, Contracts.endTime,      Suppliers.name
     FROM Contracts 
     LEFT JOIN Suppliers on Contracts.id = Suppliers.id_contract
 )X
 CROSS APPLY
 (
   SELECT COUNT(p.id)AS CNTT,SUM(p.volume) AS TOTAL_VOLUME
   from production as p where p.id_contract = X.id
 )CR

【讨论】:

    【解决方案4】:

    我通过分离子查询稍微修改了您的查询。

    Select DISTINCT 
       Contracts.id, 
       Contracts.beginTime, 
       Contracts.endTime, 
       Suppliers.name,
       (SELECT COUNT(p.id) from production as p where p.id_contract = Contracts.id) count_id,
        (SELECT SUM(p.volume) from production as p where p.id_contract = Contracts.id) sum_volume
    FROM Contracts 
    LEFT JOIN Suppliers on Contracts.id = Suppliers.id_contract
    

    【讨论】:

      猜你喜欢
      • 2012-08-25
      • 2019-09-26
      • 2010-12-18
      • 2018-09-21
      • 2013-06-15
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多