SQL Server:带有内部 SELECT 和子 SELECT 的 SUM()。每次都出错

Posted

技术标签:

【中文标题】SQL Server:带有内部 SELECT 和子 SELECT 的 SUM()。每次都出错【英文标题】:SQL Server: SUM() with internal SELECT and sub SELECT. Error every time 【发布时间】:2019-12-18 17:42:52 【问题描述】:

问题改写

我在 SQL 语句中以下列定义及其自己的 GROUP BY...

    SUM((SELECT a.CONT_TOT
     FROM  (SELECT   gl2.VisitID, gl2.MessageID, gl2.BillOfLading, COUNT(gl2.ContainerID) AS CONT_TOT
            FROM     dbo.tblEDIGoodsLines AS gl2
            WHERE    gl2.VisitID = gl.VisitID AND gl2.MessageID = gl.MessageID AND gl2.BillOfLading = gl.BillOfLading
            GROUP BY gl2.VisitID, gl2.MessageID, gl2.BillOfLading) AS a)) as TotalContainers,    

...etc

我不断收到这个错误。

无法对包含 聚合或子查询。

我正在尝试获取外部/更大 SELECT 中的总行数,以及 TOTUCONT 中的 UNIQUE 容器总数。

我做错了什么?

这是更大的 SQL 查询,以说明我对 GROUP BY 和 SUM() 等聚合函数中的子查询的观点:

SELECT
    gl.MessageID,
    gl.BillOfLading,
    gl.[Description],
    CASE WHEN e.PortID = 9 THEN 'Export'
      WHEN e.PortID = 11 THEN 'Import'
      ELSE 'ERROR'
    END AS Direction,
    CASE WHEN ctypes.ID IS NOT NULL
         THEN ctypes.ContainerSizeType 
         ELSE 'OTH'
    END AS CSizeType,
    ctypes.Length_ft + 'ft ' + ctypes.Height_ft + 'ft - ' + ctypes.Characteristics + ' (' + COALESCE(ctypes.Codes1995, ctypes.Codes1984) + ')' AS ContainerType,
    COUNT(gl.ContainerID) AS TOTCONT,
    SUM(a.CTOTAL) AS TOTUCONT
FROM tblEDIGoodsLines AS gl 
    INNER JOIN tblEDIEquipmentLines AS el 
        ON el.MessageID = gl.MessageID AND
           el.ContainerID = gl.ContainerID
    INNER JOIN tblEDI AS e
        ON CHARINDEX(e.MessageID, gl.MessageID) > 0 AND
           e.VisitID = gl.VisitID AND
           CHARINDEX('EXCEL', e.MessageRelease) = 0 AND
           e.Status = 1
    LEFT JOIN tblContainerTypesISO6346 AS ctypes 
        ON ctypes.Codes1984 = el.SizeAndType OR 
           ctypes.Codes1995 = el.SizeAndType
    LEFT JOIN (SELECT gl2.MessageID, gl2.VisitID, gl2.BillOfLading, gl2.description, COUNT(DISTINCT gl2.ContainerID) AS CTOTAL
              FROM tblEDIGoodsLines AS gl2 
              WHERE gl2.MessageID = gl.MessageID 
                AND gl2.VisitID = gl.VisitID 
                and gl2.BillOfLading = gl.billoflading 
                and gl2.description = gl.description
              GROUP BY gl2.MessageID, gl2.VisitID, gl2.BillOfLading, gl2.description) AS a
        ON a.MessageID = gl.MessageID AND a.VisitID = gl.VisitID AND a.BillOfLading = gl.billoflading AND a.description = gl.description
WHERE gl.Status = 1
  AND gl.VisitID = 22987
GROUP BY
    gl.MessageID,
    gl.BillOfLading,
    gl.[Description],
    CASE WHEN e.PortID = 9 THEN 'Export'
         WHEN e.PortID = 11 THEN 'Import'
         ELSE 'ERROR'
    END,
    CASE WHEN ctypes.ID IS NOT NULL
         THEN ctypes.ContainerSizeType 
         ELSE 'OTH'
    END,
    ctypes.Length_ft + 'ft ' + ctypes.Height_ft + 'ft - ' + ctypes.Characteristics + ' (' + COALESCE(ctypes.Codes1995, ctypes.Codes1984) + ')'

