SQL 2005:使用Cursor优化类似upsert的存储过程可能吗?

时间:2017-10-18 09:31:33

标签: sql-server stored-procedures sql-server-2005 cursor upsert

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

我想知道是否有可能的方法来优化为存储过程创建的游标,用于迭代带有两个联合的大选择语句。稍后,存储过程开始将值插入到临时表中,根据"在不存在的情况下检查每个值"选择陈述。

或者更好的是,是否可以使用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

HEADS UP:我正在使用SQL SERVER 2005

关于Leonidas199x评论主题的更新:

duplicate records

1 个答案:

答案 0 :(得分:0)

根本不需要光标,因为数据没有动态变化。您应该能够使用基于集合的方法执行此操作。下面是使用CTE的示例,左连接仅插入登台表中不存在的那些:

;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;

鉴于需要像游标一样检查每一行,我将使用以下内容,使用临时表检查每个ID1和ID2集合,当插入临时表时,标识是唯一的,然后执行插入操作临时表中的临时表:

/*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;