Query never finishes - sql-server

So I have a query for a report that never finishes. I've allowed it to run for upwards of 20+ mins without completing. Here is the query:
DECLARE #start DATE, #end DATE
SET #start = '7-1-2019'
SET #end = '8-20-2019'
SELECT cg.*
,ba.std_StandardAcctNo AS bonus_acct
FROM [CustSalesTrend_Growth] cg
LEFT JOIN (
SELECT DISTINCT std_standardacctno
FROM [CustSalesTrend_Growth]
WHERE eleph_Period_Date BETWEEN #Start
AND #End
OR ideal_Period_Date BETWEEN #Start
AND #End
) ba ON ba.std_StandardAcctNo = cg.std_StandardAcctNo
AND cg.Period_Date >= #Start
AND cg.Period_Date <= #End
WHERE [Appearance_Count] <> 0
AND Period_gp <> 0
ORDER BY cg.std_StandardAcctNo
One of my first steps in diagnosing this is doing a simple select * on the view being referenced (CustSalesTrend_Growth) and it will finish running in about 30 seconds on average, pulling 12k records. Given this, I'm a little perplexed as to how the preceding query could add so much complexity to the execution, to the point that it never finishes. In my mind, the query above is relatively simple, so any ideas as to why this is happening?
EDIT: query for the view CustSalesTrend_Growth
CREATE VIEW [dbo].[CustSalesTrend_Growth]
AS
WITH basedata
AS (
SELECT DISTINCT a.order_num
,a.Period_Date
,a.year AS std_the_year
,a.Month AS std_the_month
,a.customer_alias AS std_StandardAcctNo
,b.SalesPerson_name
,isnull(ac.counter, 0) [Appearance_Count]
,isnull(b.[Year], 0) [The_Year]
,isnull(b.[Month], 0) [The_Month]
,isnull(b.Customer_Alias, 0) [CustomerName]
,sum(isnull(b.Gallons, 0)) [Gallon_Qty]
,sum(isnull(b.[Total Sale], 0)) [Total_Sale]
,sum(isnull(b.[Gross Profit], 0)) [Total_Gross_Profit]
FROM (
SELECT DISTINCT a.year
,a.month
,b.Customer_Alias
,convert(INTEGER, convert(VARCHAR(4), a.Year) + right('00' + convert(VARCHAR(2), a.month), 2)) AS order_num
,convert(DATE, convert(VARCHAR(2), a.Month) + '/01/' + convert(VARCHAR(4), a.Year)) AS Period_Date
FROM Complete_Sales_V2 a
JOIN (
SELECT DISTINCT Customer_Alias
FROM Complete_Sales_V2
) b ON 1 = 1
) a
JOIN PDI_Warehouse_2049_01.dbo.Appearance_Count ac ON a.Customer_Alias = ac.customer_alias
LEFT JOIN Complete_Sales_V2 b ON a.Customer_Alias = b.Customer_Alias
AND a.Month = b.Month
AND a.Year = b.Year
GROUP BY a.order_num
,a.Period_Date
,a.year
,a.Month
,a.Customer_Alias
,b.SalesPerson_Name
,ac.counter
,b.[Year]
,b.[Month]
,b.Customer_Alias
)
,saleslist
AS (
SELECT DISTINCT SalesPerson_Name
,Appearance_Count
,Period_Date
,std_the_month
,std_the_year
,std_StandardAcctNo
,isnull(sum(Total_Gross_Profit), 0) Period_GP
FROM basedata
GROUP BY SalesPerson_Name
,Appearance_Count
,Period_Date
,std_StandardAcctNo
,std_the_month
,std_the_year
)
,core_GP
AS (
SELECT DISTINCT a.Customer_Alias
,convert(DATE, convert(VARCHAR(2), a.month) + '/01/' + convert(VARCHAR(4), a.year)) AS Period_Date
,sum(a.[Gross Profit]) AS Period_GP
FROM Complete_Sales_V2 a
JOIN PDI_Warehouse_2049_01.dbo.appearance_count ac ON ac.customer_alias = a.Customer_Alias
GROUP BY counter
,convert(DATE, convert(VARCHAR(2), a.month) + '/01/' + convert(VARCHAR(4), a.year))
,a.Customer_Alias
)
,GroupedData
AS (
SELECT DISTINCT cgp.std_StandardAcctNo
,cgp.Period_Date
,sum(cgp.[Total_Gross_Profit]) AS Period_GP
,Appearance_Count
FROM basedata cgp
GROUP BY cgp.std_StandardAcctNo
,cgp.Period_Date
,Appearance_Count
)
,GP_Grouping
AS (
SELECT std_StandardAcctNo
,min(Period_Date) AS range_start
,max(Period_Date) AS range_end
,count(*) AS range_count
,GP_group
FROM (
SELECT std_StandardAcctNo
,Period_Date
,CASE
WHEN Period_GP = 0
THEN 0
ELSE 1
END AS GP_Group
,row_number() OVER (
PARTITION BY std_StandardAcctNo
,CASE
WHEN Period_GP = 0
THEN 0
ELSE 1
END ORDER BY Period_Date
) AS rn
,row_number() OVER (
PARTITION BY std_StandardAcctNo
,CASE
WHEN Period_GP = 0
THEN 0
ELSE 1
END ORDER BY Period_Date
) - row_number() OVER (
PARTITION BY std_StandardAcctNo ORDER BY Period_Date
) AS grp
,row_number() OVER (
PARTITION BY std_StandardAcctNo ORDER BY Period_Date
) AS grp2
FROM GroupedData
) a
GROUP BY std_StandardAcctNo
,grp
,GP_Group
)
,GP_Group2
AS (
SELECT gd.*
,max(gpg_prev.range_end) AS last_zero_group -- , gpg.range_count
FROM GroupedData gd
LEFT JOIN GP_Grouping gpg ON gd.std_StandardAcctNo = gpg.std_StandardAcctNo
AND gd.Period_Date BETWEEN gpg.range_start
AND gpg.range_end
LEFT JOIN (
SELECT *
FROM GP_Grouping
WHERE GP_Group = 0
AND range_count >= 12
) gpg_prev ON gpg_prev.std_StandardAcctNo = gd.std_StandardAcctNo
AND gpg.range_start > gpg_prev.range_end
GROUP BY gd.std_StandardAcctNo
,Period_Date
,Period_GP
,Appearance_Count
,gpg.range_count
)
,GP_Group3
AS (
SELECT gd.*
,Appearance_Cnt_Rel = CASE
WHEN gd.last_zero_group IS NULL
THEN Appearance_Count
ELSE ROW_NUMBER() OVER (
PARTITION BY gd.std_StandardAcctNo
,gd.last_zero_group ORDER BY gd.Period_Date
)
END
FROM GP_Group2 gd
)
,almost_done
AS (
SELECT DISTINCT bd.order_num
,bd.Period_Date
,bd.std_the_year
,bd.std_the_month
,bd.std_StandardAcctNo
,CASE
WHEN bd.[Appearance_Count] > 0
THEN bd.[Appearance_Count]
WHEN isnull(c.Appearance_Count, 0) > 0
THEN c.Appearance_Count + 1
WHEN isnull(d.Appearance_Count, 0) > 0
THEN d.Appearance_Count + 2
WHEN isnull(e.Appearance_Count, 0) > 0
THEN e.Appearance_Count + 3
ELSE 0
END AS Appearance_Count
,bd.[The_Year]
,bd.[The_Month]
,bd.[CustomerName]
,bd.[Gallon_Qty]
,bd.[Total_Sale]
,isnull(c.Appearance_Count, 0) AS Prev_Count
,isnull(d.Appearance_Count, 0) AS month2_Count
,isnull(e.Appearance_Count, 0) AS month3_Count
,CASE
WHEN bd.SalesPerson_Name IS NOT NULL
THEN bd.SalesPerson_Name
WHEN c.SalesPerson_Name IS NOT NULL
THEN c.SalesPerson_Name
WHEN d.SalesPerson_Name IS NOT NULL
THEN d.SalesPerson_Name
WHEN e.SalesPerson_Name IS NOT NULL
THEN e.SalesPerson_Name
ELSE 'NA'
END [SalesPerson]
,CASE
WHEN bd.[Appearance_Count] IS NULL
AND c.[Appearance_Count] IS NULL
AND d.[Appearance_Count] IS NULL
THEN e.Period_GP
ELSE 0
END [Lost_Gross_Profit]
,CASE
WHEN bd.Appearance_Count = 1
THEN bd.Total_Gross_Profit
ELSE 0
END AS 'New_Cust_GP'
,CASE
WHEN bd.Appearance_Count <= 12
THEN bd.Total_Gross_Profit
ELSE 0
END AS 'Young_Cust_GP'
,CASE
WHEN bd.Appearance_Count > 12
THEN bd.Total_Gross_Profit
ELSE 0
END AS 'Old_Cust_GP'
,ROW_NUMBER() OVER (
PARTITION BY bd.std_StandardAcctNo
,bd.std_The_Year
,bd.std_The_Month ORDER BY (bd.std_StandardAcctNo) DESC
) AS UNI_Period
,bd.Total_Gross_Profit AS SalesP_GP
,isnull(cg.Period_gp, 0) AS Period_gp
,CASE
WHEN isnull(b_prev.Period_gp, 0) > 0
THEN isnull(b_prev.Period_gp, 0)
WHEN isnull(d.Period_gp, 0) > 0
THEN isnull(d.Period_gp, 0)
WHEN isnull(e.Period_gp, 0) > 0
THEN isnull(e.Period_gp, 0)
ELSE 0
END AS Prev_Period_GP
,h.Mat_MoM_Shift
,CASE
WHEN isnull(b_prev.Period_gp, 0) > 0
THEN isnull(b_prev.Period_gp, 0)
WHEN isnull(d.Period_gp, 0) > 0
THEN isnull(d.Period_gp, 0)
WHEN isnull(e.Period_gp, 0) > 0
THEN isnull(e.Period_gp, 0)
ELSE 0
END * h.Mat_MoM_Shift AS Expected_GP
,isnull(c.Period_gp, 0) AS True_Prev_GP
,isnull(d.Period_gp, 0) AS True_2month_GP
,isnull(e.Period_gp, 0) AS True_3month_GP
,ideal_candidate = CASE
WHEN ((isnull(c.Period_gp, 0) + isnull(d.Period_gp, 0) + isnull(bd.Total_Gross_Profit, 0)) / 3 >= 800)
AND isnull(c.Period_gp, 0) >= 150
AND isnull(d.Period_gp, 0) >= 150
AND isnull(bd.Total_Gross_Profit, 0) >= 150
THEN 'Y'
ELSE 'N'
END
,eleph_candidate = CASE
WHEN ((isnull(c.Period_gp, 0) + isnull(d.Period_gp, 0) + isnull(bd.Total_Gross_Profit, 0)) / 3 >= 5000)
AND isnull(c.Period_gp, 0) >= 1000
AND isnull(d.Period_gp, 0) >= 1000
AND isnull(bd.Total_Gross_Profit, 0) >= 1000
THEN 'Y'
ELSE 'N
'
END
FROM basedata bd
LEFT JOIN core_GP b_prev ON bd.std_StandardAcctNo = b_prev.Customer_Alias
AND b_prev.Period_Date = dateadd(month, - 1, bd.Period_Date)
LEFT JOIN saleslist c ON c.std_StandardAcctNo = bd.std_StandardAcctNo
AND c.Period_Date = dateadd(month, - 1, bd.Period_Date)
AND CASE
WHEN bd.SalesPerson_Name IS NOT NULL
THEN bd.SalesPerson_Name
ELSE c.SalesPerson_Name
END = c.SalesPerson_Name
LEFT JOIN saleslist d ON d.std_StandardAcctNo = bd.std_StandardAcctNo
AND d.Period_Date = dateadd(month, - 2, bd.Period_Date)
AND CASE
WHEN bd.SalesPerson_Name IS NOT NULL
THEN bd.SalesPerson_Name
WHEN c.SalesPerson_Name IS NOT NULL
THEN c.SalesPerson_Name
ELSE d.SalesPerson_Name
END = d.SalesPerson_Name
LEFT JOIN saleslist e ON e.std_StandardAcctNo = bd.std_StandardAcctNo
AND e.Period_Date = dateadd(month, - 3, bd.Period_Date)
AND CASE
WHEN bd.SalesPerson_Name IS NOT NULL
THEN bd.SalesPerson_Name
WHEN c.SalesPerson_Name IS NOT NULL
THEN c.SalesPerson_Name
WHEN d.SalesPerson_Name IS NOT NULL
THEN d.SalesPerson_Name
ELSE e.SalesPerson_Name
END = e.SalesPerson_Name
LEFT JOIN RicoCustom.dbo.[Rico_Global_Monthly] h ON h.month = bd.std_the_month
LEFT JOIN core_GP cg ON bd.std_StandardAcctNo = cg.Customer_Alias
AND cg.Period_Date = bd.Period_Date
)
,get_ideal
AS (
SELECT DISTINCT min(ad.Period_Date) AS ideal_Period_Date
,ad.std_StandardAcctNo
,rc.last_zero_group
FROM almost_done ad
LEFT JOIN GP_Group3 rc ON rc.Period_Date = ad.Period_Date
AND rc.std_StandardAcctNo = ad.std_StandardAcctNo
AND rc.Period_GP = ad.Period_gp
WHERE ideal_candidate = 'Y'
AND (
rc.Appearance_Cnt_Rel BETWEEN 3
AND 6
)
GROUP BY ad.std_StandardAcctNo
,rc.last_zero_group
)
,get_elephant
AS (
SELECT DISTINCT min(ad.Period_Date) AS eleph_Period_Date
,ad.std_StandardAcctNo
,rc.last_zero_group
FROM almost_done ad
LEFT JOIN GP_Group3 rc ON rc.Period_Date = ad.Period_Date
AND rc.std_StandardAcctNo = ad.std_StandardAcctNo
AND rc.Period_GP = ad.Period_gp
WHERE eleph_candidate = 'Y'
AND (
rc.Appearance_Cnt_Rel BETWEEN 3
AND 36
)
GROUP BY ad.std_StandardAcctNo
,rc.last_zero_group
)
SELECT rc.Appearance_Cnt_Rel
,gi.ideal_Period_Date
,ge.eleph_Period_Date
,ad.*
FROM almost_done ad
LEFT JOIN GP_Group3 rc ON rc.Period_Date = ad.Period_Date
AND rc.std_StandardAcctNo = ad.std_StandardAcctNo
AND rc.Period_GP = ad.Period_gp
LEFT JOIN get_ideal gi ON ad.std_StandardAcctNo = gi.std_StandardAcctNo
AND ad.Period_Date = gi.ideal_Period_Date
LEFT JOIN get_elephant ge ON ad.std_StandardAcctNo = ge.std_StandardAcctNo
AND ad.Period_Date = ge.eleph_Period_Date
WHERE order_num > 201001
The indexing I have exists on the source table, Complete_Sales_V2. It is as follows:
index desc:
clustered, unique, primary key located on PRIMARY
index keys:
Customer_Alias, SalesPerson_Name, year, month, Invoice_Number

