Sql 正在寻找光标替换

Sql 正在寻找光标替换,sql,sql-server,Sql,Sql Server,我有两张桌子TblFinal和TblData。。我需要根据唯一键F____键与D__键将TblData数据与TBLFINAL进行比较 若TblFinal中不存在唯一键的TblData数据,则TblData数据记录将插入TblFinal表中。F_计数器将默认为1,F_IS_CLEAR默认为0 如果TblFinal中存在唯一密钥的TblData数据,那么首先我们从TblFinal获取密钥F_ID 案例A:如果表TblData的D_SEV_ID不等于-11 然后需要更新表TblFinal的F_计数器以

我有两张桌子TblFinal和TblData。。我需要根据唯一键F____键与D__键将TblData数据与TBLFINAL进行比较

若TblFinal中不存在唯一键的TblData数据,则TblData数据记录将插入TblFinal表中。F_计数器将默认为1,F_IS_CLEAR默认为0

如果TblFinal中存在唯一密钥的TblData数据,那么首先我们从TblFinal获取密钥F_ID

案例A:如果表TblData的D_SEV_ID不等于-11

然后需要更新表TblFinal的F_计数器以获得F_ID

案例B:如果表TblData的D_SEV_ID等于-11

然后D_SEV_ID=-11的新条目和F_ID的F_CLEAR=1

这是我写的游标,需要一些优化的解决方案,因为我的实际TBL最终拥有巨大的数据和数据比较表TblData总是有100条记录要比较

CREATE TABLE #TblFinal
(F_ID INT IDENTITY(1,1), F_VAL NVARCHAR(20), F_SEV_ID INT, F_U_KEY NVARCHAR(200), F_COUNTER INT DEFAULT(1), F_IS_CLEAR BIT DEFAULT(0))


CREATE TABLE #TblData
(D_ID INT, D_VAL NVARCHAR(20), D_SEV_ID INT, D_U_KEY NVARCHAR(200))

INSERT INTO #TblData VALUES(1, 'test 1', 2, '1:100002135::::15124:9334'),     (2, 'test 1', 2, '1:100002135::::15124:9334'),
(3, 'test', -11, '1:100002135::::15124:9334'), (4, 'test 1', 2, '1:100002135::::15124:9334'), (5, 'test 1', 2, '1:1024:9334')


DECLARE @D_ID INT
DECLARE @D_SEV_ID INT
DECLARE @D_U_KEY NVARCHAR(200)

DECLARE A_CUR CURSOR FOR  
SELECT D_ID, D_SEV_ID, D_U_KEY FROM #TblData ORDER BY D_ID ASC
OPEN A_CUR   
FETCH NEXT FROM A_CUR INTO @D_ID, @D_SEV_ID, @D_U_KEY    

