How to Optimize this query (stuff part) - sql

How can I Optimize this following query:
SELECT
MONTH('1' + LEFT(Datename(month,visitDate),3) +'00') AS MONTH_NUMBER ,
VisitMonth = LEFT(Datename(month,visitDate),3)
,TotalVisits = Count(v.VisitID)
,TotalVisits_with_StandardReport = Count(CASE WHEN v.ReportStandard NOT IN (0,9) THEN v.ReportStandard END)
,TotalVisits_with_FeedbackReport = Count(CASE WHEN v.DiscrepancyType IN (2,5) THEN v.DiscrepancyStatus END)
,Perc =
CAST(100 - ISNULL(CAST((Count(CASE WHEN v.DiscrepancyType IN (2,5) THEN v.DiscrepancyType END) * 1.0 / Count(CASE WHEN v.ReportStandard NOT IN (0,9) THEN v.ReportStandard END)) * 100 AS FLOAT),0) AS NUMERIC(10,2))
,VisitAssignmentID_with_FeedbackRpt = STUFF (( SELECT ', ' + CAST(v2.AssignmentID AS VARCHAR) from Visits v2
INNER JOIN
Assignments a2 ON a2.AssignmentID = v2.AssignmentID
WHERE
DATENAME(MONTH,v.VisitDate) = DATENAME(MONTH,v2.VisitDate)
AND a2.ClientID IN (22,33) AND v2.DiscrepancyType IN (2,5)
GROUP BY V2.AssignmentID
FOR xml path('')), 1, 2, '')
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (22,33)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
GROUP BY DATENAME(MONTH,v.VisitDate)
ORDER BY MONTH_NUMBER
Result
Second query which i tried to simplify and optimize (but query results are not same)
I have tried to optimize the query and tried to simply the stuff inner query but i am not getting the results i want. It has reduced the total execution time though but results are not same.
WITH ALL_CTE(MONTH_NUMBER,VisitMonth,VisitID,AssignmentID,ReportStandard,DiscrepancyStatus,DiscrepancyType,VisitA ssignmentID_with_FeedbackRpt)
AS
-- Define the CTE query.
(
SELECT
MONTH('1' + LEFT(Datename(month,visitDate),3) +'00') AS MONTH_NUMBER ,
VisitMonth = LEFT(Datename(month,visitDate),3)
,v.VisitID, v.AssignmentID , v.ReportStandard , v.DiscrepancyStatus, v.DiscrepancyType
,VisitAssignmentID_with_FeedbackRpt =
STUFF (( SELECT ', ' + CAST((CASE WHEN DiscrepancyType IN (2,5) THEN v.AssignmentID END) AS VARCHAR)
FOR xml path('')), 1, 2, '')
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (22,33)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
group by v.AssignmentID,visitDate,VisitID,ReportStandard,DiscrepancyStatus,DiscrepancyType
)
-- Define the outer query referencing the CTE name.
SELECT
MONTH_NUMBER
,VisitMonth
,COUNT(VisitID)
,TotalVisits_with_StandardReport = COUNT(CASE WHEN ReportStandard NOT IN (0,9) THEN ReportStandard END)
,TotalVisits_with_FeedbackReport = COUNT(CASE WHEN DiscrepancyType IN (2,5) THEN DiscrepancyStatus END)
,isnull(VisitAssignmentID_with_FeedbackRpt,0)
FROM ALL_CTE
GROUP BY MONTH_NUMBER,VisitMonth,VisitAssignmentID_with_FeedbackRpt
Client Stats

I think the only real optimization you can do here is make sure that you have an index on AssignmentID so that the inner join is quicker. All the other parts are pretty irrelevant, they are just conversions handled in memory.

Related

SUM individual dynamic columns from pivot table -SQL server/SAP B1

