我正在尝试从SQL Server数据库中清除旧数据(每个表中大约有5000个条目),但是由于要在另一个内部循环CURSOR,所以它花费的时间太长(一个多小时)。
BEGIN
DECLARE @UserId int
DECLARE @productNum varchar(50)
DECLARE user_ids CURSOR FOR SELECT id
FROM Users
WHERE productId IN (SELECT ap.id
FROM Account AS a, AccountProduct AS ap
WHERE a.id = ap.accountId
AND a.name IN ('XXXXXX', 'XXXXXX', 'XXXXXX', 'XXXXXX'))
DECLARE product_cur CURSOR FOR
SELECT ap.id
FROM Account AS a, AccountProduct AS ap
WHERE a.id = ap.accountId
AND a.name IN ('XXXXXX', 'XXXXXX', 'XXXXXX', 'XXXXXX')
OPEN user_ids
FETCH NEXT FROM user_ids INTO @UserId
WHILE @@FETCH_STATUS = 0
BEGIN
OPEN product_cur
FETCH NEXT FROM product_cur INTO @productNum
WHILE @@FETCH_STATUS = 0
BEGIN
DELETE FROM UserRole
WHERE userId = @UserId
AND productId = (SELECT id
FROM AccountProduct
WHERE number = @productNum)
DELETE FROM AccountProduct
WHERE number = @productNum
FETCH NEXT FROM product_cur INTO @productNum
END
CLOSE product_cur
DELETE FROM Users
WHERE id = @UserId
AND accountId IN (SELECT id FROM Account
WHERE name IN ('XXXXXX', 'XXXXXX', 'XXXXXX', 'XXXXXX'))
FETCH NEXT FROM user_ids INTO @UserId
END
CLOSE user_ids
DEALLOCATE user_ids
DEALLOCATE product_cur
END
您知道执行此任务的更好方法吗?
答案 0 :(得分:1)
对于评论来说,这太长了,但是,这足以给您正确的想法。但是,上面的SQL似乎不是完整的SQL,因此我无法为您提供可以提供相同行为的SQL(例如,您在room_cur
中引用了游标FETCH
但是,SQL语句中没有声明游标room_cur
。
SQL是一种基于集合的语言,擅长基于集合的解决方案。游标不是基于不是的解决方案,它们是迭代任务,SQL Server很烂。那是设计。 SQL不是一种编程语言,因此像编程语言一样编写它意味着性能很差。
对于DELETE
语句,您只需要像对待其他任何语句一样对待它,因为DELETE
将从FROM
返回的数据集中的已定义表中删除行。对于DELETE
上的Users
,(可能)这意味着您想要这样的东西:
DELETE U
FROM dbo.Users U
JOIN dbo.Account A ON U.acccountID = A.id
WHERE A.[name] IN ('XXXXXX', 'XXXXXX', 'XXXXXX', 'XXXXXX');
这只是DELETE
中dbo.Users
的行,其中在dbo.Account
中找到了连接的行,而name
中的值在IN
子句中。
答案 1 :(得分:1)
您可以删除频繁提交。我建议一次在事务中删除一个用户ID并提交。
Go for batch based deletion.
DECLARE @UserIdsToDelete TABLE(RowNo int, UserId int)
DECLARE @ProductsToDelete TABLE(RowNo int, ProductId int)
INSERT INTO @UserIdsToDelete
SELECT ROW_NUMBER() OVER (ORDER BY UserId) as RowNo, UserId
FROM Users
WHERE productId IN (SELECT ap.id
FROM Account AS a, AccountProduct AS ap
WHERE a.id = ap.accountId
AND a.name IN ('XXXXXX', 'XXXXXX', 'XXXXXX', 'XXXXXX'))
INSERT INTO @productsToDelete
SELECT ROW_NUMBER() OVER (ORDER BY ap.id) as RowNo, ap.id
FROM Account AS a, AccountProduct AS ap
WHERE a.id = ap.accountId
AND a.name IN ('XXXXXX', 'XXXXXX', 'XXXXXX', 'XXXXXX')
DECLARE @UserIdForDeletion INT
DECLARE @RowNoForDeletion INT = 1
SET @UserIdForDeletion = (SELECT UserID from
@UserIdsToDelete
WHERE RowNO = @RowNoForDeletion )
-- Deletion of Users
WHILE (@UserIdForDeletion IS NOT NULL )
BEGIN
BEGIN TRY
SET XACT_ABORT ON
BEGIN TRANSACTION
DELETE FROM UserRole
WHERE UserId = @UserIdForDeletion
AND productId IN (SELECT ProductID from @ProductsToDelete)
DELETE FROM Users
WHERE id = @UserId
AND accountId IN (SELECT id FROM Account
WHERE name IN ('XXXXXX', 'XXXXXX', 'XXXXXX', 'XXXXXX'))
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRANSACTION;
THROW;
END CATCH
@RowNoForDeletion += 1;
SET @UserIdForDeletion = (SELECT UserID from
@UserIdsToDelete
WHERE RowNO = @RowNoForDeletion )
END
-- Delete the account products
BEGIN TRY
SET XACT_ABORT ON
BEGIN TRANSACTION
DELETE FROM AccountProduct
WHERE number IN (SELECT ProductID from @ProductsToDelete)
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRANSACTION;
THROW;
END CATCH
答案 2 :(得分:-1)
不要使用游标,因为它肯定会影响查询的性能。为什么不以较小的批次执行删除操作以加快查询执行时间? How to delete large data of table in SQL without log?