WHILE @@FETCH_STATUS = 0   
BEGIN   
       IF EXISTS(SELECT 1 FROM #TblFinal(NOLOCK) WHERE F_U_KEY = @D_U_KEY AND F_IS_CLEAR = 0 AND F_SEV_ID <> -11)
       BEGIN
            DECLARE @FId INT
            SELECT @FId = F_ID FROM #TblFinal(NOLOCK) WHERE F_U_KEY = @D_U_KEY AND F_IS_CLEAR = 0 AND F_SEV_ID <> -11
            --IF @D_SEV_ID != -11
            IF (@D_SEV_ID <> -11)
            BEGIN
                UPDATE #TblFinal
                SET F_COUNTER = F_COUNTER + 1 WHERE F_ID = @FId
            END
            --IF @D_SEV_ID = -11
            ELSE IF(@D_SEV_ID = -11)
            BEGIN
                INSERT INTO #TblFinal (F_VAL, F_SEV_ID, F_U_KEY)
                SELECT  D_VAL, D_SEV_ID, D_U_KEY FROM #TblData(NOLOCK) WHERE D_ID =  @D_ID

                UPDATE #TblFinal
                SET F_IS_CLEAR = 1 WHERE F_ID = @FId
            END
            ELSE
            BEGIN
                PRINT 'DO NOTHING'
            END
       END
       ELSE
       BEGIN
            INSERT INTO #TblFinal (F_VAL, F_SEV_ID, F_U_KEY)
            SELECT  D_VAL, D_SEV_ID, D_U_KEY FROM #TblData(NOLOCK) WHERE D_ID =  @D_ID
       END

       FETCH NEXT FROM A_CUR INTO @D_ID, @D_SEV_ID, @D_U_KEY  
END   

CLOSE A_CUR   
DEALLOCATE A_CUR


--SELECT * FROM #TblData
SELECT * FROM #TblFinal
DROP TABLE #TblData
DROP TABLE #TblFinal

看起来基于集合的方法不适合您,因为数据的顺序看起来很重要。也就是说,您可以做一些事情,将查询速度提高50%左右

循环从minD_ID到maxD_ID,递增1。 仅在TblFinal中查找该项一次,而不是在exists中查找一次,然后再次查找以获取@FId。另外,确保TblFinal在F___键+F_IS_CLEAR+F_SEV_ID上有一个索引,并且F_ID是聚集索引的一部分或在INCLUDE子句中。理想情况下,它是聚集索引中更新最快的第一个字段。 通过在变量中存储D_VAL,您可以从变量直接插入TblFinal,而无需再次从TblData中选择。 代码如下:

DECLARE @D_ID INT
DECLARE @FId INT
DECLARE @D_SEV_ID INT
DECLARE @D_U_KEY NVARCHAR(200)
DECLARE @D_VAL NVARCHAR(20)

DECLARE @Min_D_ID INT, @Max_D_ID INT
SELECT  @Min_D_ID = min(D_ID), @Max_D_ID = max(D_ID) FROM #TblData(NOLOCK)

SELECT @D_ID = @Min_D_ID

WHILE @D_ID <= @Max_D_ID
BEGIN   
    SELECT @D_SEV_ID = D_SEV_ID, @D_U_KEY = D_U_KEY, @D_VAL = D_VAL FROM #TblData(NOLOCK) WHERE D_ID = @D_ID

    IF @D_U_KEY IS NOT NULL
    BEGIN
        SELECT @FId = F_ID FROM #TblFinal(NOLOCK) WHERE F_U_KEY = @D_U_KEY AND F_IS_CLEAR = 0 AND F_SEV_ID <> -11

        IF @FId IS NOT NULL
        BEGIN
            IF (@D_SEV_ID <> -11)
            BEGIN
                UPDATE #TblFinal
                SET F_COUNTER = F_COUNTER + 1 WHERE F_ID = @FId
            END
            ELSE IF(@D_SEV_ID = -11)
            BEGIN
                INSERT INTO #TblFinal (F_VAL, F_SEV_ID, F_U_KEY)
                SELECT  @D_VAL, @D_SEV_ID, @D_U_KEY

                UPDATE #TblFinal
                SET F_IS_CLEAR = 1 WHERE F_ID = @FId
            END
            ELSE
            BEGIN
                PRINT 'DO NOTHING'
            END
        END
        ELSE
        BEGIN
            INSERT INTO #TblFinal (F_VAL, F_SEV_ID, F_U_KEY)
            SELECT  @D_VAL, @D_SEV_ID, @D_U_KEY
        END
    END

    SELECT @D_ID = @D_ID + 1, @FId = NULL
END   

您应该能够根据设置来执行此操作。这里有一个解决这个问题的方法。我唯一不明白的是F_很清楚。这方面的商业规则有点模糊。如果你需要帮助的部分,我需要了解的理由是什么

CREATE TABLE #Output
(F_ID INT IDENTITY(1,1), F_VAL NVARCHAR(20), F_SEV_ID INT, F_U_KEY NVARCHAR(200), F_COUNTER INT DEFAULT(1), F_IS_CLEAR BIT DEFAULT(0))

insert #Output
    select x.D_VAL
    , x.D_SEV_ID
    , x.D_U_KEY
    , x.F_COUNTER
    , x.F_IS_CLEAR
from 
(
    select D_VAL = MIN(D_VAL)
        , D_SEV_ID = MIN(D_SEV_ID)
        , D_U_KEY
        , F_COUNTER = COUNT(*)
        , F_IS_CLEAR = 1
        , SortOrder = 1
    from #TblData d
    where D_ID < (select d2.D_ID from #TblData d2 where d2.D_U_KEY = d.D_U_KEY and d2.D_SEV_ID = -11)
    group by d.D_U_KEY

    UNION ALL

    select D_VAL
        , D_SEV_ID
        , D_U_KEY
        , F_COUNTER = 1
        , F_IS_CLEAR = 0
        , SortOrder = 2
    from #TblData d
    where d.D_SEV_ID = -11

    UNION ALL

    select D_VAL = MIN(D_VAL)
        , D_SEV_ID = MIN(D_SEV_ID)
        , D_U_KEY
        , F_COUNTER = COUNT(*)
        , F_IS_CLEAR = 0
        , SortOrder = 3
    from #TblData d
    where D_ID > (select d2.D_ID from #TblData d2 where d2.D_U_KEY = d.D_U_KEY and d2.D_SEV_ID = -11)
    group by d.D_U_KEY

    UNION ALL

    select D_VAL = MIN(D_VAL)
        , D_SEV_ID = MIN(D_SEV_ID)
        , D_U_KEY
        , F_COUNTER = COUNT(*)
        , F_IS_CLEAR = 0
        , SortOrder = 4
    from #TblData d
    where NOT EXISTS (select d2.D_ID from #TblData d2 where d2.D_U_KEY = d.D_U_KEY and d2.D_SEV_ID = -11)
    group by d.D_U_KEY
) x

order by D_U_KEY, SortOrder

select * from #Output

什么是海量数据?你试过什么?额外的荣誉,希望摆脱这个光标!!!“我不得不说,那些列名让我想挖出我的眼球。”肖恩·兰格,为难看的列名感到抱歉。你能解释一下这是怎么回事吗?所有的样本数据看起来都是重复的,很难破译。还有,为什么所有的诺洛克暗示?它们没有提供任何好处,因为这些是您创建的临时表。如果你坚持使用查询提示,你应该使用WITH关键字,省略它是不可取的。我同意nolock。数据是重复的,这是正确的,这就是为什么我接受了游标的帮助。每次检查一条记录,并根据唯一键检查决定更新或插入。光标代码是自解释的。我不同意代码是自解释的。这是一碗意大利面,我没有一个小时的时间来解释。您有5行输入,但执行后只有4行。为什么?输出中缺少一行…哪一行不存在以及原因?gr8建议。数据的顺序非常重要。感谢您的代码,但输出与我的光标结果集不匹配。已编辑。在循环结束时将@FId设置回NULL。请将其标记为答案,好吗?我测试了Sean提出的基于集合的解决方案,但发现,正如所写的,如果最终表中已经有行,那么它就不起作用。是的,正确,但至少他提供了一种思考线性化的方法,谢谢你,请你更确切地解释代码。我刚把它分成4块。首先是状态为-11之前的行。然后是显式-11。第三个区块是状态-11之后的任何行。最后但并非最不重要的是那些状态不为-11的键。然后,为了以正确的顺序返回数据,我在每个查询中都包含了一列用于排序。你还有什么不明白的地方吗?谢谢你的解释。我对SortOrder=1节的代码有问题,我们在这里设置F_IS_CLEAR=1。如果我们没有任何键的-11,那么这不适用OK,我们正在检查和d2.D_SEV_ID=-11,谢谢!我会检查不同的数据组合,并让大家知道,再次感谢!!!!