I writing a SQL server query for based on an SAP Business One DB, I have a dynamic list of columns that I am using in a PIVOT to create dynamic columns.im currently getting separate data since I pass though the "PrcName" into the group by for the Pivot.
I want to be able to sum each dynamic column in my #TEMP table to SUMing the individual "PrcName" Rows. Something lie this: SUM(' + #Columns + ')
The below query gives me something like this:
Type FatherAccount levels AcctCode AccntntCod AcctName Group1 Group2 Project1 Project2
Expense Cost 11 1000002 400002 Resource 0 0 1000 0
Expense Cost 11 1000002 400002 Resource 0 0 0 2000
But I trying to sum them into this:
Type FatherAccount levels AcctCode AccntntCod AcctName Group1 Group2 Project1 Project2
Expense Cost 11 1000002 400002 Resource 0 0 1000 2000
SQL query:
SELECT * INTO #TEMP FROM(
SELECT Type, FatherAccount, Levels, [AcctCode], [AccntntCod], [AcctName], [Tender], [OverHead], ' + #Columns + '
FROM
(SELECT
CASE WHEN T0.GroupMask IN (''4'') THEN ''Revenue'' ELSE ''Expense'' END AS "Type",
T5.AcctName AS "FatherAccount", ''11'' AS Levels, T0.[AcctCode], T0.[AccntntCod], T0.[AcctName],
CASE WHEN T2.PrcName LIKE ''%Group1%'' THEN SUM(T1.[Debit] - T1.[Credit])END AS Group1",
CASE WHEN T2.PrcName LIKE ''%Group2%'' THEN SUM(T1.[Debit] - T1.[Credit])END AS "Group1",
SUM(T1.[Debit] - T1.[Credit]) AS Balance,
T2.[PrcName] AS PrcName
,ROW_NUMBER() OVER(PARTITION BY T0.AcctName, SUM(T1.[Debit] - T1.[Credit]) ORDER BY T0.AcctCode DESC) rn
FROM OACT T0
LEFT JOIN JDT1 T1 ON T0.[AcctCode] = T1.[Account]
LEFT JOIN OPRC T2 ON T2.PrcCode = T1.ProfitCode
LEFT JOIN OPRC T3 ON T3.PrcCode = T1.OcrCode2
LEFT JOIN OPRC T4 ON T4.PrcCode = T1.OcrCode3
LEFT JOIN OACT T5 ON T5.AcctCode = T0.FatherNum
WHERE T0.GroupMask IN (''4'', ''5'', ''6'', ''7'') AND (T0.Postable = ''Y'')
AND ((T1.RefDate Between ''[%0]'' AND ''[%1]'') OR T0.CurrTotal = 0)
AND (T4.PrcName = ''[%2]'' OR T0.CurrTotal = 0)
--GROUP BY GROUPING SETS ((T0.Levels,T0.[AcctCode],T0.[AccntntCod], T0.[AcctName], T5.AcctName, T2.PrcName, T0.GroupMask),(T5.AcctName))
--GROUP BY T0.Levels,T0.[AcctCode],T0.[AccntntCod], T0.[AcctName], T5.AcctName, T2.PrcName, T0.GroupMask
) as PivotData
PIVOT
(
SUM(Balance)
FOR PrcName IN (' + #Columns + ')
) AS PivotResult
) AS #TEMP
SELECT T0.Type, T0.FatherAccount, T0.levels, T0.AcctCode, T0.AccntntCod, T0.AcctName, SUM(T0.Group1) AS Group1, SUM(T0.Group2) AS Group1, SUM(' + #Columns + ')
FROM #TEMP T0
GROUP BY T0.Type, T0.FatherAccount, T0.levels, T0.AcctCode, T0.AccntntCod, T0.AcctName, ' + #Columns + '
ORDER BY Type, Levels, FatherAccount
Thanks for the help!
Without adding the values from the PrcName column to the pivoted columns, does this query return correct output?
SELECT CASE WHEN T0.GroupMask IN ('4') THEN 'Revenue' ELSE 'Expense' END AS [Type],
T5.AcctName AS FatherAccount,
'11' AS Levels,
T0.[AcctCode],
T0.[AccntntCod],
T0.[AcctName],
T2.[PrcName] AS PrcName,
sum(CASE WHEN T2.PrcName LIKE '%Group1%' THEN (T1.[Debit] - T1.[Credit]) else 0 END) AS Group1,
sum(CASE WHEN T2.PrcName LIKE '%Group2%' THEN (T1.[Debit] - T1.[Credit]) else 0 END) AS Group2,
SUM(T1.[Debit] - T1.[Credit]) AS Balance
FROM OACT T0
LEFT JOIN JDT1 T1 ON T0.[AcctCode] = T1.[Account]
LEFT JOIN OPRC T2 ON T2.PrcCode = T1.ProfitCode
LEFT JOIN OPRC T3 ON T3.PrcCode = T1.OcrCode2
LEFT JOIN OPRC T4 ON T4.PrcCode = T1.OcrCode3
LEFT JOIN OACT T5 ON T5.AcctCode = T0.FatherNum
WHERE T0.GroupMask IN ('4', '5', '6', '7')
AND (T0.Postable = 'Y')
AND ((T1.RefDate Between '[%0]' AND '[%1]') OR T0.CurrTotal = 0)
AND (T4.PrcName = '[%2]' OR T0.CurrTotal = 0)
GROUP BY [Type], T5.AcctName, T0.[AcctCode], T0.[AccntntCod], T0.[AcctName], T2.PrcName;

