Reputation: 1936
I have two questions.
First:
I am trying to combine two queries into one to put in a stored procedure for report making. They are
Query 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
Query 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
I have combined them into this:
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
The problem is that while the results of the separate 2 queries above are correct, the combined query above returns incorrect values which are very large in amount. How do I combine the two properly?
Second:
Query 1 also takes some time to execute if there is very large data, any way to improve its efficiency? Using Microsoft SQL Server 2008
Upvotes: 0
Views: 513
Reputation: 19356
Due to the nature of joins you are bound to get duplicated aggregations if you try to aggregate two or more tables simultaneously. There is probably n:m relation between T2 and T3 when connected through TO. To solve this you might use cte or derived tables - this rewrite uses derived table for T3 to retrieve one row exactly per TO.AbsEntry, thus removing duplication.
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
Another possibility is that you need to filter ORCT table by year, which is omitted in combined query. For performance reasons you might consider expanding that filter to date-range test, to allow Sql Server to use index on DocPeriod:
where DocDate >= convert(datetime, convert(varchar(20), @year) + '0101')
and DocDate < dateadd (year, 1,
convert(datetime, convert(varchar(20), @year) + '0101'))
Note I've added TurnoverMonth to group by. This does not change anything because there will be exaclty one row per T3.
I've also removed isnull() test from sum, because null values are removed from sum() anyway. If you want to replace final null results with zeros, envelope sum() in isnull().
EDIT: forgot to mention that I've changed left join to inner join because the condition on T1 alters the meaning of the join to inner join (missing rows cannot be matched by anything except is null/is not null). If you actually need outer join, move condition to ON clause.
Upvotes: 2