0

我有两个问题。 First:我正在尝试将两个查询合并为一个以放入存储过程以进行报告制作。他们是

查询 1

SELECT T0.Name AS Period, SUM(ISNULL(T2.LineTotal, 0)) AS CurrentDebtors, MAX(T1.DocRate) AS ExchangeRate,
SUM(CASE WHEN DATEDIFF(day, T1.DocDate, T1.DocDueDate) > 30
THEN T2.LineTotal END) AS NonCurrentDebtors 
FROM OFPR T0 LEFT OUTER JOIN OINV T1 ON T0.AbsEntry = T1.FinncPriod 
INNER JOIN INV1 T2 ON T1.DocEntry = T2.DocEntry WHERE YEAR(T1.DocDate) = @Year
GROUP BY T0.Name 
ORDER BY T0.Name 

查询 2

SELECT T0.Name AS Period, SUM(T1.DocTotal) AS TurnoverMonth
FROM dbo.OFPR T0 LEFT OUTER JOIN dbo.ORCT T1 ON T0.AbsEntry = T1.FinncPriod
WHERE YEAR(T1.DocDate) =  @Year
GROUP BY T0.Name
ORDER BY T0.Name

我将它们组合成这样:

SELECT T0.Name AS Period, SUM(ISNULL(T2.LineTotal, 0)) AS CurrentDebtors, MAX(T1.DocRate) AS ExchangeRate,
SUM(CASE WHEN DATEDIFF(day, T1.DocDate, T1.DocDueDate) > 30
THEN T2.LineTotal END) AS NonCurrentDebtors , SUM(ISNULL(T3.DocTotal, 0)) AS TurnoverMonth
FROM OFPR T0 LEFT OUTER JOIN OINV T1 ON T0.AbsEntry = T1.FinncPriod 
INNER JOIN INV1 T2 ON T1.DocEntry = T2.DocEntry 
JOIN ORCT T3 ON T0.AbsEntry = T3.FinncPriod
WHERE YEAR(T1.DocDate) = @Year
GROUP BY T0.Name 
ORDER BY T0.Name 

问题是,虽然上面单独的 2 个查询的结果是正确的,但上面的组合查询返回的值不正确,数量非常大。如何正确地将两者结合起来? Second:如果数据非常大,查询1也需要一些时间来执行,有什么方法可以提高效率吗?使用 Microsoft SQL Server 2008

4

1 回答 1

2

由于连接的性质,如果您尝试同时聚合两个或多个表,则必然会得到重复的聚合。通过 TO 连接时,T2 和 T3 之间可能存在 n:m 关系。要解决这个问题,您可以使用 cte 或派生表 - 此重写使用 T3 的派生表来准确检索每个 TO.AbsEntry 的一行,从而消除重复。

SELECT T0.Name AS Period, 
       SUM(T2.LineTotal) AS CurrentDebtors, 
       MAX(T1.DocRate) AS ExchangeRate,
       SUM(CASE WHEN DATEDIFF(day, T1.DocDate, T1.DocDueDate) > 30
                THEN T2.LineTotal 
           END) AS NonCurrentDebtors, 
       T3.TurnoverMonth
  FROM OFPR T0 
 INNER JOIN OINV T1
    ON T0.AbsEntry = T1.FinncPriod 
 INNER JOIN INV1 T2 
    ON T1.DocEntry = T2.DocEntry
 INNER JOIN 
 (
    SELECT ORCT.FinncPriod, 
           SUM(ORCT.DocTotal) AS TurnoverMonth
      FROM ORCT
     WHERE YEAR(ORCT.DocDate) = @Year
     GROUP BY ORCT.FinncPriod
 )  T3 
    ON T0.AbsEntry = T3.FinncPriod
 WHERE YEAR(T1.DocDate) = @Year
 GROUP BY T0.Name, T3.TurnoverMonth
 ORDER BY T0.Name

另一种可能是您需要按年份过滤 ORCT 表,这在组合查询中被省略了。出于性能原因,您可能会考虑将该过滤器扩展到日期范围测试,以允许 Sql Server 在 DocPeriod 上使用索引:

where DocDate >= convert(datetime, convert(varchar(20), @year) + '0101')
  and DocDate < dateadd (year, 1, 
                         convert(datetime, convert(varchar(20), @year) + '0101'))

请注意,我已将 TurnoverMonth 添加到分组依据。这不会改变任何东西,因为每个 T3 将精确一行。

我还从 sum 中删除了 isnull() 测试,因为无论如何都会从 sum() 中删除 null 值。如果您想用零替换最终的空结果,请在 isnull() 中包含 sum()。

编辑:忘了提到我已将左连接更改为内连接,因为 T1 上的条件将连接的含义更改为内连接(除了为空/不为空之外,缺失的行不能被任何匹配)。如果您确实需要外连接,请将条件移至 ON 子句。

于 2012-08-17T10:49:26.800 回答