query won't work after pivot

I'm trying to develop a query and i got something like this
SELECT a.No,
a.Finish,
a.Shift,
a.McCode,
d.NAME,
b.ItemCode,
b.ItemName,
b.Qty,
b.LotNo,
b.Description,
CASE
WHEN b.Description LIKE '%not good%' THEN 'NG'
ELSE 'OK'
END AS OKNG,
c.ItemCode AS matcode,
c.LotNo AS matlot,
CASE
WHEN e.GroupName = 'CONTACT' THEN 'CONTACT'
ELSE 'Coil/Silver/Wire'
END AS GroupName,
( c.Qty / ( a.Qty + a.QtyNg ) ) * b.Qty AS materialused
FROM PPRDDLV a
LEFT JOIN ICMUTTRAN b
ON a.PrdNo = b.TranNo
AND a.No = b.TranId
LEFT JOIN ICMUTTRAN c
ON a.PrdNo = c.TranNo
AND a.Finish = c.DatePost
AND c.TranTypeID = 6
AND c.LotNo <> '0'
LEFT JOIN popr d
ON a.OprCode = d.Code
LEFT JOIN ICITEM e
ON c.ItemId = e.id
WHERE c.qty IS NOT NULL
AND b.ItemCode IS NOT NULL
I got around 49000 records in around 2 seconds, then I wan't to pivot a column that has 2 kind of value, so I turned it to something like this
SELECT no,
finish,
shift,
mccode,
NAME,
itemcode,
itemname,
qty,
lotno,
description,
okng,
matcode,
matlot
FROM (SELECT a.No,
a.Finish,
a.Shift,
a.McCode,
d.NAME,
b.ItemCode,
b.ItemName,
b.Qty,
b.LotNo,
b.Description,
CASE
WHEN b.Description LIKE '%not good%' THEN 'NG'
ELSE 'OK'
END AS OKNG,
c.ItemCode AS matcode,
c.LotNo AS matlot,
CASE
WHEN e.GroupName = 'CONTACT' THEN 'CONTACT'
ELSE 'Coil/Silver/Wire'
END AS GroupName,
( c.Qty / ( a.Qty + a.QtyNg ) ) * b.Qty AS materialused
FROM PPRDDLV a
LEFT JOIN ICMUTTRAN b
ON a.PrdNo = b.TranNo
AND a.No = b.TranId
LEFT JOIN ICMUTTRAN c
ON a.PrdNo = c.TranNo
AND a.Finish = c.DatePost
AND c.TranTypeID = 6
AND c.LotNo <> '0'
LEFT JOIN popr d
ON a.OprCode = d.Code
LEFT JOIN ICITEM e
ON c.ItemId = e.id
WHERE c.qty IS NOT NULL
AND b.ItemCode IS NOT NULL) AS a
PIVOT (Max(materialused)
FOR groupname IN ([CONTACT],
[Coil/Silver/Wire])) AS b
but the query won't complete even after 10 minutes, it won't even show a single records. beside that one, the first query before I used pivot, I put an order by a.no but when I executed it, the result only shows to record 105 but the query is still working and no more record was loaded.
can anyone help me find out what's going on?
thank you!
Here's the sample data collected from the inner query and the expected value after the query.
enter image description here
I realize that I selected "matcode" in the outer select while it shouldn't be there (still didn't work though, and note for the column "material used", I'll be using it later after the pivot is done)
Try using conditional aggregate instead of pivot.
SELECT no,
finish,
shift,
mccode,
NAME,
itemcode,
itemname,
qty,
lotno,
description,
okng,
matcode,
matlot,
[CONTACT] = Max(CASE WHEN GroupName = 'CONTACT' THEN materialused END),
[Coil/Silver/Wire] = Max(CASE WHEN GroupName <> 'CONTACT' THEN materialused END)
FROM (<inner query>) AS a
GROUP BY no,
finish,
shift,
mccode,
NAME,
itemcode,
itemname,
qty,
lotno,
description,
okng,
matcode,
matlot
---IF you don't have index on filter columns then try to apply for performance
SELECT a.No,
a.Finish,
a.Shift,
a.McCode,
d.NAME,
b.ItemCode,
b.ItemName,
b.Qty,
b.LotNo,
b.Description,
CASE WHEN b.Description LIKE '%not good%' THEN 'NG'
ELSE 'OK'
END AS OKNG,
c.ItemCode,
c.LotNo,
MAX(CASE WHEN e.groupname ='CONTACT' THEN ( c.Qty / ( a.Qty + a.QtyNg ) ) * b.Qty END)OVER(ORDER BY a.no) AS [CONTACT],
MAX(CASE WHEN e.groupname ='Coil/Silver/Wire' THEN ( c.Qty / ( a.Qty + a.QtyNg ) ) * b.Qty END)OVER(ORDER BY a.no) AS [Coil/Silver/Wire]
FROM PPRDDLV a WITH(NOLOCK)
LEFT JOIN ICMUTTRAN b WITH(NOLOCK)
ON a.PrdNo = b.TranNo
AND a.No = b.TranId
LEFT JOIN ICMUTTRAN c WITH(NOLOCK)
ON a.PrdNo = c.TranNo
AND a.Finish = c.DatePost
AND c.TranTypeID = 6
AND c.LotNo <> '0'
LEFT JOIN popr d WITH(NOLOCK)
ON a.OprCode = d.Code
LEFT JOIN ICITEM e
ON c.ItemId = e.id
WHERE c.qty IS NOT NULL
AND b.ItemCode IS NOT NULL

