Using CTE in a DELETE is not executing - sql

Here is the original query that runs and runs:
;WITH includedCs_cte
AS
(
SELECT
x.PKey,
x.OKey,
y.CKey
FROM
#Permissions x
JOIN WHDATA.dbo.tb_DimC y
ON
x.PKey = y.PKey AND
x.OKey = y.OKey
)
, b_cte
AS
(
SELECT
i.OKey,
i.PKey
FROM
WHData.dbo.vw_FactCX b
INNER JOIN includedCs_cte i
ON
b.PKey = i.PKey AND
b.PlayCKey = i.CKey
WHERE b.DateKey >= #myLAST28DAYS
GROUP BY
i.OKey,
i.PKey
)
, POK_cte
AS
(
SELECT
i.OKey,
i.PKey
FROM
WHData.dbo.vw_FactCY b
INNER JOIN includedCs_cte i
ON
b.PKey = i.PKey AND
b.PlayCKey = i.CKey
WHERE b.DateKey >= #myLAST28DAYS
GROUP BY
i.OKey,
i.PKey
)
, includedOKeys
AS
(
SELECT *
FROM b_cte
UNION
SELECT * FROM POK_cte
)
DELETE FROM #Permissions
FROM #Permissions p
WHERE NOT EXISTS
(
SELECT 1
FROM includedOKeys x
WHERE
p.PKey = x.PKey AND
p.OKey = x.OKey
)
If I change the above to the below then it runs in less than 10 seconds. Why are these executing so differently?
;WITH includedCs_cte
AS
(
SELECT
x.PKey,
x.OKey,
y.CKey
FROM
#Permissions x
JOIN WHDATA.dbo.tb_DimC y
ON
x.PKey = y.PKey AND
x.OKey = y.OKey
)
, b_cte
AS
(
SELECT
i.OKey,
i.PKey
FROM
WHData.dbo.vw_FactCX b
INNER JOIN includedCs_cte i
ON
b.PKey = i.PKey AND
b.PlayCKey = i.CKey
WHERE b.DateKey >= #myLAST28DAYS
GROUP BY
i.OKey,
i.PKey
)
, POK_cte
AS
(
SELECT
i.OKey,
i.PKey
FROM
WHData.dbo.vw_FactCY b
INNER JOIN includedCs_cte i
ON
b.PKey = i.PKey AND
b.PlayCKey = i.CKey
WHERE b.DateKey >= #myLAST28DAYS
GROUP BY
i.OKey,
i.PKey
)
, includedOKeys
AS
(
SELECT *
FROM b_cte
UNION
SELECT * FROM POK_cte
)
SELECT *
INTO #includedOKeys
FROM includedOKeys
CREATE CLUSTERED INDEX ix_inclProdOper ON #includedOKeys(OKey, PKey)
DELETE FROM #Permissions
FROM #Permissions p
WHERE NOT EXISTS
(
SELECT 1
FROM #includedOKeys x
WHERE
p.PKey = x.PKey AND
p.OKey = x.OKey
)

A CTE is resolved when it is used. It means that if you use it twice in a query, it may get resolved twice. CTEs do not always get resolved once and cache in memory. SQL Server is free to execute the query as it sees fit.
In your case, it may be worse than that - because you have used it as a correlated subquery in an EXISTS clause, which is a row-by-row operation. This probably means the plan results in the CTE being resolved for EACH ROW of the #permissions table! That could well be where all the time will be going. Show Execution Plan (Ctrl-L) in SSMS is your friend here.
Check this SQLFiddle, which shows that NONE of the GUIDs are the same, even though the CTE only creates 3 rows. In fact, we get 18 distinct GUIDs.
with cte(guid,other) as (
select newid(),1 union all
select newid(),2 union all
select newid(),3)
select a.guid, a.other, b.guid guidb, b.other otherb
from cte a
cross join cte b
order by a.other, b.other;

Related

How to optimize long running cte

