0

2つの質問があります。 First:2つのクエリを1つに組み合わせて、レポート作成用のストアドプロシージャを配置しようとしています。彼らです

クエリ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つの個別のクエリの結果は正しいものの、上記の結合されたクエリは非常に大量の誤った値を返すことです。2つを適切に組み合わせるにはどうすればよいですか? Second:クエリ1は、非常に大きなデータがある場合にも実行に時間がかかりますが、その効率を向上させる方法はありますか?Microsoft SQLServer2008の使用

4

1 に答える 1

2

結合の性質上、2つ以上のテーブルを同時に集計しようとすると、重複した集計が発生する可能性があります。TOを介して接続した場合、T2とT3の間にはおそらくn:mの関係があります。これを解決するには、cteまたは派生テーブルを使用します。この書き換えでは、T3の派生テーブルを使用して、TO.AbsEntryごとに正確に1つの行を取得し、重複を排除します。

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

もう1つの可能性は、ORCTテーブルを年でフィルタリングする必要があることです。これは結合クエリでは省略されます。パフォーマンス上の理由から、そのフィルターを日付範囲テストに拡張して、SQLServerがDocPeriodでインデックスを使用できるようにすることを検討してください。

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

groupbyにTurnoverMonthを追加したことに注意してください。T3ごとに1行が存在するため、これによって何も変更されません。

とにかくsum()からnull値が削除されるため、sumからisnull()テストも削除しました。最終的なnullの結果をゼロに置き換えたい場合は、isnull()のエンベロープsum()を使用します。

編集:T1の条件によって内部結合への結合の意味が変わるため、左結合を内部結合に変更したことを忘れました(欠落している行はnull / nullでない場合を除いて一致できません)。実際に外部結合が必要な場合は、条件をON句に移動します。

于 2012-08-17T10:49:26.800 に答える