How to optimize the sql query

This query takes dynamic input in the place of cg.ownerid IN (294777,228649 ,188464).when the input increases in the IN condition the query is taking too much time to execute. Please suggest me a way to optimize it.
For example, the below query is taking 4 seconds, if I reduce the list to just IN(188464) its just taking 1 second.
SELECT *
FROM
(SELECT *,
Row_number() OVER(
ORDER BY datecreated DESC) AS rownum
FROM
(SELECT DISTINCT c.itemid,
(CASE WHEN (Isnull(c.password, '') <> '') THEN 1 ELSE 0 END) AS password,
c.title,
c.encoderid,
c.type,
(CASE WHEN c.author = 'education' THEN 'Discovery' ELSE c.type END) AS TYPE,
c.publisher,
c.description,
c.author,
c.duration,
c.copyright,
c.rating,
c.userid,
Stuff(
(SELECT DISTINCT ' ' + NAME AS [text()]
FROM firsttable SUB
LEFT JOIN secondtable AS rgc ON thirdtable = rgc.id
WHERE SUB.itemid = c.itemid
FOR xml path('')), 1, 1, '')AS [Sub_Categories]
FROM fourthtable AS cg
LEFT JOIN item AS c ON c.itemid = cg.itemid
WHERE Isnull(title, '') <> ''
AND c.active = '1'
AND c.systemid = '20'
AND cg.ownerid IN (294777,
228649,
188464)) AS a) AS b
WHERE rownum BETWEEN 1 AND 32
ORDER BY datecreated DESC
As I haven't further information, I just would suggest a first change of your where clause. They should be moved to a subquery as you left join those columns.
SELECT *
FROM(
SELECT *,
Row_number() OVER(
ORDER BY datecreated DESC) AS rownum
FROM
(SELECT DISTINCT c.itemid,
(CASE WHEN (Isnull(c.password, '') <> '') THEN 1 ELSE 0 END) AS password,
c.title,
c.encoderid,
c.type,
(CASE WHEN c.author = 'education' THEN 'Discovery' ELSE c.type END) AS TYPE,
c.publisher,
c.description,
c.author,
c.duration,
c.copyright,
c.rating,
c.userid,
Stuff(
(
SELECT DISTINCT ' ' + NAME AS [text()]
FROM firsttable SUB
LEFT JOIN secondtable AS rgc ON thirdtable = rgc.id
WHERE SUB.itemid = c.itemid
FOR xml path('')
), 1, 1, ''
) AS [Sub_Categories]
FROM (
SELECT cg.itemid
FROM fourthtable as cg
WHERE cg.ownerid IN (294777,228649, 188464)
) AS cg
LEFT JOIN (
SELECT DISTINCT c.itemid, c.type, c.author, c.title, c.encoderid, c.type, c.publisher, c.description, c.author, c.duration, c.copyright, c.rating,c.userid
FROM item as c
WHERE Isnull(c.title, '') <> ''
AND c.active = '1'
AND c.systemid = '20'
) AS c
ON c.itemid = cg.itemid
) AS a
) AS b
WHERE rownum BETWEEN 1 AND 32
ORDER BY datecreated DESC
But not quite sure if everything is connected right away, your missing some aliases which makes it hard for me to get through your query. But I thing you'll get my idea. :-)
With this little information it's impossible to give any specific ideas, but the normal general things apply:
Turn on statistics io and check what's causing most of the logical I/O and try to solve that
Look at the actual plan and check if there's something that doesn't look ok, for example:
Clustered index / table scans (new index could solve this)
Key lookups with a huge amount of rows (adding more columns to index could solve this, either as normal or included fields)
Spools (new index could solve this)
Big difference between estimated and actual number of rows (10x, 100x and so on)
To give any better hints you should really include the actual plan, table / index structure at least on the essential parts and tell what is too much time (seconds, minutes, hours?)