以上无论如何都不起作用,因为我试图通过将“列” SELECT 分成它自己的 JOIN 查询来解决这个问题,但现在我明白了:

无法绑定多部分标识符“gl.MessageID”。

所以这意味着 LEFT JOIN (SELECT...) 无效?

再次感谢

更新 2

这里有一个数据样本来进一步解释:

所以你可以看到我想要的结果,TOTCONT 加起来是“4”。这很简单 - 只计算行数,但 TOTUCONT 只计算一次容器 ID。

【问题讨论】:

添加示例输入和输出数据可能会让其他人更容易理解您的问题。 您的 SQL 看起来有效,您确定这是给出错误的那一行吗? @Nick 是的。我也是这么想的,按照语法 你可以使用多个CTE来解决这个问题,可以参考早期CTE的结果来解决聚合问题。使用这种方法可以避免在GROUP BY 中复制CASE 语句和ContainerType 计算。数据库必须遵循类似的执行计划(不太可能影响性能)。这将使代码的每个部分更加模块化和可读。 【参考方案1】:

尝试将代码重构为更像这样(正如我之前评论的那样)。它还可能有助于解决您正在处理的其他问题:

with cte_ctotal
as (
    select gl2.MessageID,
        gl2.VisitID,
        gl2.BillOfLading,
        gl2.description,
        COUNT(distinct gl2.ContainerID) as CTOTAL
    from tblEDIGoodsLines as gl2
    group by gl2.MessageID,
        gl2.VisitID,
        gl2.BillOfLading,
        gl2.description
    ),
cte_containers
as (
    select
    a.ContainerID,
    gl.MessageID,
    gl.VisitID,
    gl.BillOfLading,
    gl.Description,
    case 
        when e.PortID = 9
            then 'Export'
        when e.PortID = 11
            then 'Import'
        else 'ERROR'
        end as Direction,
    case 
        when ctypes.ID is not null
            then ctypes.ContainerSizeType
        else 'OTH'
        end as CSizeType,
    ctypes.Length_ft + 'ft ' + ctypes.Height_ft + 'ft - ' + ctypes.Characteristics + ' (' + COALESCE(ctypes.Codes1995, ctypes.Codes1984) + ')' as ContainerType,
    from tblEDIGoodsLines gl
    inner join tblEDIEquipmentLines el on el.MessageID = gl.MessageID
                                        and el.ContainerID = gl.ContainerID
    inner join tblEDI e on CHARINDEX(e.MessageID, gl.MessageID) > 0
                        and e.VisitID = gl.VisitID
                        and CHARINDEX('EXCEL', e.MessageRelease) = 0
                        and e.status = 1
    left join tblContainerTypesISO6346 ctypes on ctypes.Codes1984 = el.SizeAndType
                                              or ctypes.Codes1995 = el.SizeAndType
    where gl.status = 1
    and gl.VisitID = 22987
    )
select 
c.MessageID,
c.BillOfLading,
c.Description,
c.Direction,
c.CSizeType,
c.ContainerType,
COUNT(c.ContainerID) as TOTCONT,
SUM(COALESCE(ct.CTOTAL,0)) as TOTUCONT
from cte_containers c
left join cte_ctotal ct on ct.MessageID = c.MessageID
                       and ct.VisitID = c.VisitID
                       and ct.BillOfLading = c.billoflading
                       and ct.description = c.description
group by c.MessageID,
    c.BillOfLading,
    c.Description,
    c.Direction,
    c.CSizeType,
    c.ContainerType;

【讨论】:

是的,我就是这么做的。事实上,我更进一步,将我的 CTE 变成了一个视图,并像普通表格一样链接它。十分简单。感谢您的想法。【参考方案2】:

改成这样:

SUM(a.CONT_TOT) as TotalContainers
     FROM  (
       SELECT   COUNT(gl2.ContainerID) AS CONT_TOT
       FROM     dbo.tblEDIGoodsLines AS gl2
       WHERE    gl2.VisitID = gl.VisitID AND gl2.MessageID = gl.MessageID AND gl2.BillOfLading = gl.BillOfLading
       GROUP BY gl2.VisitID, gl2.MessageID, gl2.BillOfLading
     ) AS a,

根据代码的用途,您可能需要在末尾使用括号。 我从 SELECT 列表中删除了其他列,因为它们不需要获取 SUM() (除非您需要它们来做其他事情?)。 编辑 从最后一个 JOIN 中删除 WHERE 子句:

WHERE gl2.MessageID = gl.MessageID 
  AND gl2.VisitID = gl.VisitID 
  and gl2.BillOfLading = gl.billoflading 
  and gl2.description = gl.description

这些条件应用在 ON 子句中。

【讨论】:

谢谢,但这不起作用,因为我尝试使用外括号。我得到 Column 'tblEDIGoodsLines.VisitID' 在选择列表中无效,因为它既不包含在聚合函数中,也不包含在 GROUP BY 子句中。这是因为它自己的 SUM() 不能与 FROM 一起使用,除非它之前有一个 SELECT,这会引发上述错误 我假设您发布的代码只是您使用的代码的一部分,并且在 SUM() 之前有一个 SELECT 不存在? 感谢@forpas,但我在独特案例中获得的计数比整体案例更多。我知道它应该更少(基于 Excel 数据透视表) 我不知道你得到的结果。您正在对子查询获得的计数器求和。也许您可以单独执行子查询并检查这些计数器。 错误数据示例。对不起,这个问题已经超出了原来的问题。我对整体 SQL 有其他问题。感谢您的宝贵时间。【参考方案3】:

我认为您可以将其简化为:

(SELECT COUNT(gl2.ContainerID)
 FROM dbo.tblEDIGoodsLines gl2
 WHERE gl2.VisitID = gl.VisitID AND
       gl2.MessageID = gl.MessageID AND
       gl2.BillOfLading = gl.BillOfLading
) as TotalContainers,    

注意事项:

不需要外层SUM()。您可以在子查询中进行聚合。 您不需要两个级别的子查询。 GROUP BY 是不必要的。没有 GROUP BY 的聚合查询始终只返回一行,这就是您想要的标量子查询。

根据您查询的其余部分,这可能仍不适用于您的完整查询。如果是这种情况,那么您应该提出一个问题,其中包含示例数据、所需结果以及(简化版本)不起作用的查询。

【讨论】:

根据我的其他回复。还是谢谢【参考方案4】:

试试这个,

 (SELECT SUM(a.CONT_TOT)
 FROM  (SELECT   gl2.VisitID, gl2.MessageID, gl2.BillOfLading, COUNT(gl2.ContainerID) AS CONT_TOT
        FROM     dbo.tblEDIGoodsLines AS gl2
        WHERE    gl2.VisitID = gl.VisitID AND gl2.MessageID = gl.MessageID AND gl2.BillOfLading = gl.BillOfLading
        GROUP BY gl2.VisitID, gl2.MessageID, gl2.BillOfLading) AS a) as TotalContainers, 

【讨论】:

同样,这不起作用,因为整个 SELECT 周围的 () 使我的较大查询发送一个错误,即较大查询的 GROUP BY 中没有聚合。我会更新我的问题来解释。

以上是关于SQL Server:带有内部 SELECT 和子 SELECT 的 SUM()。每次都出错的主要内容,如果未能解决你的问题,请参考以下文章

带有 JOIN 的 SQL Server SELECT 分页

带有内部联接和子查询的 Microsoft Access 更新语句

带有 2 个内部联接的 SQL Server 2014 STUFF

SQL Server代理的阶梯 - 第2级:作业步骤和子系列

连接sql server2005时提示:内部连接致命错误。

带有内部联接的 SQL 更新查询语法