Since you say you can query the view I suggest selecting your view into a temp table prior to performing the query. This will simplify the query plan, allowing you to inspect it. And may hopefully speed it up.
I note in your original query you are filtering you main table against #Start and #End in the join condition. I don't think you want to do that. I think it should be in the where clause.
Also you can probably pre-filter the temp table so long as you can work out which records may be required (I couldn't because you compare to 3 different dates).
DECLARE #start DATE = '7-1-2019', #end DATE = '8-20-2019';
-- Best practice is to list the actual columns required
-- You may also be able to pre-filter here based on #start and #end
-- But as you compare them to 3 different columns in the query I don't know enough about your logic to know if this is possible or not
-- You can also add indexes to the temp table if they would speed things up
select *
into #cg
from [CustSalesTrend_Growth];
SELECT cg.*
, ba.std_StandardAcctNo AS bonus_acct
FROM #cg cg
LEFT JOIN (
SELECT DISTINCT std_standardacctno
FROM #cg
WHERE eleph_Period_Date BETWEEN #Start AND #End
OR ideal_Period_Date BETWEEN #Start AND #End
) ba ON ba.std_StandardAcctNo = cg.std_StandardAcctNo
WHERE [Appearance_Count] <> 0
AND Period_gp <> 0
AND cg.Period_Date >= #Start
AND cg.Period_Date <= #End
ORDER BY cg.std_StandardAcctNo;
drop table #cg;

Try this query that uses CTEs. I do not have the schema so some of my assumptions may not be correct. Please verify the query before you execute.
WITH t1 AS
(
SELECT *
FROM CustSalesTrend_Growth
WHERE Appearance_Count <> 0
AND Period_gp <> 0
AND Period_Date between #Start and #End
),
t2 AS
(
SELECT DISTINCT std_StandardAcctNo
FROM CustSalesTrend_Growth
WHERE eleph_Period_Date BETWEEN #Start AND #End
OR ideal_Period_Date BETWEEN #Start AND #End
)
select t1.*, t2.std_StandardAcctNo AS bonus_acct
FROM t1
LEFT JOIN t2 ON t2.std_StandardAcctNo = t1.std_StandardAcctNo
ORDER BY t2.std_StandardAcctNo

Related

How do i escape aliases in Dynamic sql

I am getting an error near Alias Names i.e., Month_Name, Limit_Mins, Amount in this dynamic sql, how do i resolve that , i tried with adding one more quote, but when i do tyhat i am only getting #columnames into the sql. How do i resolve the error and make it part of the string
DECLARE #columns NVARCHAR(MAX) = '', #sql NVARCHAR(MAX) = '';
SELECT
#columns = coalesce(#columns + ',', '') + quotename(Limit_Mins)
from
(
select DISTINCT
(L.Limit_Mins ) as 'Limit_Mins'
from
Limit L
)
AS lIMITS
SELECT #columns
SET
#sql = 'select * from (
SELECT
month (Sa.[Date]) AS 'Month_Name',
convert(varchar(10),
case
when
S.Limit_Mins = L.Limit_Mins
and S.Childcare_Flag = 1
then
S.Limit_Mins
else
0
end
) as 'Limit_Mins', sum(
case
when
S.Childcare_Flag = 1
and S.Limit_Mins = L.Limit_Mins
then
case
when
D.PID = Sa.PID
and D.Discount_Date = Sa.[Date]
then
D.Discount_Price
else
P.Retail_price
end
*Sa.Quantity
else
0
end
) as 'Amount'
FROM
Limit L
join
Store S
on L.Limit_Mins = S.Limit_Mins
join
Sale Sa
on S.Store_Number = Sa.Store_number
join
[Date] Dt
on Dt.[Date] = Sa.[Date]
join
Product P
on Sa.PID = P.PID
left outer join
Discount D
on Sa.PID = D.PID
and Sa.[Date] = D.Discount_Date
WHERE
Sa.[Date] >= DATEADD(year, - 1, DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1))
AND Sa.[Date] < DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1) -- DATEDIFF(MM,Sale.[Date] ,GETDATE())<=12
--and Sale.[Date] < DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
GROUP BY
month(Sa.[Date]) ,
case
when
S.Limit_Mins = L.Limit_Mins
and S.Childcare_Flag = 1
then
S.Limit_Mins
else
0
end
union all
SELECT
month (Sa.[Date]) AS 'Month_Name',
''No Childcare'' as 'Limit_Mins',
isnull(sum(
case
when
S.Childcare_Flag = 0
then
case
when
D.PID = Sa.PID
and D.Discount_Date = Sa.[Date]
then
D.Discount_Price
else
P.Retail_price
end
*Sa.Quantity
else
0
end
), 0) as 'Sales_Amount'
FROM
Store S
join
Sale Sa
on S.Store_Number = Sa.Store_number
join
[Date] Dt
on Dt.[Date] = Sa.[Date]
join
Product P
on Sa.PID = P.PID
left outer join
Discount D
on Sa.PID = D.PID
and Sa.[Date] = D.Discount_Date
WHERE
Sa.[Date] >= DATEADD(year, - 1, DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1))
AND Sa.[Date] < DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1) -- DATEDIFF(MM,Sale.[Date] ,GETDATE())<=12
--and Sale.[Date] < DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
GROUP BY
month(Sa.[Date])
)
A PIVOT(sum(Amount) for Limit_Mins in
(
'+#columns+
',[No Childcare]
)
)as PIVOTTable
order by
[Month]'
print #sql
Use brackets instead of quotes: month (Sa.[Date]) AS [Month_Name],
I tried the quotes earlier, but it was throwing an error invalid column which i later realized was happening because i renamed the alias and forgot to rename the same column order by , Thanks for the input

