How do I optimize this query - sql

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

Related

Transposing Rows to Columns and having values flow down in TSQL

I am using TSQL.
If I have the following results:
And I want to transpose this to be like this:
How can I achieve this?
I have the following query:
WITH q AS (
SELECT *
FROM tableOne
WHERE ID = 1
UNION ALL
SELECT m.*
FROM tableOne m
JOIN ON m.ParentID = q.ID
)
SELECT *
FROM q
This gives me all of the items underneath the specified node including the specified node.
It could be easy for us to help you if you add scripting data and not an image. Note that tbl is the name of your table, it is called 3 times. Try this:
select
a.fieldValue company,
b.fieldValue department,
c.fieldValue Job
from tbl a
inner join tbl b on a.parentId is null and a.id=b.parentID
inner join tbl c on b.id= c.parentID
If it does not bring desired results please add data as text and let me know, I could modify the query
Another option (assuming this is NOT a jagged hierarchy).
This is a standard Recursive CTE, with a little twist in the final SELECT
Example
;with cteP as (
Select ID
,ParentID
,PathID = cast(FieldValue as varchar(max))
From YourTable
Where ParentID is Null
Union All
Select ID = r.ID
,ParentID = r.ParentID
,PathID = cast(p.PathID+'|||'+r.FieldValue as varchar(max))
From YourTable r
Join cteP p on r.ParentID = p.ID)
Select ID
,B.*
From cteP A
Cross Apply (
Select Company = xDim.value('/x[1]','varchar(max)')
,Department = xDim.value('/x[2]','varchar(max)')
,Job = xDim.value('/x[3]','varchar(max)')
From (Select Cast('<x>' + replace(PathID,'|||','</x><x>')+'</x>' as xml) as xDim) as X
) B
Where ID not in (Select Distinct ParentID from YourTable where ParentID is not null)
Order By PathID
Returns

Simplifying the SQL Server query

Below query is taking a lot of time to return results in two different databases. Is there any way to simplify this query?
WITH tblParent AS
(
SELECT *
FROM REFERENCES
WHERE referenced_id = 208593
UNION ALL
SELECT REFERENCES.*
FROM REFERENCES
JOIN tblParent ON REFERENCES.referenced_id = tblParent.entity_Id
)
SELECT DISTINCT(entity_Id)
FROM tblParent
WHERE entity_Id <> 208593 AND field_type = 'ChildField'
OPTION(MAXRECURSION 5)
This should simplify it:
WITH tblParent AS
(
SELECT entity_Id, 0 c
FROM [REFERENCES]
WHERE referenced_id = 208593
UNION ALL
SELECT r.entity_Id, 1
FROM [REFERENCES] r
JOIN tblParent
ON r.referenced_id = tblParent.entity_Id
)
SELECT DISTINCT entity_Id
FROM tblParent
WHERE c = 1
OPTION(MAXRECURSION 5)
By checking the c value, it becomes apparent that it is a child value. I am assuming that this text has field_type = 'ChildField' for all childs.
(REFERENCES is a reserved word and DISTINCT is not a function)
Since you're only interested in the child entityId's, then just select the fields you need in the recursive CTE.
WITH tblParent AS (
SELECT entity_Id, referenced_id as baseId
FROM [REFERENCES]
WHERE referenced_id = 208593
UNION ALL
SELECT t.entity_Id, cte.baseId
FROM tblParent cte
JOIN [REFERENCES] t
ON (t.referenced_id = cte.entity_Id
AND t.entity_Id <> cte.baseId -- to avoid a circular reference
)
WHERE t.field_type = 'ChildField'
)
SELECT DISTINCT entity_Id
FROM tblParent
WHERE entity_Id <> baseId
OPTION(MAXRECURSION 5)
And you might want to double-check if there's an index on referenced_id.

MSSQL Inner Join on Concatenated Column

