SQL 2005:可以使用 Cursor 优化 upsert-like 存储过程吗?

SQL 2005: Optimize upsert-like Stored Procedure using Cursor, possible?

好的,这是我第二次尝试解决这个问题。

我想知道是否有可能优化为存储过程创建的游标,该游标用于迭代具有两个联合的大 select 语句。随后,存储过程开始将值插入到暂存 table 中,根据 "where not exist" select 语句检查每个值。

或者更好的是,是否可以使用 select 语句创建所有这些并可能加入。

插入过程需要很长时间才能完成,我认为select处理数据会快得多。

这里是 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

注意:我正在使用 SQL SERVER 2005

关于 Leonidas199x 评论线程的更新:

不要认为您根本不需要游标,因为数据不是动态变化的。您应该能够使用基于集合的方法来执行此操作。下面是一个使用 CTE 的示例,左连接仅插入暂存中不存在的那些 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;

鉴于需要像游标一样检查每一行,我将使用以下内容,使用临时 table 检查标识的每组 ID1 和 ID2 在插入临时 table,然后从临时 table:

插入暂存 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;