how to make multi column with multi where condition

i have 3 SQL statement i want to merge them into one but each of them has its own where condition how can i merge them into one query that show the result for each of them to be like
Project Code||Balance||Salaries||Materials
1st
SELECT
ProjectCode,
SUM (- 1 *(CreditAm) +(DebitAm)) AS Balance
FROM
[dbo].[AcDetail]
WHERE
AcCode IN (
401000,
402000,
403000,
404000,
405000,
406000,
407000,
499999
)
AND BranchCode = 'WP'
AND TDate >= '2016-01-01'
AND TDate <= '2016-12-31'
GROUP BY
ProjectCode
2nd
SELECT
ProjectCode,
SUM (- 1 *(CreditAm) +(DebitAm)) AS Materials
FROM
[dbo].[AcDetail]
WHERE
AcCode IN (600001, 600002, 600151)
AND BranchCode = 'WP'
AND TDate >= '2016-01-01'
AND TDate <= '2016-12-31'
GROUP BY
ProjectCode
3rd
SELECT
ProjectCode,
SUM (- 1 *(CreditAm) +(DebitAm)) AS Salaries
FROM
[dbo].[AcDetail]
WHERE
AcCode IN (
650001,
650005,
650006,
650007,
650008,
650009,
650010,
650020,
650021,
650022,
650023,
650024,
650025,
650026,
650027
)
AND BranchCode = 'WP'
AND TDate >= '2016-01-01'
AND TDate <= '2016-12-31'
GROUP BY
ProjectCode
ORDER BY
ProjectCode
Use Conditional SUM. Sum the values only when the Accode matches the list
SELECT ProjectCode,
Sum (CASE
WHEN AcCode IN ( '401000', '402000', '403000', '404000',
'405000', '406000', '407000', '499999' ) THEN -1 * ( CreditAm ) + ( DebitAm )
ELSE 0
END) AS Balance,
Sum (CASE
WHEN AcCode IN ( '600001', '600002', '600151' ) THEN -1 * ( CreditAm ) + ( DebitAm )
ELSE 0
END) AS Materials,
Sum (CASE
WHEN AcCode IN ( '650001', '650005', '650006', '650007',
'650008', '650009', '650010', '650020',
'650021', '650022', '650023', '650024',
'650025', '650026', '650027' ) THEN -1 * ( CreditAm ) + ( DebitAm )
ELSE 0
END) AS Salaries
FROM [dbo].[AcDetail]
WHERE AcCode IN ( '401000', '402000', '403000', '404000',
'405000', '406000', '407000', '499999',
'600001', '600002', '600151', '650001',
'650005', '650006', '650007', '650008',
'650009', '650010', '650020', '650021',
'650022', '650023', '650024', '650025',
'650026', '650027' )
GROUP BY ProjectCode
We can done it using OUTER APPLY as well by making different set for each column as below:
SELECT D.ProjectCode,
ISNULL(SUM(B.Balance), 0) AS Balance,
ISNULL(SUM(M.Materials), 0) AS Materials,
ISNULL(SUM(S.Salaries), 0) AS Salaries
FROM [dbo].[AcDetail] AS D
OUTER APPLY (SELECT (- 1 *(CreditAm) +(DebitAm)) AS Balance
FROM [dbo].[AcDetail]
WHERE AcCode IN ('401000', '402000', '403000', '404000',
'405000', '406000', '407000', '499999')
AND AcCode = D.AcCode
AND TDate = D.TDate
AND ProjectCode = D.ProjectCode) AS B --Balance
OUTER APPLY (SELECT (- 1 *(CreditAm) +(DebitAm)) AS Materials
FROM [dbo].[AcDetail]
WHERE AcCode IN ('600001', '600002', '600151')
AND AcCode = D.AcCode
AND TDate = D.TDate
AND ProjectCode = D.ProjectCode) AS M -- Materials
OUTER APPLY (SELECT (- 1 *(CreditAm) +(DebitAm)) AS Salaries
FROM [dbo].[AcDetail]
WHERE AcCode IN ('650001', '650005', '650006', '650007', '650008', '650009',
'650010', '650020','650021', '650022', '650023', '650024',
'650025', '650026', '650027')
AND AcCode = D.AcCode
AND TDate = D.TDate
AND ProjectCode = D.ProjectCode) AS S --Salary
WHERE D.BranchCode = 'WP'
AND D.AcCode IN ('401000', '402000', '403000', '404000', '405000', '406000', '407000',
'499999','600001', '600002', '600151', '650001', '650005', '650006', '650007',
'650008','650009', '650010', '650020', '650021', '650022', '650023', '650024',
'650025','650026', '650027')
AND D.TDate >= '2016-01-01'
AND D.TDate <= '2016-12-31'
GROUP BY D.ProjectCode
ORDER BY D.ProjectCode