I'm not a DBA so please don't yell at me. Trying to do an inner join and Group By using a concatenated column. The ON statement is producing a syntax error. I do not have access to the original tables and am trying to normalize this into another table, I know its ugly. Not overly worried about performance, just need to work. Cant use functions either.
SELECT DISTINCT A.[carrier_code],[carrier_name], [carrier_grouping], A.[collector_name], [dataset_loaded], [docnum], [envoy_payer_id], [loc], [market], [master_payor_grouping], [plan_class], [plan_name], A.[resp_ins],A.[resp_ind], A.[resp_payor_grouping], A.[Resp_Plan_Type], A.[rspphone], A.[state], A.[sys],A.[resp_ins]+A.[resp_payor_grouping]+A.[carrier_code]+A.[state]+A.[Collector_Name] as ExtId
FROM [Table1] A
INNER JOIN
(SELECT [resp_ins]+[resp_payor_grouping]+[carrier_code]+[state]+[Collector_Name] as Extid
FROM [Table1]
WHERE [resp_ind] = 'Insurance'
GROUP BY [resp_ins]+[resp_payor_grouping]+[carrier_code]+[state]+[Collector_Name]) B
ON A.[resp_ins]+A.[resp_payor_grouping]+A.[carrier_code]+A.[state]+A.[Collector_Name] = B.[resp_ins]+B.[resp_payor_grouping]+B.[carrier_code]+B.[state]+B.[Collector_Name];
My ON and Group By statements are eventually the primary key in new table.
Your alias B hasn't columns as you mentioned. It has just on column Extid.
SELECT DISTINCT A.[carrier_code],[carrier_name], [carrier_grouping], A.[collector_name], [dataset_loaded], [docnum], [envoy_payer_id], [loc], [market], [master_payor_grouping], [plan_class], [plan_name], A.[resp_ins],A.[resp_ind], A.[resp_payor_grouping], A.[Resp_Plan_Type], A.[rspphone], A.[state], A.[sys],A.[resp_ins]+A.[resp_payor_grouping]+A.[carrier_code]+A.[state]+A.[Collector_Name] as ExtId
FROM [Table1] A
INNER JOIN
(SELECT [resp_ins]+[resp_payor_grouping]+[carrier_code]+[state]+[Collector_Name] as Extid
FROM [Table1]
WHERE [resp_ind] = 'Insurance'
GROUP BY [resp_ins]+[resp_payor_grouping]+[carrier_code]+[state]+[Collector_Name]) B
ON A.[resp_ins]+A.[resp_payor_grouping]+A.[carrier_code]+A.[state]+A.[Collector_Name] = B.Extid;
Try this, I didn't put all the column in result, you can manage yourself.
select A.*
from
(
select [carrier_code],[carrier_name], [sys],[resp_ins]+[resp_payor_grouping]+[carrier_code]+[state]+[Collector_Name] as ExtId
FROM [Table1]
) A
inner join
(
select distinct Extid
from
(
SELECT [resp_ins]+[resp_payor_grouping]+[carrier_code]+[state]+[Collector_Name] as ExtId
FROM [Table1]
WHERE [resp_ind] = 'Insurance'
) ins
) B on (A.ExtId = B.ExtId)
You don't need to concatenate the values - you can GROUP BY and JOIN on multiple columns.
SELECT DISTINCT
...
FROM
[Table1] A
INNER JOIN
(
SELECT
[resp_ins],
[resp_payor_grouping],
[carrier_code],
[state],
[Collector_Name]
FROM
[Table1]
WHERE
[resp_ind] = 'Insurance'
GROUP BY
[resp_ins],
[resp_payor_grouping],
[carrier_code],
[state],
[Collector_Name]
) B
ON
(
A.[resp_ins] = B.[resp_ins]
Or
(A.[resp_ins] Is Null And B.[resp_ins] Is Null)
)
And
(
A.[resp_payor_grouping] = B.[resp_payor_grouping]
Or
(A.[resp_payor_grouping] Is Null And B.[resp_payor_grouping] Is Null)
)
And
(
A.[carrier_code] = B.[carrier_code]
Or
(A.[carrier_code] Is Null And B.[carrier_code] Is Null)
)
And
(
A.[state] = B.[state]
Or
(A.[state] Is Null And B.[state] Is Null)
)
And
(
A.[Collector_Name] = B.[Collector_Name]
Or
(A.[Collector_Name] Is Null And B.[Collector_Name] Is Null)
)
;

