SQL Server中游标用例的替代方法

时间:2019-03-24 09:12:12

标签: sql-server join cursor

我有两个表:

  • tb1(ID,AccountId,updated_at,...,超过30列)
  • tb2(ID,AccountId,updated_at,...,超过30列)

如果tb1中有同一行,我正在尝试更新tb2中的几个列值,否则,如果行中不存在,则需要插入tb2中的所有新值。表tb1

我用过一个游标,但是它的运行速度非常慢。

我的代码:

DECLARE @accountId INT, @id INT, 
        @title NVARCHAR(1000), @num_questions INT,
        @type NVARCHAR(1000), @starts_at NVARCHAR(1000), 
        @finishes_at NVARCHAR(1000), @published_at NVARCHAR(1000),
        @summary NVARCHAR(2000), @updated_at NVARCHAR(1000)

-- Data from tb2
DECLARE quizRecSet CURSOR FOR
    SELECT 
        AccountId, ID, title, num_questions, type, 
        starts_at, finishes_at, published_at, summary, updated_at
    FROM 
        tb2 
    WHERE
        accountId = 1 
    ORDER BY
        updated_at DESC

OPEN quizRecSet
FETCH NEXT FROM quizRecSet INTO @accountId, @id, @title, @num_questions, @type, 
                                @starts_at, @finishes_at, @published_at, @summary, @updated_at

WHILE (@@FETCH_STATUS = 0)  
BEGIN  
    IF ((SELECT COUNT(*) FROM tb1(nolock) 
         WHERE ID = @ID AND accountId = @accountId) = 0)
    BEGIN
        INSERT INTO tb1
        VALUES (@accountId, @id, @title, @num_questions, @type,
                @starts_at, @finishes_at, @published_at, @summary, @updated_at)
    END
    ELSE
    BEGIN
        UPDATE tb1
        SET title = @title, num_questions = @num_questions,
            type = @type, published_at = @published_at,
            summary = @summary, updated_at = @updated_at
        WHERE ID = @ID AND AccountId = @accountId
    END

    FETCH NEXT FROM quizRecSet INTO @accountId, @id, @title, @num_questions, @type,
                                    @starts_at, @finishes_at, @published_at, @summary, @updated_at
END

CLOSE quizRecSet
DEALLOCATE quizRecSet

上面的代码工作非常缓慢,因为tb1和tb2中都有很多行(大约2M)

有人可以帮助我寻求更好的替代方法并提高性能。

谢谢

2 个答案:

答案 0 :(得分:1)

也许运行得更快:

insert into tb1 (accountId, ID, title, num_questions, type, 
    starts_at, finishes_at, published_at, summary, updated_at)
select AccountId, ID, title, num_questions, type,
    starts_at, finishes_at, published_at, summary, updated_at
  from tb2
 where accountId = 1 -- from the previous cursor sample
   and not exists
    (select 1 from tb1 where ID=tb2.ID and accountId = tb2.accountId)

update tb1 set 
    title = tb2.title, num_questions = tb2.num_questions,
    type=tb2.type, published_at = tb2.published_at, 
    summary = tb2.summary, updated_at = tb2.updated_at
from tb1
join tb2 on (tb1.ID = tb2.ID and tb1.accountId = tb2.accountId)
where tb1.accountId = 1 -- from the previous cursor sample
and not (tb1.title=tb2.title and tb1.num_questions=tb2.num_questions
    and tb1.type=tb2.type and tb1.published_at=tb2.published_at 
    and tb1.summary=tb2.summary and tb1.updated_at=tb2.updated_at)

答案 1 :(得分:0)

设置操作比光标操作快。以下sql可以帮助您提高性能。在这种情况下,我们首先要更新表tb1,然后再插入从表tb2到表tb1尚不存在的行。

UPDATE tb1 SET 
  title = t2.title, 
  num_questions = t2.num_questions,
  type = t2.type,
  published_at = t2.published_at, 
  summary = t2.summary,
  updated_at = t2.updated_at
FROM tb1 AS t1
JOIN tb2 AS t2 ON (t1.ID = t2.ID AND t1.accountId = t2.accountId)
WHERE t1.accountId = 1

INSERT INTO tb1 (
  t2.AccountId, t2.ID, t2.title, t2.num_questions, t2.type, 
  t2.starts_at, t2.finishes_at, t2.published_at, t2.summary, t2.updated_at)
SELECT 
  t2.AccountId, t2.ID, t2.title, t2.num_questions, t2.type, 
  t2.starts_at, t2.finishes_at, t2.published_at, t2.summary, t2.updated_at
FROM (
  SELECT
    AccountId, ID, title, num_questions, type, 
    starts_at, finishes_at, published_at, summary, updated_at
  FROM tb2
  EXCEPT
  SELECT 
    t2.AccountId, t2.ID, t2.title, t2.num_questions, t2.type, 
    t2.starts_at, t2.finishes_at, t2.published_at, t2.summary, t2.updated_at
  FROM tb2 AS t2
  JOIN tb1 AS t1 ON (t1.ID = t2.ID AND t1.accountId = t2.accountId)
  WHERE t2.accountId = 1
) AS TempTable