error with an aggregate function but parses fine

I'm having trouble with this query
CREATE VIEW qryMonthlyFeesToCollectSummary1
AS
SELECT ContractID,
Round(Sum([CreditAmount] * ( ( 1
+ COALESCE((SELECT Max(MonthlyRate)
FROM tblCases
WHERE LEFT(CaseID, 4) = [ContractID] AND CaseStatus = 'Open Case'),
0.0199) ) / 30 ) * Exp(30 - 1)), 2) AS InterestCredit
FROM tblCredits
GROUP BY tblCredits.ContractID,
(SELECT Count(*)
FROM tblInterestPayments
WHERE ContractMainID = [ContractID]
AND ( PaymentDate IS NULL
OR ( PaymentDate IS NOT NULL
AND Bounced <> 0
AND RetrySuccessful IS NULL ) ))
HAVING (SELECT Count(*)
FROM tblInterestPayments
WHERE ContractMainID = [ContractID]
AND ( PaymentDate IS NULL
OR ( PaymentDate IS NOT NULL
AND Bounced <> 0
AND RetrySuccessful IS NULL ) )) = 0;
it errors out as shown below even though it parses fine. Please help! Thanks so much!
Msg 144, Level 15, State 1, Procedure qryMonthlyFeesToCollectSummary1,
Line 11 Cannot use an aggregate or a subquery in an expression used
for the group by list of a GROUP BY clause.
Msg 130, Level 15, State 1, Procedure qryMonthlyFeesToCollectSummary1,
Line 5 Cannot perform an aggregate function on an expression
containing an aggregate or a subquery.
I think this is what you are looking for:
CREATE VIEW qryMonthlyFeesToCollectSummary1
AS
SELECT ContractID,
Round(Sum([CreditAmount] * ( ( 1
+ COALESCE((SELECT Max(MonthlyRate)
FROM tblCases
WHERE LEFT(CaseID, 4) = [ContractID] AND CaseStatus = 'Open Case'),
0.0199) ) / 30 ) * Exp(30 - 1)), 2) AS InterestCredit
FROM tblCredits
WHERE (SELECT Count(*)
FROM tblInterestPayments
WHERE ContractMainID = tblCredits.[ContractID]
AND ( PaymentDate IS NULL
OR ( PaymentDate IS NOT NULL
AND Bounced <> 0
AND RetrySuccessful IS NULL ) )) = 0
GROUP BY tblCredits.ContractID;
I think I have got it right. Try to properly alias the table names and each column used in the query.
CREATE VIEW qryMonthlyFeesToCollectSummary1
AS
SELECT tblCredits.ContractID
, Round(Sum([CreditAmount] * ( ( 1
+ COALESCE(Max(MonthlyRate) , 0.0199) ) / 30 )
* Exp(30 - 1)), 2) AS InterestCredit
FROM tblCredits
INNER JOIN tblCases ON LEFT(CaseID, 4) = [ContractID]
AND CaseStatus = 'Open Case'
INNER JOIN tblInterestPayments ON ContractMainID = [ContractID]
WHERE PaymentDate IS NULL
OR ( PaymentDate IS NOT NULL
AND Bounced <> 0
AND RetrySuccessful IS NULL )
GROUP BY tblCredits.ContractID
HAVING Count(*) = 0;
You may try the following:
SELECT
Credits.ContractID,
ROUND(SUM(Credits.CreditAmount * ( ( 1 + COALESCE(Cases.MaxMonthlyRate,0.0199) ) / 30 ) * Exp(30 - 1)), 2) AS InterestCredit
FROM
tblCredits Credits
OUTER APPLY(
SELECT
Max(Cases.MonthlyRate) MaxMonthlyRate
FROM
tblCases Cases
WHERE
LEFT(Cases.CaseID, 4) = Credits.ContractID
AND Cases.CaseStatus = 'Open Case'
)Cases
WHERE
NOT EXISTS(
SELECT NULL
FROM tblInterestPayments Payments
WHERE
Payments.ContractMainID = Credits.ContractID
AND (
Payments.PaymentDate IS NULL
OR (
Payments.PaymentDate IS NOT NULL
AND Payments.Bounced <> 0
AND Payments.RetrySuccessful IS NULL
)
)
)
GROUP BY
Credits.ContractID
The errors you are experienced are encountered when you try to use an aggregate function (MAX, MIN, AVG, etc.) inside a subquery that is inside an expression that is already using an aggregate function. The remove them, I moved that subquery out of the select clause and into an outer apply. I used a Outer apply because you tagged the questions as SQL server and the subquery is performing an aggregate. In other databases you would use a left join.
Also, I moved the tblInterestPayments subquery into a NOT EXISTS clause. Since you are checking for the non existence of rows in the table matching a criteria, this makes more sense.
try something like:
CREATE VIEW qryMonthlyFeesToCollectSummary1
AS
SELECT
ContractID,
ROUND(SUM([CreditAmount] * ( ( 1 + COALESCE(max_monthlyRate, 0.0199) ) / 30 ) *
EXP(30 - 1)), 2) AS InterestCredit
FROM
tblCredits c LEFT OUTER JOIN
(SELECT
ContractMainID
FROM
tblInterestPayments
WHERE
PaymentDate IS NULL OR
( Bounced <> 0 AND RetrySuccessful IS NULL )) i ON
c.ContractID = i.ContractMainID LEFT OUTER JOIN
(SELECT
LEFT(CaseID, 4) AS left_caseID4,
MAX(MonthlyRate) AS max_monthlyRate
FROM
tblCases
WHERE
CaseStatus = 'Open Case'
GROUP BY
LEFT(caseid,4)) s ON
c.contractID = s.left_caseID4
WHERE
i.ContractMainID IS NULL
GROUP BY
ContractID