I have one query that is taking 10 minute and giving me 6852104 records. How can I optimize it? I have added execution plan as well.
My query is like this:
WITH CTE (columns name
)
AS (
SELECT columns
FROM #tempTable b WITH (NOLOCK)
INNER JOIN physicaltable1 A ON A.ParentInvoiceItemId = b.InvoiceItemId
INNER JOIN physicaltable2 c ON c.LinkedInvoiceItemId = a.LinkedInvoiceItemId
AND c.HospitalId = B.Hospital_Id
UNION ALL
SELECT columns
FROM CTE LIP
JOIN physicaltable1 LIC ON LIP.ChildInvoiceItemId = LIC.ParentInvoiceItemId
INNER JOIN physicaltable12 LIH ON LIH.LinkedInvoiceItemId = LIC.LinkedInvoiceItemId
AND LIH.HospitalId = LIP.HospitalId
WHERE LIP.[Level] < 3
)
SELECT *
INTO #newtemptable
FROM CTE;

Alternative solution for below SQL Query ,TableMasterID NOT IN take so much time

Need alternative solution for below SQL Query ,TableMasterID NOT IN take so much time, If i remove AND TableMasterID NOT IN
(SELECT DISTINCT c.TableMasterID
FROM ComFinalDataBS as C
WHERE C.ComFileID IN
(SELECT Number FROM fn_SplitInt(#ComFileID,','))) text from below query then getting result in 20 seconds otherwise result getting arround 4 minus.
SELECT A.SubTitleId,TableMasterID from SubTitle as A JOIN ComTableMaster as B ON a.SubTitle = b.TblName AND TableMasterID NOT IN (SELECT DISTINCT c.TableMasterID
FROM ComFinalDataBS as C
WHERE C.ComFileID IN
(SELECT Number FROM fn_SplitInt(#ComFileID,','))) AND B.TableMasterID IN
(SELECT DISTINCT d.TableMasterID
FROM ComData as D
WHERE D.ComFileID IN
(SELECT Number FROM fn_SplitInt(#ComFileID,','))) ORDER BY A.MainTitleID
SELECT A.SubTitleId ,
TableMasterID
FROM SubTitle AS A
JOIN ComTableMaster AS B ON A.SubTitle = B.TblName
WHERE NOT EXISTS ( SELECT 1
FROM ComFinalDataBS C
WHERE TableMasterID = C.TableMasterID
AND C.ComFileID IN (
SELECT Number
FROM MEFCampus..fn_SplitInt(#ComFileID, ',') ) )
AND NOT EXISTS ( SELECT 1
FROM ComData D
WHERE TableMasterID = D.TableMasterID
AND D.ComFileID IN (
SELECT Number
FROM MEFCampus..fn_SplitInt(#ComFileID,
',') ) )
AND B.IsDeleted = 0
ORDER BY MainTitleID
Have you tried storing the result from the fn_split_string() into an indexed temp-table first? It should help the Query Optimizer a lot.
SELECT DISTINCT Number
INTO #ComFileID
FROM dbo.fn_SplitInt(#ComFileID,',')
CREATE UNIQUE CLUSTERED INDEX uq0_ComFileID ON #ComFileID (Number) WITH (FILLFACTOR = 100)
SELECT A.SubTitleId,TableMasterID
FROM SubTitle as A
JOIN ComTableMaster as B
ON a.SubTitle = b.TblName
/*
AND B.TableMasterID NOT IN (SELECT DISTINCT c.TableMasterID
FROM ComFinalDataBS as C
JOIN #ComFileID CFI
ON CFI.Number = C.ComFileID )
*/
AND NOT EXISTS ( SELECT *
FROM ComFinalDataBS as C
JOIN #ComFileID CFI
ON CFI.Number = C.ComFileID
WHERE c.TableMasterID = B.TableMasterID )
/*
AND B.TableMasterID IN (SELECT DISTINCT d.TableMasterID
FROM ComData as D
JOIN #ComFileID CFI
ON CFI.Number = D.ComFileID
*/
AND EXISTS ( SELECT *
FROM ComData as D
JOIN #ComFileID CFI
ON CFI.Number = D.ComFileID
WHERE D.TableMasterID = B.TableMasterID )
ORDER BY A.MainTitleID

How to Reduce Execution Time

It's just my startup in SQL, I have 4.5 GB database from that I am trying to select records from specific dates and its 25 seconds execute. Is there a way to make it faster?
My query:
select a.*,b.ModelName
from Validation a
left Join ModelMaster b
on SUBSTRING(a.STB,11,2) = b.ProductCode and
Convert(int,substring(a.STB,9,2)) = b.BrandCode
Where STB in (Select STB From Scan_Data_For_Century) and
a.date between '2015-01-14' and '2015-01-14'
You can try query like this:
SELECT a.*,
b.ModelName
FROM Scan_Data_For_Century sd
INNER JOIN Validation a
ON a.STB = sd.STB AND a.date between '2015-01-14' and '2015-01-14'
LEFT JOIN ModelMaster b
ON SUBSTRING(a.STB,11,2) = b.ProductCode AND Convert(int,substring(a.STB,9,2)) = b.BrandCode
I know, that must be a comment. Please, try query. If it wont help I will remove my answer.
Try this:
;with cte as (
select
*,
ProductCode = SUBSTRING( STB, 11, 2 ),
BrandCode = Convert( int, substring( STB, 9, 2 ) )
from Validation
)
select
a.*,
c.ModelName
from
cte a
inner join Scan_Data_For_Century b on ( b.STB = a.STB )
left Join ModelMaster c on ( c.ProductCode = b.ProductCode ) and ( c.BrandCode = b.BrandCode )
Where
( a.date = '2015-01-14' )
Also indexes on:
Validation.STB
Validation.Date
Scan_Data_For_Century.STB
ModelMaster.ProductCode
ModelMaster.BrandCode
might be helpfull

SQL - UNION, priority on the first select statement when doing order by

I'm trying to print out the results from the "GermanDB" Database first, while also showing everything from the Boston DB that was not in the German database. Can this be done in one query?
My query (the bold part functions but does not order the way I want)
select * from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select * from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by ta.ProductRef** , tb.productRef
Just add one field to signal the priority. Like this:
select *, 0 as Priority from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select *, 1 as Priority from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by Priority, ProductRef

How do I optimize this query

Create table #tmptble(RuleId, SubjectId, RID, Date)
Insert into #tmptble(RuleId,SubjectId, RID, Date)
Select RuleTable.RuleId, RuleTable.SubjectId, KeyTable.RID, KeyTable.ParentId
FROM RuleTable INNER JOIN KeyTable
ON KeyTable.RID = RuleTable.RID
This query is very slow. I have an clustered index on RID on KeyTable, clustered index on RuleId on RuleTable, Unique non clustered index on RuleId+SubjectId on the RuleTable. (RuleTable is used in various other places)
In the above query if I introduce a where clause like
Insert into #tmptble(RuleId,SubjectId, RID, Date)
Select RuleTable.RuleId, RuleTable.SubjectId, KeyTable.RID, KeyTable.ParentId
FROM RuleTable INNER JOIN KeyTable
ON KeyTable.RID = RuleTable.RID
WHERE KeyTable.RID = #RID -- #RID is passed into the storedproc
the running time reduces by > 50%. But the problem is that I use the original table result without the WHERE clause in the following way
WITH ResourceTree AS
(
SELECT
#tmptble.RuleId AS [RuleId],
#tmptble.SubjectId AS [SubjectRecId],
#tmptble.RId AS [RId],
#tmptble.ParentID AS [ParentID]
FROM #tmptble WHERE #tmptble.SubjectId = #SubjectId
AND #tmptble.RId = #RId
UNION ALL
-- Recursive step
-- Note that the recursive step uses the results from original #tmptable
SELECT
#tmptble.RuleId AS [RuleId],
#tmptble.SubjectId AS [SubjectId],
#tmptble.RId AS [RId],
#tmptble.ParentID AS [ParentID]
FROM #tmptble INNER JOIN ResourceTree RT
ON RT.ParentID = #tmptble.RId
)
SELECT *
FROM ResourceTree
Is there a way to optimize this query? Any kind of suggestions regarding indexes or way recursion is done would be helpful
Possible this be helpful for you -
;WITH temp AS
(
SELECT
r.RuleId
, r.SubjectId
, r.RID
, k.ParentId
FROM (
SELECT r.*
FROM dbo.RuleTable r
WHERE r.RID = #RID
) r
JOIN dbo.KeyTable k ON k.RID = r.RID
)
, ResourceTree AS
(
SELECT
t.RuleId
, [SubjectRecId] = t.SubjectId
, t.RId
, t.ParentID
FROM temp t
WHERE t.SubjectId = #SubjectId
UNION ALL
SELECT
t.RuleId
, t.SubjectId
, t.RId
, t.ParentID
FROM temp t
WHERE EXISTS (
SELECT 1
FROM ResourceTree r
WHERE r.ParentID = t.RId
)
)
SELECT *
FROM ResourceTree