Using CTE in a DELETE is not executing

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;

Problem joining tables in SQL

SELECT MID, FAD.FirstOpenedDate ,LCD.LastCloseDate
FROM mwMaster.dbo.Merchant M
JOIN (
SELECT MerchID, MIN(moddate) AS FirstOpenedDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) FAD ON FAD.MerchID = M.MerchID
LEFT JOIN (
SELECT MerchID, MAX(moddate) AS LastCloseDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) LCD ON LCD.MerchID = M.MerchID
JOIN (
SELECT merchid ,avg(Transactions) ,avg(Profit)
FROM mwMaster.dbo.ResidualSummary RS
WHERE RS.Date_Processed < LCD.LastCloseDate
GROUP BY Merchid
) R ON R.MerchID = M.MerchID
I am having trouble performing the following join. I have run into this problem before and used temp tables but would like to find out what I am doing wrong. Basically the line that is not working is the 3rd to last. The "< LCD.LastClostDate" says that it cannot be bound. Is it possible to use the value from LCD which I created in a nested query above (in that query I used the M table in a similar way but I didnt run into any issue)? I am thinking becasue the LCD table is dynamically created here it cannot be used in the nested query but this is just my guess.
Any ideas?
On a side note I have also seen people using a CROSS and OVER. Not to farmiliar with how this works but may be applicable here?
I think though haven't tested you can just change your JOIN to a CROSS APPLY in SQL 2005+
SELECT MID, FAD.FirstOpenedDate ,LCD.LastCloseDate
FROM mwMaster.dbo.Merchant M
JOIN (
SELECT MerchID, MIN(moddate) AS FirstOpenedDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) FAD ON FAD.MerchID = M.MerchID
LEFT JOIN (
SELECT MerchID, MAX(moddate) AS LastCloseDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) LCD ON LCD.MerchID = M.MerchID
CROSS APPLY(
SELECT merchid ,avg(Transactions) ,avg(Profit)
FROM mwMaster.dbo.ResidualSummary RS
WHERE RS.Date_Processed < LCD.LastCloseDate
GROUP BY Merchid
) R ON R.MerchID = M.MerchID
But it might be easier to use CTEs
WITH LCD AS (SELECT MerchID, MAX(moddate) AS LastCloseDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID),
R AS (
SELECT merchid ,avg(Transactions) ,avg(Profit)
FROM mwMaster.dbo.ResidualSummary RS
INNER JOIN LCD on
LCD.MERCHID = RS.MERCHID
WHERE RS.Date_Processed < LCD.LastCloseDate
GROUP BY Merchid
)
SELECT MID, FAD.FirstOpenedDate ,LCD.LastCloseDate
FROM mwMaster.dbo.Merchant M
JOIN (
SELECT MerchID, MIN(moddate) AS FirstOpenedDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) FAD ON FAD.MerchID = M.MerchID
LEFT JOIN LCD ON LCD.MerchID = M.MerchID
LEFT JOIN R ON R.MerchID = M.MerchID
I can't really test this without your data, but here's one way you could do it:
SELECT MID,
MIN(moddate) OVER (PARTITION BY MerchID) as FirstOpenedDate,
MAX(moddate) OVER (PARTITION BY MerchID) as LastCloseDate
FROM mwMaster.dbo.Merchant
HAVING DateProcessed < LastCloseDate