SQL select sum/average all rows

i have this code who picks rows from table, but i need to make him return sum of all rows or avg of all rows. I tried to put code in other select like select within sum(select), but its not working, any ideas how to make it work? Sorry for not much info. Right now it prints 25 rows. I need to make it return 1 Row that would be like sum(ontime) and avg(actual). I mean sum all 25 rows of ontime column and avg actual column.
SELECT AVG(CASE WHEN a.ontime = 'on time' THEN 100 ELSE 0 END)*COUNT(a.country) AS ontime
,AVG(CASE WHEN a.accuracy = 'accurate' THEN 100 ELSE 0 END) AS actual
,COUNT(a.country) AS task_count
FROM
bstplanning.dbo.sc_data a
INNER JOIN bstplanning.dbo.users
ON a.user_id = bstplanning.dbo.users.user_id
WHERE
a.date >= '2015-04-01 00:00:00.000' AND a.date <= '2015-04-30 00:00:00.000' AND a.actual > 0 AND
((a.ontime='on time' OR a.ontime = 'late') OR (a.accuracy='accurate' OR a.accuracy = 'error'))
and bstplanning.dbo.users.split='Rep'
and bstplanning.dbo.users.user_surname='user'
GROUP BY
a.country
,a.client
,a.task_group
,frequency
,a.track
you can write like outer query and your query as inner query ..
SELECT Sum(Ontime) AS Ontime
,AVG(actual) AS Actual
,Sum(task_count) AS task_count
FROM (
SELECT AVG(CASE
WHEN a.ontime = 'on time'
THEN 100
ELSE 0
END) * COUNT(a.country) AS ontime
,AVG(CASE
WHEN a.accuracy = 'accurate'
THEN 100
ELSE 0
END) AS actual
,COUNT(a.country) AS task_count
FROM bstplanning.dbo.sc_data a
INNER JOIN bstplanning.dbo.users ON a.user_id = bstplanning.dbo.users.user_id
WHERE a.DATE >= '2015-04-01 00:00:00.000'
AND a.DATE <= '2015-04-30 00:00:00.000'
AND a.actual > 0
AND (
(
a.ontime = 'on time'
OR a.ontime = 'late'
)
OR (
a.accuracy = 'accurate'
OR a.accuracy = 'error'
)
)
AND bstplanning.dbo.users.split = 'Rep'
AND bstplanning.dbo.users.user_surname = 'user'
GROUP BY a.country
,a.client
,a.task_group
,frequency
,a.track
) Drive

