值在执行连接时,莫名其妙地乘以15的幂?

时间:2015-02-10 18:05:47

标签: sql sql-server tsql

所以我开始构建一个查询,将5个单独的表连接在一起,以获取乘法和求和的值。但是,我似乎面临着一个相当奇怪的问题。每当我尝试将某个表连接到我的查询时,突然我的所有值都乘以15,计数,总和等等。我试图弄清楚导致所有这些额外运行的原因。有什么想法吗?

完整查询

USE Facilities_Database
DECLARE @minimumDate DATE
DECLARE @maximumDate DATE

SET @minimumDate = '2014/12/11'
SET @maximumDate = '2014/12/15' 

SELECT  tab4.TypeName AS 'Labor Type'
        ,tab1.Building
        ,CAST(@minimumDate AS nvarchar(255)) +  ' - ' + CAST(@maximumDate AS nvarchar(255)) AS 'Date Range'
        ,Count(tab1.CHSRNumber) AS 'Number of CHSRs'
        ,ISNULL(SUM(tab5.[Item Cost] * tab3.[Amount Used]),0) AS 'Total Material Cost'
        ,ISNULL(SUM(tab2.[Hour Worked] * tab2.[Hourly CHSR Labor Rate]),0) AS 'Total Labor Cost'
FROM    [Facilities].[HardwareSupportRequest] tab1
    JOIN Facilities.tblCHSRLaborPerCHSR tab2
        ON tab1.CHSRNumber = tab2.[CHSR #]
    JOIN Facilities.tblMaterialUsed tab3
        ON tab1.CHSRNumber = tab3.[CHSR #]
    JOIN Facilities.LaborTypes tab4
        ON tab2.LaborTypeId = tab4.Id
    JOIN Facilities.tblMaterial tab5
        ON tab3.MaterialId = tab5.Id
WHERE tab1.ActualCompleteDate BETWEEN @minimumDate AND @maximumDate AND tab4.TypeName IS NOT NULL
GROUP BY    tab4.TypeName,Building
ORDER BY    Building,tab4.TypeName

工作查询:

USE Facilities_Database
SELECT  tab1.Building
        ,COUNT(*) AS 'CHSR Count'
        ,SUM(tab2.[Hour Worked] * 40) AS 'Labor Cost'
    FROM [Facilities].[HardwareSupportRequest] tab1
    INNER JOIN Facilities.tblCHSRLaborPerCHSR tab2 ON
        tab1.CHSRNumber = tab2.[CHSR #]
    INNER JOIN Facilities.LaborTypes tab3 ON
        tab2.LaborTypeId = tab3.Id
    --INNER JOIN Facilities.tblMaterialUsed tab4 ON
        --tab4.[CHSR #] = tab1.CHSRNumber
    --INNER JOIN Facilities.tblMaterial tab5 ON
    --  tab4.MaterialId = tab5.Id
    WHERE ActualCompleteDate BETWEEN '2014/12/11' AND '2014/12/15'
    GROUP BY tab1.Building,tab3.TypeName

导致问题的表是tblMaterialsUsed

感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

您的JOIN条件不足,导致一行加入多行。

如果以下查询产生两个不同的数字,那么您应该知道JOIN应该受到指责:

SELECT COUNT(*),COUNT(DISTINCT [CHSR #])
FROM Facilities.tblMaterialUsed

然后,您需要先确定如何通过添加JOIN条件或者首先在子查询中聚合来排除额外记录。

<强>更新: 要首先聚合,您可以使用cte或子查询按CHSR #聚合,然后加入到该cte /子查询:

;WITH Materials AS (SELECT mat.[CHSR #]
                          ,ISNULL(SUM(tab5.[Item Cost] * tab3.[Amount Used]),0) AS Total_Material_Cost                  
                    FROM Facilities.tblMaterialUsed tab3
                    JOIN Facilities.tblMaterial tab5
                      ON tab3.MaterialId = tab5.Id
                    GROUP BY mat.[CHSR #]
                    )
SELECT  tab4.TypeName AS 'Labor Type'
        ,tab1.Building
        ,CAST(@minimumDate AS nvarchar(255)) +  ' - ' + CAST(@maximumDate AS nvarchar(255)) AS 'Date Range'
        ,Count(tab1.CHSRNumber) AS 'Number of CHSRs'
        ,SUM(mat.Total_Material_Cost) AS 'Total Material Cost'
        ,ISNULL(SUM(tab2.[Hour Worked] * tab2.[Hourly CHSR Labor Rate]),0) AS 'Total Labor Cost'
FROM    [Facilities].[HardwareSupportRequest] tab1
    JOIN Facilities.tblCHSRLaborPerCHSR tab2
        ON tab1.CHSRNumber = tab2.[CHSR #]
    JOIN Facilities.LaborTypes tab4
        ON tab2.LaborTypeId = tab4.Id
    JOIN Materials mat
        ON tab1.CHSRNumber = mat.[CHSR #]
WHERE tab1.ActualCompleteDate BETWEEN @minimumDate AND @maximumDate AND tab4.TypeName IS NOT NULL
GROUP BY    tab4.TypeName,Building
ORDER BY    Building,tab4.TypeName

答案 1 :(得分:0)

如果添加JOIN会使GROUP BY操作的结果相乘,则JOIN每个JOIN条件返回的行数超过1行。要么缩小它(可能你缺少一个或多个JOIN字段?)或者在GROUP BY中添加更多字段来改变聚合内容的粒度。

问题是每个[CHSR #]都有一个材料用户列表。这些行导致“笛卡尔积”,使tblMaterialUsed中每行的其他行重复。因此,当Material CostNumber of CHSRs成倍增加时,总Total Labor Cost字段可能是正确的。从本质上讲,您需要以相同的粒度级别对数据进行分组,这意味着CHSRNumber / [CHSR #]

之间的对数为1比1

以下内容应解决此问题。如果没有,那将是由于主查询中每CHSRNumber / [CHSR #]行超过1行(每个CHSRNumber / {{1加入1行} [CHSR #] CTE)。在这种情况下,您可以通过为该聚合创建第二个CTE,然后在新的主查询中加入这两个结果,将相同的理论应用于主查询。 (我已经更新了下面的查询以纳入该更改,因为它可能不需要它)

material

如果您想在View中使用它,请使用内联表值函数,方法是在开头添加以下内容:

;WITH material AS
(
  SELECT mu.[CHSR #],
         ISNULL(SUM(mtrl.[Item Cost] * mu.[Amount Used]),0) AS [MaterialCost]
  FROM   Facilities.tblMaterialUsed mu
  INNER JOIN Facilities.tblMaterial mtrl
          ON mu.MaterialId = mtrl.Id
  GROUP BY mu.[CHSR #]
), labour AS
(
  SELECT tab1.CHSRNumber,
         tab4.TypeName,
         tab1.Building,
         ISNULL(SUM(tab2.[Hour Worked] * tab2.[Hourly CHSR Labor Rate]),0) AS [LaborCost]
  FROM   [Facilities].[HardwareSupportRequest] tab1
  JOIN   Facilities.tblCHSRLaborPerCHSR tab2
     ON tab1.CHSRNumber = tab2.[CHSR #]
  JOIN Facilities.LaborTypes tab4
     ON tab2.LaborTypeId = tab4.Id
  WHERE tab1.ActualCompleteDate BETWEEN @minimumDate AND @maximumDate
  AND   tab4.TypeName IS NOT NULL
  GROUP BY    tab1.CHSRNumber, tab4.TypeName, tab1.Building
)
SELECT  labour.TypeName AS [Labor Type],
        labour.Building,
        CAST(@minimumDate AS NVARCHAR(255)) +  ' - '
           + CAST(@maximumDate AS NVARCHAR(255)) AS [Date Range],
        COUNT(labour.[CHSRNumber]) AS [Number of CHSRs],
        SUM(material.[MaterialCost]) AS [Total Material Cost],
        SUM(labour.[LaborCost]) AS [Total Labor Cost]
FROM    labour
INNER JOIN material
        ON labour.CHSRNumber = material.[CHSR #]
GROUP BY  labour.TypeName, labour.Building
ORDER BY  labour.Building, labour.TypeName;

  • 删除CREATE FUNCTION GetCosts (@minimumDate DATE, @maximumDate DATE) RETURNS TABLE AS RETURN
  • 之前的;
  • 删除;WITH

此外,如果您使用表别名而不是ORDER BYtab1等首字母缩略词,这将是一个巨大的好处,因为它会使查询更多更容易阅读,特别是考虑到两个查询中的同一个表甚至不是tab2

答案 2 :(得分:0)

看来我可以使用子查询来解决这个问题

USE Facilities_Database
DECLARE @minimumDate DATE
DECLARE @maximumDate DATE

SET @minimumDate = '2012/12/11'
SET @maximumDate = '2014/12/15' 

SELECT  LaborTypes.TypeName AS 'Labor Type'
        ,CHSRs.Building
        ,CAST(@minimumDate AS nvarchar(255)) +  ' - ' + CAST(@maximumDate AS nvarchar(255)) AS 'Date Range'
        ,Count(CHSRs.CHSRNumber) AS 'Number of CHSRs'
        ,ISNULL(SUM(matUsed.cost),0) AS 'Total Material Cost'
        ,ISNULL(SUM(Labor.[Hour Worked] * Labor.[Hourly CHSR Labor Rate]),0) AS 'Total Labor Cost'
FROM    [Facilities].[HardwareSupportRequest] CHSRs
    JOIN Facilities.tblCHSRLaborPerCHSR Labor
        ON CHSRs.CHSRNumber = Labor.[CHSR #]
    JOIN (SELECT ROUND(SUM(matU.[Amount Used] * mat.[Item Cost] * 1.05417 * 1.15),2) AS cost, [CHSR #]
        FROM Facilities.tblMaterialUsed matU
        JOIN Facilities.tblMaterial mat ON
        matU.MaterialId = mat.Id
        GROUP BY matU.[CHSR #]) matUsed ON
        matUsed.[CHSR #] = CHSRs.CHSRNumber
    --JOIN Facilities.tblMaterialUsed tab3
    --  ON CHSRs.CHSRNumber = tab3.[CHSR #]
    JOIN Facilities.LaborTypes LaborTypes
        ON Labor.LaborTypeId = LaborTypes.Id
    --JOIN Facilities.tblMaterial tab5
    --  ON tab3.MaterialId = tab5.Id
WHERE CHSRs.ActualCompleteDate BETWEEN @minimumDate AND @maximumDate AND LaborTypes.TypeName IS NOT NULL
GROUP BY    LaborTypes.TypeName,Building
ORDER BY    Building,LaborTypes.TypeName