Looking at SQL Code for Validation

I have two queries that appear to be the same. We are trying to get quarter today. One using a hard coded date and one not. One query is returning 2100 less records. The first and last record are the same. All of Feb match in both and the last 5 days in January match too.
Query 1 with hard coded date.
SELECT c.Account_RecordType
,timewait
,a.convotime
,DATENAME(Month, timewait) AS 'Mnth'
,DATENAME(year, timewait) AS 'yr'
,DATENAME(quarter, timewait) AS 'qrt'
FROM SalesForce.dbo.SalesForceContact AS b
INNER JOIN Dossier_Replication.dbo.vwSF_DATA_Contact c
ON b.ContactID = c.CONTACTID__C
RIGHT OUTER JOIN satVRS.dbo.rptNECACallHistory AS a
ON b.UserID = a.UserID_Caller
WHERE ( b.Platform = 'HandsonVRS' )
AND ( a.timeWait BETWEEN '2014-01-01' AND '2014-02-24' )
AND ( a.isReport = '1' )
AND ( a.NECA_isReport = '1' )
AND ( a.ConvoTime > '0' )
AND ( c.Account_RecordType = 'Enterprise Account' )
GROUP BY c.Account_RecordType
,timewait
,a.convotime
Second query is pulling quarter,year, and day from the date field in question.
SELECT c.Account_RecordType
,timewait
,a.convotime
,DATENAME(Month, timewait) AS 'Mnth'
,DATENAME(year, timewait) AS 'yr'
,DATENAME(quarter, timewait) AS 'qrt'
FROM SalesForce.dbo.SalesForceContact AS b
INNER JOIN Dossier_Replication.dbo.vwSF_DATA_Contact c
ON b.ContactID = c.CONTACTID__C
RIGHT OUTER JOIN satVRS.dbo.rptNECACallHistory AS a
ON b.UserID = a.UserID_Caller
WHERE ( b.Platform = 'HandsonVRS' )
AND ( a.timeWait BETWEEN '2014-01-01' AND '2014-02-24' )
AND ( a.isReport = '1' )
AND ( a.NECA_isReport = '1' )
AND ( a.ConvoTime > '0' )
AND ( c.Account_RecordType = 'Enterprise Account' )
GROUP BY c.Account_RecordType
,timewait
,a.convotime
Does any one see anything obviously wrong with these two. Any suggestions, I thank you in advance. We are running sql 2012
Sorry my bad, here is the second one...
select
c.Account_RecordType,timewait,a.convotime,(datename(year,timewait))as twy,(datename(quarter,timewait))as twq
FROM SalesForce.dbo.SalesForceContact AS b INNER JOIN
Dossier_Replication.dbo.vwSF_DATA_Contact c ON b.ContactID = c.CONTACTID__C RIGHT OUTER JOIN
satVRS.dbo.rptNECACallHistory AS a ON b.UserID = a.UserID_Caller
WHERE (b.Platform = 'HandsonVRS')
AND (datename(year,timewait))=(datename(year,getdate()-1)) AND (datename(quarter,timewait))=(datename(quarter,getdate()-1))
AND (a.isReport = '1')
AND (a.NECA_isReport = '1')
AND (a.ConvoTime > '0')
AND (c.Account_RecordType = 'Enterprise Account')
Group by c.Account_RecordType,timewait,a.convotime
Order by timewait