Take the first line of the every partition

I have this query :
select ROW_NUMBER() OVER ( PARTITION BY codtiers ORDER BY case when isnull(cumulapport, 0) > isnull(cumulretrait, 0) then cumulapport
else cumulretrait
end DESC ) AS classement,
codTiers
from dbo.TiersComptesLocal
group by codTiers
which return this result :
classement | codTiers
-------------------------
1 | 1XXXXX
2 | 1XXXXX
1 | 1YYXXX
2 | 1YYXXX
3 | 1YYXXX
1 | 1XXXYY
I want to display the first line from every partition
I want to have a result like this :
classement | codTiers
-------------------------
1 | 1XXXXX
1 | 1YYXXX
1 | 1XXXYY
Any idea will be appreciate.
this is my full query :
select ROW_NUMBER() OVER ( PARTITION BY tcl_codtiers ORDER BY case when isnull(cumulapport, 0) > isnull(cumulretrait, 0) then cumulapport
else cumulretrait
end DESC ) AS classement,
TCL_CodTiers,
'le montant max des ' + Senscumul + ' ( '
+ convert(varchar(50), '') + ' € ) du tiers titulaire '
+ TCL_CodTiers + ' ( ' + ''
+ ' ) est supérieur au seuil fixé dans l''alerte : '
+ '75000'
from dbo.CLI_TCL_TiersComptesLocal
INNER JOIN dbo.CLI_GCO_GeneriquesComptes ON TCL_CodTiers = GCO_CodTiersPrincipal
AND TCL_NumLien IN ( 0, 1 )
inner join ( select TCL_CodTiers CodTiers,
sum(case when ESO_MntValoRetenuEnEuros > 0
then ESO_MntValoRetenuEnEuros
else 0
end) cumulapport,
sum(case when ESO_MntValoRetenuEnEuros < 0
then abs(ESO_MntValoRetenuEnEuros)
else 0
end) cumulretrait,
case when isnull( sum(case when ESO_MntValoRetenuEnEuros > 0
then ESO_MntValoRetenuEnEuros
else 0
end), 0) > isnull( sum(case when ESO_MntValoRetenuEnEuros < 0
then abs(ESO_MntValoRetenuEnEuros)
else 0
end), 0)
then 'Apports'
else 'Retraits'
end as Senscumul
from CRO_ESO_EntreeSortie
inner join CLI_TCL_TiersComptesLocal on TCL_CodCompte = ESO_CodCompte
and TCL_NumLien in ( 0, 1 )
group by TCL_CodTiers
having sum(case when ESO_MntValoRetenuEnEuros > 0
then ESO_MntValoRetenuEnEuros
else 0
end) > 75000
or sum(case when ESO_MntValoRetenuEnEuros < 0
then abs(ESO_MntValoRetenuEnEuros)
else 0
end) > 75000
) surveillance on GCO_CodTiersPrincipal = CodTiers
Thanks in advance.
No idea why you don't want to use a CTE, but you can use a derived table if you want, though it still is basically the same answer that Mahmoud already posted:
SELECT *
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY codtiers
ORDER BY CASE WHEN ISNULL(cumulapport, 0)>ISNULL(cumulretrait, 0)
THEN cumulapport ELSE cumulretrait END DESC) AS classement,
codTiers
FROM dbo.TiersComptesLocal
GROUP BY codTiers) A
WHERE classement = 1
Here is a solution without using WITH - though I'm not sure why that is a problem.
SELECT classement, codtiers
FROM
(
SELECT
classement = ROW_NUMBER() OVER (PARTITION BY codtiers
ORDER BY CASE WHEN ISNULL(cumulapport, 0) > ISNULL(cumulretrait, 0)
THEN cumulapport ELSE cumulretrait END DESC),
codTiers
FROM dbo.TiersComptesLocal
) AS x
WHERE classement = 1;
WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY codtiers
ORDER BY CASE
WHEN ISNULL(cumulapport, 0) >
ISNULL(cumulretrait, 0) THEN cumulapport
ELSE cumulretrait
END DESC) AS classement,
codTiers
FROM dbo.TiersComptesLocal
)
SELECT *
FROM CTE
WHERE classement = 1;
Update
You can't filter on that column classement in the same query, you have to use a CTE or a subquery instead like in #AaronBertrand answer. However, if you want to do this in the same query, you have to do this without ROW_NUMBER():
SELECT
codtiers,
MAX(CASE
WHEN ISNULL(cumulapport, 0) >
ISNULL(cumulretrait, 0) THEN cumulapport
ELSE cumulretrait
END DESC) AS classement
FROM dbo.TiersComptesLocal
GROUP BY codtiers;

Resources