Ok So this is the second time i'm trying to fix this.
I was wondering if there is a possible way to optimize the cursor created for a stored procedure, used to iterate through a big select statement with two unions. In which later on, the stored procedure begins inserting values to a staging table checking each value against a "where not exist" select statement.
Or better yet, is it possible to create all this with a select statement and possibly joins.
The inserting process takes far too long to complete, and I would recon selecting the data would be much faster.
Here is an example of the SQL:
declare #ID1 varchar(40) ,
#ID2 varchar(20) ,
#State varchar(20) ,
#isActive bit
Declare CuTable SCROLL INSENSITIVE cursor for
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE When (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
From
server1.db.dbo.table1
Inner Join
server1.db.dbo.table2 on ID2 = ID1
Where ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AP' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
From
server1.db.dbo.table1
Inner Join
server1.db.dbo.table2 on ID2 = ID1
Where
ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
Select
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
From server1.db.dbo.table1 inner join server1.db.dbo.table2 on ID2 = ID1
inner join server1.db.dbo.table13 on ID2 = ID4
Where ID3 = 5 and toDate is null and fromDate is not null AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
Open CuTable
Fetch Next From CuTable Into #ID1, #ID2, #[State], #isActive
While ##Fetch_Status = 0
Begin
Insert Into StagingTable (ID1, ID2, [State], isActive)
--Values
Select #ID1, #ID2, #[State], #isActive
where not exists(select * from StagingTable where ID1 = #ID1 and ID2 = #ID2)
Fetch Next From CuTable Into #ID1, #ID2, #[State], #isActive
End
close CuTable
deallocate CuTable
HEADS UP: I'm using SQL SERVER 2005
UPDATE regarding Leonidas199x comment thread:
Don't think you need a cursor at all, as the data is not dynamically changing. You should be able to do this with a set based approach. Below is an example using CTE, with a left join to only insert those that do not exist in the staging table:
;WITH CTE AS
(
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE When (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AP' AS [State],
CASE WHEN (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
UNION
SELECT
Cast(ID1 as Varchar(20)) AS ID1,
Cast(ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 ON ID2 = ID1
INNER JOIN server1.db.dbo.table13 ON ID2 = ID4
WHERE ID3 = 5 and toDate is null and fromDate is not null AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
)
INSERT INTO StagingTable
(
ID1,
ID2,
[State],
isActive
)
SELECT DISTINCT
CT.ID1,
CT.ID2,
CT.[State],
CT.isActive
FROM CTE AS CT
LEFT JOIN StagingTable AS ST ON ST.ID1 = CT.ID1 AND ST.ID2 = CT.ID2
WHERE ST.ID1 IS NULL
AND ST.ID2 IS NULL;
Given the requirement to check each row as the cursor does, I would use the following, using a temp table to check each set of ID1 and ID2 that are identified are unique when inserting into the temp table, then do the insert to the staging table from the temp table:
/*Create temp table*/
IF OBJECT_ID('tempdb..#tmpData') IS NOT NULL DROP TABLE #tmpData
GO
CREATE TABLE #tmpData
(
ID1 VARCHAR(20) ,
ID2 VARCHAR(20) ,
[State] VARCHAR(2) ,
IsActiveData BIT
)
/*Insert into the temp table, with each insert join back to the temp table to ensure ID1 and ID2 are not already inserted*/
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT CAST(ID1 as Varchar(20)) AS ID1,
CAST(ID2 as Varchar(20)) AS ID2,
'AT' AS [State],
CASE WHEN (isAvtiveDate > { fn CURDATE() }) or isAvtiveDate is null Then 1 else 0 end AS isAvtive
FROM server1.db.dbo.table1
INNER JOIN server1.db.dbo.table2 on ID2 = ID1
WHERE ID3 = 1 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT CAST(T1.ID1 as VARCHAR(20)) AS ID1,
CAST(T2.ID2 as VARCHAR(20)) AS ID2,
'AP' AS [State],
CASE WHEN (isActiveDate > { fn CURDATE() }) or isActiveDate is null Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1 AS T1
INNER JOIN server1.db.dbo.table2 AS T2 ON T2.ID2 = T1.ID1
LEFT JOIN #tmpData AS T ON T.ID1 = T1.ID1 AND T.ID2 = T2.ID2
WHERE ID3 = 2 AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
AND T.ID1 IS NULL
AND T.ID2 IS NULL
INSERT INTO #tmpData
(
ID1 ,
ID2 ,
[State] ,
IsActiveData
)
SELECT
Cast(T1.ID1 as Varchar(20)) AS ID1,
Cast(T2.ID2 as Varchar(20)) AS ID2,
'AH' AS [State],
CASE When (isActiveDate > { fn CURDATE() }) OR isActiveDate IS NULL Then 1 else 0 end AS isActive
FROM server1.db.dbo.table1 AS T1
INNER JOIN server1.db.dbo.table2 AS T2 ON T2.ID2 = T1.ID1
INNER JOIN server1.db.dbo.table13 AS T13 ON T2.ID2 = T13.ID4
LEFT JOIN #tmpData AS T ON T.ID1 = T1.ID1 AND T.ID2 = T2.ID2
WHERE ID3 = 5
AND toDate IS NULL
AND fromDate IS NOT NULL
AND isActiveDate <= ISNULL(isActiveDate,'2020-01-01')
AND T.ID1 IS NULL
AND T.ID2 IS NULL
/*Insert into the staging table from the temp table ensuring only records that are not already in there are inserted.*/
INSERT INTO StagingTable
(
ID1,
ID2,
[State],
isActive
)
SELECT CT.ID1,
CT.ID2,
CT.[State],
CT.isActive
FROM #tmpData AS CT
LEFT JOIN StagingTable AS ST ON ST.ID1 = CT.ID1 AND ST.ID2 = CT.ID2
WHERE ST.ID1 IS NULL
AND ST.ID2 IS NULL;
Related
i got task to optimize the database, because this database has slow response.
is this possible, if i have around 90k records, if i want to select around 27k records, the responses time is 15s?
anything wrong with my query? or this is about indexes?
SET NOCOUNT ON;
SELECT workID AS pageID,
projectName AS recProject,
workType AS recType,
workTitle AS recTitle,
workDescription,
workDate AS recDate,
startTime,
endTime,
RTRIM(LTRIM(firstName + ' ' + lastName)) AS recName
FROM dbo.vTimesheets WITH (NOLOCK)
WHERE isActiveProject = 1
AND workType = 1
AND timeFor = 2
ORDER BY recdate DESC, pageID DESC;
SET NOCOUNT OFF;
this query for view
SELECT dbo.tblTimesheet.workID,
dbo.tblTimesheet.staffID,
dbo.tblTimesheet.workDate,
dbo.tblTimesheet.startTime,
dbo.tblTimesheet.endTime,
dbo.tblTimesheet.projectID,
dbo.tblTimesheet.timeFor,
dbo.tblTimesheet.workType,
dbo.tblTimesheet.workTitle,
dbo.tblTimesheet.workDescription,
dbo.tblProject.projectName,
dbo.tblProject.isDeleted AS isDeletedProject,
dbo.tblProject.isActive AS isActiveProject,
dbo.tblUser.firstName,
dbo.tblUser.lastName,
dbo.tblUser.isDeleted AS isDeletedStaff,
dbo.tblUser.staffType
FROM dbo.tblTimesheet WITH (NOLOCK)
INNER JOIN dbo.tblUser WITH (NOLOCK) ON dbo.tblTimesheet.staffID = dbo.tblUser.userID
INNER JOIN dbo.tblProject WITH (NOLOCK) ON dbo.tblTimesheet.projectID = dbo.tblProject.projectID
WHERE (dbo.tblTimesheet.isDeleted = 0)
ALTER TABLE dbo.tblTimesheet
ADD workDescription2 AS CAST(workDescription AS VARCHAR(MAX))
GO
CREATE INDEX ix ON dbo.tblTimesheet (projectID, staffID, workDate DESC, workID DESC)
INCLUDE (startTime, endTime, workType, workTitle, workDescription2, timeFor, isDeleted)
WHERE isDeleted = 0 AND workType = 1 AND timeFor = 2
--WITH (DROP_EXISTING=ON)
SELECT t.workID AS pageID,
t.workDate AS recDate,
t.startTime,
t.endTime,
t.workType AS recType,
t.workTitle AS recTitle,
t.workDescription2 AS workDescription,
p.projectName AS recProject,
RTRIM(LTRIM(u.firstName + ' ' + u.lastName)) AS recName
FROM dbo.tblTimesheet t --WITH (INDEX (ix))
JOIN dbo.tblUser u ON t.staffID = u.userID
JOIN dbo.tblProject p ON t.projectID = p.projectID
WHERE t.isDeleted = 0
AND p.isActive = 1
AND t.workType = 1
AND t.timeFor = 2
ORDER BY t.workDate DESC, t.workID DESC
this is the new execution plan
but this slower than before
CREATE INDEX ix1 ON dbo.tblTimesheet (projectID, staffID, workID)
INCLUDE (workType, timeFor, isDeleted)
WHERE isDeleted = 0 AND workType = 1 AND timeFor = 2
IF OBJECT_ID('tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp (
workID INT PRIMARY KEY,
recProject VARCHAR(50),
recName VARCHAR(150)
)
INSERT INTO #temp (workID, recProject, recName)
SELECT t.workID,
p.projectName,
RTRIM(LTRIM(u.firstName + ' ' + u.lastName)) AS recName
FROM dbo.tblTimesheet t WITH (INDEX (ix1))
JOIN dbo.tblUser u ON t.staffID = u.userID
JOIN dbo.tblProject p ON t.projectID = p.projectID
WHERE t.isDeleted = 0
AND p.isActive = 1
AND t.workType = 1
AND t.timeFor = 2
SELECT t.workID AS pageID,
t.workDate AS recDate,
t.startTime,
t.endTime,
1 AS recType,
t.workTitle AS recTitle,
t.workDescription,
tt.recProject,
tt.recName
FROM dbo.tblTimesheet t
JOIN #temp tt ON t.workID = tt.workID
ORDER BY t.workDate DESC, t.workID DESC
--OPTION(RECOMPILE)
I am working on optimizing a procedure, currently it takes a little more than 23 minutes to execute from SQL. I have tried re-indexing my tables and it showed no signs of improvement.
I have not been able to figure out a way to us a cross apply or UNPIVOT for this to work right.
Is there anything i could do to improve the speed of this procedure?
#ClientId int,
#FormParentId int = NULL,
#FormQueryMode varchar(20) = 'changelog'
declare
#ClientId int,
#FormParentId INT,
#FormQueryMode varchar(20)
select
#ClientId = 11,
#FormParentId = 277719,
#FormQueryMode = NULL
DROP TABLE #History
*/
CREATE TABLE #History
(
FormHistRecId bigint IDENTITY(1,1) NOT NULL,
FormParentId int,
EffDate datetime,
Updated datetime NULL,
Change varchar(50),
VersionNote varchar(1000),
VersionUserId varchar(50)
)
INSERT INTO #History (FormParentId, EffDate, Updated, Change, VersionNote, VersionUserId)
SELECT ParentId, EffDate, Updated, Change, VersionNote, VersionUserId
FROM (
SELECT f.ClientId, CASE WHEN f.MainFormId IS NULL THEN f.ParentId ELSE (SELECT ParentId FROM Forms WHERE FormId=f.MainFormId) END AS ParentId, f.EffDate, f.Updated, 'Forms' as Change,
f.VersionNote, f.VersionUserId
FROM Forms f
WHERE Status IN (0,1,2) --negatives are 'temporary', and should be ignored if they aren't already cleaned out
UNION ALL/*
SELECT f.ClientId, (SELECT ParentId FROM Forms WHERE FormId=f.MainFormId) AS ParentId, f.EffDate, f.Updated, 'Attachment_Forms' as Change,
f.VersionNote, f.VersionUserId
FROM Forms f
WHERE Status IN (0,1,2) --negatives are 'temporary', and should be ignored if they aren't already cleaned out
UNION ALL*/
SELECT f.ClientId, CASE WHEN f.MainFormId IS NULL THEN f.ParentId ELSE (SELECT ParentId FROM Forms WHERE FormId=f.MainFormId) END, h.EffDate, h.Updated, 'Holders' as Change, 'Holder Updated' as VersionNote, h.VersionUserId
FROM Forms f
LEFT OUTER JOIN Forms f_exp ON f_exp.FormId=(
SELECT TOP 1 FormId
FROM Forms
WHERE ParentId=f.ParentId
AND ( EffDate > f.EffDate OR (EffDate=f.EffDate AND Updated > f.Updated))
ORDER BY EffDate ASC, Updated ASC
)
--check for holder updates within the span this Forms record's selection implies
INNER JOIN Holders h ON h.ParentId=f.HolderParentId
AND h.EffDate >= f.EffDate --must be effective after-or-with the form
AND h.EffDate >= f.Updated --must be entered after-or-with the form
AND (f_exp.FormId IS NULL OR h.EffDate < f_exp.EffDate) --must be effective before next form becomes effective
AND (f_exp.FormId IS NULL OR h.EffDate < f_exp.Updated) --must be entered before next form is entered
UNION ALL
SELECT f.ClientId, CASE WHEN f.MainFormId IS NULL THEN f.ParentId ELSE (SELECT ParentId FROM Forms WHERE FormId=f.MainFormId) END, ct.Updated as EffDate, NULL AS Updated, 'ClientTemplates' as Change
,ct.VersionNote, ct.VersionUserId
FROM Forms f
LEFT OUTER JOIN Forms f_exp ON f_exp.FormId=(
SELECT TOP 1 FormId
FROM Forms
WHERE ParentId=f.ParentId
AND ( EffDate > f.EffDate OR (EffDate=f.EffDate AND Updated > f.Updated))
ORDER BY EffDate ASC, Updated ASC
)
INNER JOIN ClientTemplates ct ON ct.ParentId=f.TemplateParentId
AND ct.Updated >= f.EffDate
--AND ct.Updated >= f.Updated
AND (f_exp.FormId IS NULL OR ct.Updated < f_exp.EffDate)
--AND (f_exp.FormId IS NULL OR ct.Updated < f_exp.Updated)
UNION ALL
SELECT f.ClientId, CASE WHEN f.MainFormId IS NULL THEN f.ParentId ELSE (SELECT ParentId FROM Forms WHERE FormId=f.MainFormId) END, mt.Updated as EffDate, NULL AS Updated, 'MasterTemplates' as Change
,mt.VersionNote, mt.VersionUserId
FROM Forms f
LEFT OUTER JOIN Forms f_exp ON f_exp.FormId=(
SELECT TOP 1 FormId
FROM Forms
WHERE ParentId=f.ParentId
AND ( EffDate > f.EffDate OR (EffDate=f.EffDate AND Updated > f.Updated))
ORDER BY EffDate ASC, Updated ASC
)
INNER JOIN ClientTemplates ct ON ct.ParentId=f.TemplateParentId
AND ct.Updated >= f.EffDate
--AND ct.Updated >= f.Updated
AND (f_exp.FormId IS NULL OR ct.Updated < f_exp.EffDate)
--AND (f_exp.FormId IS NULL OR ct.Updated < f_exp.Updated)
LEFT OUTER JOIN ClientTemplates ct_exp ON ct_exp.TemplateId=(
SELECT TOP 1 TemplateId
FROM ClientTemplates
WHERE ParentId=ct.ParentId
AND Updated > ct.Updated --(no eff date)
ORDER BY Updated ASC
)
INNER JOIN MasterTemplates mt ON mt.ParentId=ct.MasterParentId
AND mt.Updated >= ct.Updated
--AND mt.Updated >= ct.Updated
AND (ct_exp.TemplateId IS NULL OR mt.Updated < ct_exp.Updated)
UNION ALL
SELECT (SELECT TOP 1 ClientId FROM Forms WHERE ParentId=ctblsel.FormParentId),
(SELECT
CASE WHEN f.MainFormId IS NULL THEN f.ParentId ELSE (SELECT ParentId FROM Forms WHERE FormId=f.MainFormId) END
FROM Forms f WHERE FormId=(
SELECT TOP 1 FormId
FROM Forms
WHERE ParentId=ctblsel.FormParentId
AND ( EffDate > ctblsel.EffDate OR (EffDate=ctblsel.EffDate AND Updated > ctblsel.Updated))
ORDER BY EffDate DESC, Updated DESC
)
)AS FormParentId,
--ctblsel.FormParentId,
ctblsel.EffDate, ctblsel.Updated, 'CTblEntrySelection' as Change
,'Client Table Entry Selected' as VersionNote, ctblsel.VersionUserId
FROM CTblEntrySelection ctblsel
) dt
WHERE ClientId=#ClientId
AND (#FormParentId IS NULL OR ParentId=#FormParentId)
DECLARE CTblCur CURSOR FOR
SELECT ClientTableId
FROM ClientTables
WHERE ClientId=#ClientId
OPEN CTblCur
DECLARE #ClientTableId int
DECLARE #q nvarchar(MAX), #p nvarchar(100), #tblname nvarchar(50)
SET #p =
'#ClientId int,
#FormParentId int,
#ClientTableId int'
FETCH NEXT FROM CTblCur INTO #ClientTableId
WHILE ##FETCH_STATUS = 0
BEGIN
SET #tblname = '[ClientTable_'+CONVERT(nvarchar,#ClientTableId)+']'
SET #q =
'INSERT INTO #History (FormParentId, EffDate, Updated, Change, VersionNote, VersionUserId)
SELECT ctblsel.FormParentId, ctbl.Updated as EffDate, NULL AS Updated, ''ClientTable'' as Change
,ctbl.VersionNote, ctbl.VersionUserId
FROM CTblEntrySelection ctblsel
LEFT OUTER JOIN CTblEntrySelection ctblsel_exp ON ctblsel_exp.RecId = (
SELECT TOP 1 RecId
FROM CTblEntrySelection
WHERE FormParentId=ctblsel.FormParentId
AND ClientTableId=ctblsel.ClientTableId
AND ( EffDate > ctblsel.EffDate OR (EffDate=ctblsel.EffDate AND Updated > ctblsel.Updated))
ORDER BY EffDate ASC, Updated ASC
)
INNER JOIN '+#tblname+' ctbl ON ctbl.ParentId=ctblsel.CTblEntryParentId
AND ctbl.Updated >= ctblsel.EffDate --must be effective after-or-with the form
--AND ctbl.Updated >= ctblsel.Updated --must be entered after-or-with the form
AND (ctblsel_exp.RecId IS NULL OR ctbl.Updated < ctblsel_exp.EffDate) --must be effective before next form becomes effective
--AND (ctblsel_exp.RecId IS NULL OR ctbl.Updated < ctblsel_exp.Updated) --must be entered before next form is entered
WHERE (SELECT TOP 1 ClientId FROM Forms WHERE ParentId=ctblsel.FormParentId)=#ClientId
AND ctblsel.ClientTableId=#ClientTableId'
IF #FormParentId IS NOT NULL
SET #q = #q + '
AND ctblsel.FormParentId=#FormParentId'
print #q
EXEC sp_ExecuteSql #q, #p, #ClientId, #FormParentId, #ClientTableId
FETCH NEXT FROM CTblCur INTO #ClientTableId
END
CLOSE CTblCur
DEALLOCATE CTblCur
IF #FormQueryMode = 'issuancelog'
BEGIN
INSERT INTO #History (FormParentId, EffDate, Updated, Change, VersionNote, VersionUserId)
SELECT l.FormParentId, l.EffDate, l.Updated, 'Issuance', h.VersionNote, l.UserId
FROM FormIssuanceLog l
LEFT OUTER JOIN #History h ON h.FormHistRecId=(
SELECT TOP 1 FormHistRecId
FROM #History
WHERE FormParentId=l.FormParentId
AND EffDate <= l.EffDate
AND COALESCE(Updated,EffDate) <= l.Updated --if #history's Updated is null, means it's a change that cannot be anachronistic; updated = effective.
ORDER BY EffDate DESC, Updated DESC
)
WHERE (
(#FormParentId IS NOT NULL AND l.FormParentId=#FormParentId)
OR
(#FormParentId IS NULL AND l.FormParentId IN ( SELECT ParentId FROM Forms WHERE ClientId=#ClientId ))
)
AND l.ConfirmIssued=1
DELETE FROM #History WHERE Change<>'Issuance'
END
SELECT *,
LastName + ', ' + FirstName + ' (' + Username + ')' as VersionAuthor
FROM (
SELECT
'Courier' FormType,
dt2.FormParentId,
NULL as LegacyFormId,
dt2.EffDate,
dt2.Updated,
dt2.Change,
dt2.VersionNote,
dt2.VersionUserId
,Forms.FormId, ClientTemplates.Name as CTName, Users.FirstName, Users.LastName, Users.Username
,'Courier_'+CONVERT(nvarchar,dt2.FormParentId)+'_'+CONVERT(nvarchar,dt2.EffDate,127)+COALESCE('_'+CONVERT(nvarchar,dt2.Updated,127),'') as PK
,CASE Change
WHEN 'MasterTemplates' THEN 'Master Form (blank PDF)'
WHEN 'ClientTemplates' THEN 'Template'
WHEN 'ClientTable' THEN 'Client Table'
WHEN 'Forms' THEN 'Form'
WHEN 'Holders' THEN 'Holder Address Record'
WHEN 'CTblEntrySelection' THEN 'Client Table Selection'
WHEN 'Issuance' THEN 'Form Issuance'
ELSE '-----'
END ChangeText
,'pg_IssuanceLogPrompt' as Interface
,CASE #FormQueryMode WHEN 'issuancelog' THEN 'ShowHistForm_IssMode' ELSE 'ShowHistForm' END as Command
,CONVERT(varchar(50),dt2.FormParentId) as Param1
,CONVERT(varchar(50),dt2.EffDate,126) Param2
,CONVERT(varchar(50),dt2.Updated,126) Param3
,'Courier.frm' AS HTTPTarget
,Forms.Status FormStatus
,ie.Name AS EditionName
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY FormParentId, EffDate, Updated ORDER BY ChangeDominance ASC ) rn
FROM (
SELECT *,
CASE Change
WHEN 'MasterTemplates' THEN 1
WHEN 'ClientTemplates' THEN 2
WHEN 'ClientTable' THEN 3
WHEN 'Forms' THEN 4
WHEN 'Holders' THEN 5
WHEN 'CTblEntrySelection' THEN 6
ELSE 99
END as ChangeDominance --largely applies to change log; issuance log will get 99, but it only has one value anyway
FROM #History
) dt
) dt2
LEFT OUTER JOIN Forms ON Forms.FormId=(
SELECT TOP 1 FormId
FROM Forms
WHERE ParentId=dt2.FormParentId
AND EffDate <= dt2.EffDate
AND Updated <= COALESCE(dt2.Updated, dt2.Updated) --if #history's Updated is null, means it's a change that cannot be anachronistic; updated = effective.
AND [Status] >= 0
ORDER BY EffDate DESC, Updated DESC
)
LEFT OUTER JOIN Users ON Users.UserId=dt2.VersionUserId
LEFT OUTER JOIN ClientTemplates ON ClientTemplates.TemplateId=(
SELECT TOP 1 TemplateId
FROM ClientTemplates
WHERE ParentId=Forms.TemplateParentId
AND Updated <= dt2.EffDate
--AND Updated <= dt2.Updated
ORDER BY Updated DESC
)
LEFT OUTER JOIN dbo.IssuanceEditions ie ON ie.EditionId=(
SELECT TOP 1 EditionId
FROM dbo.IssuanceEditions ie2
WHERE ie2.ClientId=(SELECT DISTINCT ClientId FROM dbo.Forms WHERE ParentId=dt2.FormParentId)
AND ie2.Updated <= dt2.EffDate
ORDER BY ie2.Updated DESC
)
WHERE rn=1
UNION ALL
SELECT
'LegacyPDF' as FormType,
NULL as FormParentId,
LegacyFormId,
EffDate,
NULL as Updated,
'Imported File' as Change,
HistDescription as VersionNote,
NULL as VersionUserId
,NULL as FormId, NULL as CTName, NULL as FirstName, NULL as LastName, NULL as Username
,'LegacyPDF_'+CONVERT(nvarchar,LegacyFormId) as PK
,'Imported File' as ChangeText
,'ShowLegacyForm' as Interface
,'' as Command
,CONVERT(varchar(50),LegacyFormId) as Param1
,NULL Param2
,NULL Param3
,'Courier.dwnl' AS HTTPTarget
,NULL FormStatus
,NULL EditionName
FROM LegacyForms
WHERE ClientId=#ClientId
AND (#FormParentId IS NULL OR FormParentId=#FormParentId)
) dt
ORDER BY EffDate DESC, Updated DESC
The cursor jumps out as something you may want to look at eliminating. This is such a large query with so many pieces its almost impossible to see where, and if, it can be improved.
Look at the query plans generated by the batch and probably one, or two queries will take up 80% if the batch and those should be examined very carefully. I would predict that most of the time is spent on the sp_executesql statement and that, if possible should be changed to a join that SQL Server could optimize.
I have got a table in SQL Server 2008 looking like this
FIELD1;FIELD2;FIELD3;FIELD4
-------------------------------
TEST1 ;AAAAAA;NULL ;NULL
TEST1 ;NULL ;BBBBBB;NULL
TEST1 ;NULL ;NULL ;CCCC
TEST2 ;XXXXXX;NULL ;NULL
TEST2 ;NULL ;YYYYYY;NULL
TEST2 ;NULL ;NULL ;ZZZZ
TEST3 ;UUUUUU;VVVVVV;NULL
and I want a result like this:
FIELD1;FIELD2;FIELD3;FIELD4
TEST1 ;AAAAAA;BBBBBB;CCCC
TEST2 ;XXXXXX;YYYYYY;ZZZZ
TEST3 ;UUUUUU;VVVVVV;NULL
Is there a good way to achieve this?
A simple way do achieve it is to use subqueries on the select.
declare #MyTable table
(
FIELD1 varchar(50)
,FIELD2 varchar(50)
,FIELD3 varchar(50)
,FIELD4 varchar(50)
)
insert into #MyTable values
('TEST1', 'AAAAAA', NULL , NULL)
,('TEST1', NULL , 'BBBBBB', NULL)
,('TEST1', NULL , NULL , 'CCCC')
,('TEST2', 'XXXXXX', NULL , NULL)
,('TEST2', NULL ,'YYYYYY', NULL)
,('TEST2', NULL ,NULL ,'ZZZZ')
,('TEST3', 'UUUUUU','VVVVVV', NULL)
select * from #MyTable t0
select t1.FIELD1
, (select top 1 t2.FIELD2 from #MyTable t2 where t2.FIELD2 is not null and t2.FIELD1 = t1.Field1)
, (select top 1 t3.FIELD3 from #MyTable t3 where t3.FIELD3 is not null and t3.FIELD1 = t1.Field1)
, (select top 1 t4.FIELD4 from #MyTable t4 where t4.FIELD4 is not null and t4.FIELD1 = t1.Field1)
from #MyTable t1
group by t1.FIELD1
static solution is here:
SELECT a.FIELD1,a.FIELD2,b.FIELD3,c.FIELD4
FROM YourTable a
LEFT JOIN YourTable b ON a.FIELD1 = b.FIELD1 AND b.FIELD3 IS NOT NULL
LEFT JOIN YourTable c ON a.FIELD1 = c.FIELD1 AND c.FIELD4 IS NOT NULL
WHERE a.FIELD2 IS NOT NULL
I have 3 columns to base my JOIN on -> ID, Account, Cust. There can be multiple rows containing the same ID value.
I want to prioritise my JOIN on 1) ID, 2) Account, 3) Cust.
So in the example below, the UserCode that should be populated in #UserData should be 'u11z' as all columns contain a value.
How do I do this? Below my code to date...
UPDATE #UserData
SET UserCode = ur.UserCode
FROM #UserData uA
INNER JOIN UserReference ur
ON uA.ID = ur.ID
AND ((ua.Account = ur.Account) OR (ur.Account = ur.Account))
AND ((ua.Cust = ur.Cust) OR (ur.Cust = ur.Cust))
UserReference TABLE:
Cust Account ID UserCode
234 NULL 9A2346 u12x
234 Test 9A2346 u11z
NULL NULL 9A2346 u30s
#UserData TABLE:
Cust Account ID UserCode
234 Test 9A2346 NULL
Thanks!
You can try the following. I joined tables, counted the number of matches, and ranked them. Then select rank 1.
; with userCte (userCodeA, userCodeB, rank)
as
(
select a.usercode, b.usercode,
rank() over (partition by a.id order by case when a.cust = b.cust then 1 else 0 end +
case when a.account = b.account then 1 else 0 end +
case when a.id = b.id then 1 else 0 end desc) as rank
from userdata a
join userreference b
on a.id = b.id or a.account = b.account or a.id = b.id
)
select * from userCte
--update userCte
--set userCodeA = userCodeB
where rank = 1
Is this what you want? It is difficult to understand what you are asking for.
USE tempdb;
CREATE TABLE UserReference
(
ID VARCHAR(255) NULL
, Account VARCHAR(255) NULL
, Cust INT NULL
, UserCode VARCHAR(255)
);
INSERT INTO UserReference VALUES ('9A2346', NULL, 234, 'A');
INSERT INTO UserReference VALUES ('9A2346', 'TEST', 234, 'B');
INSERT INTO UserReference VALUES ('9A2346', NULL, NULL, 'C');
DECLARE #UserData TABLE
(
ID VARCHAR(255) NULL
, Account VARCHAR(255) NULL
, Cust INT NULL
, UserCode VARCHAR(255)
);
INSERT INTO #UserData
SELECT UR.ID, UR.Account, UR.Cust, NULL
FROM dbo.UserReference UR;
UPDATE #UserData
SET UserCode = ur.UserCode
FROM #UserData uA
INNER JOIN UserReference ur
ON uA.ID = ur.ID
AND ua.Account = ur.Account
AND ua.Cust = ur.Cust;
SELECT *
FROM #UserData;
Results of the last SELECT :
If I understood your question correctly...
And if any col in a row having a null value will drop the priority then you can use a query something like below to check the count of null values in a row, this might not be a complete answer, but a possible approach...
SELECT count(*)
FROM TableName
WHERE Col1 IS NULL and Col2 is null
i have a table called emp. The table has following data.
empid name Manager_id
1 A Null
2 B 1
3 C 2
4 D 2
5 E 4
i want the output from above table as below.
empid name Manager_id Level1 Level2 Level3 Level4
1 A null A null null null
2 B 1 A B null null
3 C 2 A B C null
4 D 2 A B D D
5 E 4 A B D E
with C as
(
select T.EmpID,
T.ManagerID,
T.Name,
cast('' as xml).query('element X { attribute V {sql:column("T.Name")}}') as LvlXML
from YourTable as T
where T.ManagerID is null
union all
select T.EmpID,
T.ManagerID,
T.Name,
C.LvlXML.query('., element X { attribute V {sql:column("T.Name")}}')
from YourTable as T
inner join C
on T.ManagerID = C.EmpID
)
select C.EmpID,
C.Name,
C.ManagerID,
C.LvlXML.value('/X[1]/#V', 'varchar(100)') as Level1,
C.LvlXML.value('/X[2]/#V', 'varchar(100)') as Level2,
C.LvlXML.value('/X[3]/#V', 'varchar(100)') as Level3,
C.LvlXML.value('/X[4]/#V', 'varchar(100)') as Level4,
C.LvlXML.value('/X[5]/#V', 'varchar(100)') as Level5
from C;
SQL Fiddle
Update:
#t-clausen.dk pointed out that performence for the query above is not what it can be so here is a faster version.
First add an index on ManagerID with Name as an included column.
create index IX_YourTable_ManagerID on YourTable(ManagerID) include(Name)
And the new query that builds the needed columns as we go in the recursion.
with C as
(
select T.EmpID,
T.ManagerID,
T.Name,
T.Name as Level1,
cast(null as varchar(100)) as Level2,
cast(null as varchar(100)) as Level3,
cast(null as varchar(100)) as Level4,
1 as Lvl
from YourTable as T
where T.ManagerID is null
union all
select T.EmpID,
T.ManagerID,
T.Name,
C.Level1,
case when C.lvl = 1 then T.Name else C.Level2 end,
case when C.lvl = 2 then T.Name else C.Level3 end,
case when C.lvl = 3 then T.Name else C.Level4 end,
C.Lvl + 1
from YourTable as T
inner join C
on T.ManagerID = C.EmpID
)
select C.EmpID,
C.Name,
C.ManagerID,
C.Level1,
C.Level2,
C.Level3,
C.Level4
from C;
That gives you this nice little query plan with an index seek both in the anchor and in the recursive part of the query:
SQL Fiddle
I would use PIVOT, I imagine this will offer the best performance.
-- testtable and data
DECLARE #t table(Empid int identity(1,1), Name char(1), Manager_id int)
INSERT #t VALUES('A',Null),('B',1),('C',2),('D',2),('E',4)
-- query
;WITH CTE as
(
SELECT
Name,
Manager_id,
Name lvlName,
Empid,
1 reverselvl
FROM #t
UNION ALL
SELECT
CTE.Name,
T.Manager_id,
T.Name lvlName,
CTE.Empid,
CTE.reverselvl + 1
FROM #t T
JOIN CTE
ON T.Empid = CTE.Manager_id
),
CTE2 as
(
SELECT Empid,
count(*) over (partition by Empid) - reverselvl lvl,
lvlName,
max(Manager_id) over (partition by Empid) Manager_id,
CTE.Name
FROM CTE
)
SELECT
Empid,
Name,
Manager_id,
[0] Level1, [1] Level2, [2] Level3, [3] Level4
FROM CTE2
PIVOT (max(lvlName) FOR [lvl] IN ([0],[1],[2],[3])) AS pvt
OPTION (maxrecursion 0)
Result:
Empid Name Manager_id Level1 Level2 Level3 Level4
1 A NULL A NULL NULL NULL
2 B 1 A B NULL NULL
3 C 2 A B C NULL
4 D 2 A B D NULL
5 E 4 A B D E