Is this possible to simplify this SQL query?

I have designed the following sql query to get the percentage of
100 - ((reportedDate – Submission Date) / TotalNumOfVisits) * 100
Is there any way to simplify it? Like combine the two queries into one?
SELECT
q1.VisitMonth,q1.TotalVisit, ISNULL(q2.diff,0) AS DIFF
,100 - ISNULL( (CAST((q2.diff * 1.0 / q1.TotalVisit ) * 100 AS FLOAT)),0) PERC
FROM
(
SELECT DATENAME(MONTH,v.VisitDate) as VisitMonth, count(v.VisitID) as TotalVisit
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
group by DATENAME(MONTH,v.VisitDate)
) q1
LEFT OUTER JOIN
(
SELECT DATENAME(MONTH,v.VisitDate) as MonthName,COUNT(*) as diff
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
AND DATEDIFF(DAY,v.ReportDate,v.SubmissionDate) > 2
group by DATENAME(MONTH,v.VisitDate)
) q2
ON q1.VisitMonth = q2.MonthName
Result:
Try this :-
Select
VisitMonth,isnull(diff,0) as DIFF,
100 - ISNULL( (CAST((diff * 1.0 / Nullif(TotalVisit,0) ) * 100 AS FLOAT)),0) PERC
from
(
SELECT
VisitMonth = Datename(month,visitDate) ,
Diff = Sum(case when DATEDIFF(DAY,v.ReportDate,v.SubmissionDate) > 2 then 1
else 0 end) ,
TotalVisit = Count(v.VisitID)
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
group by DATENAME(MONTH,v.VisitDate)
)a