有没有办法在同一个大表上使用3x UNION All加速复杂查询?

时间:2015-03-25 17:44:10

标签: sql-server sql-server-2008 case union union-all

我正在努力优化一个过程,目前从SQL执行需要花费超过23分钟。我试过重新索引我的表格,但没有任何改善的迹象。

我无法找到一种方式向我们提交交叉申请或UNPIVOT以使其正常工作。

我有什么办法可以提高这个程序的速度吗?

    @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

1 个答案:

答案 0 :(得分:0)

光标跳出来,你可能想看一下消除。这是一个如此大的查询,有这么多的部分几乎不可能看到它在哪里,如果,它可以改进。

查看批处理生成的查询计划,如果批次和那些应该非常仔细地检查,可能会有一个或两个查询占用80%。我预测大部分时间都花在sp_executesql语句上,如果可能的话,应该更改为SQL Server可以优化的连接。