删除记录时,“子查询返回的值大于1”,如果我更改获取的数字,则不是这样

时间:2018-12-06 20:28:56

标签: sql-server tsql

我正在尝试从4个数据库中删除数百万条记录,并遇到意外错误。我制作了一个临时表,其中包含要删除的所有ID的列表:

CREATE TABLE #CaseList (case_id int)
INSERT INTO #CaseList 
SELECT DISTINCT id 
    FROM my_table
    WHERE <my criteria for choosing cases>

我已删除所有关联的记录(在case_id上​​带有外键)

DELETE FROM image WHERE case_id in (SELECT case_id from #CaseList)

然后我要从my_table中批量删除记录(以免炸毁事务日志-尽管我的数据库处于简单模式下,但在进行类似删除的更改时仍会增长):

DELETE FROM my_table WHERE id in (SELECT case_id
FROM #CaseList
ORDER by case_id
OFFSET 0 ROWS
FETCH NEXT 10000 ROWS ONLY)

这将在一,三或五轮中正常工作(因此我已删除了1万至5万条记录),然后将失败,并显示以下错误消息:

消息512,级别16,状态1,过程trgd_image,第188行 子查询返回的值超过1。当子查询遵循=,!=,<,<=,>,> =或将子查询用作表达式时,不允许这样做。

这真的很奇怪,因为正如我所说,我已经从图像表中删除了所有相关记录。然后它变得很奇怪,因为如果我选择较小的批次,则删除工作不会出现错误。

我通常将FETCH NEXT切成一半(5k),然后再切成两半(2500),然后再切成两半(1200),依此类推,直到它起作用为止

DELETE FROM my_table WHERE id in (SELECT case_id
FROM #CaseList
ORDER by case_id
OFFSET 50000 ROWS
FETCH NEXT 1200 ROWS ONLY)

然后重复该金额,直到我超过失败的数量为止,然后将其恢复为10000,然后可以再次工作一到三个...

DELETE FROM my_table WHERE id in (SELECT case_id
FROM #CaseList
ORDER by case_id
OFFSET 60000 ROWS
FETCH NEXT 10000 ROWS ONLY)

然后再次失败,并出现相同的错误...漂洗,清洗并重复。

当图像表中有与 NOT 相关的记录时,什么会导致该子查询错误?为什么选择较小批次的案件“可以解决”然后又允许较大批次的案件?

我真的很想解决这个问题,因此我可以进行WHILE循环并以这种方式在数百万行中运行此删除操作,而不必手动进行管理,这将使我花费数周的时间来处理数百万行已从4个数据库中删除。

1 个答案:

答案 0 :(得分:0)

您所显示的查询无法产生您所看到的错误。如果确定是,则有错误报告。我的猜测是 trgd_image,第188行(或附近)会发现您使用的是标量比较,=而不是in

我也为您提供一些建议,可以免费询问。我写了很多类似您的查询,却从未使用过OFFSET 60000 ROWS FETCH NEXT 10000 ROWS ONLY这样的查询。您也不需要 ,如果不需要,您的SQL会更容易编写。

首先,除非您的计算机在2018年因使用的数据规模而严重不足,否则我认为您会发现100,000行交易就可以了。如果没有,至少尝试理解为什么不这样做。一台管理数百万行的机器应该能够处理其中的1%,而不会费力。

填充#CaseList时,请捕获@@rowcount。然后,您可以打印/记录该数据,并计算工作中的“块”数。

但是,理想情况下,没有临时表。相反,这些情况可能会有一些逻辑分组可以操作。它们可能具有地区,所有者或日期,无论最初使用哪种方法进行选择。对此进行迭代,例如

delete from T where id in (select id from S where user = 1

一旦这样做,就可以编写一个循环:

select @user = min(user) from S where ...
while @user is not NULL begin
    print "deleting cases for user", @user
    delete from T where id in (select id from S where user = @user)
    select @u = @user
    select @user = min(user) from S where ... and user > @u
end

这样,如果由于任何原因而使过程中途中断,您将拥有逻辑分组删除和明确的中断:您知道所有小于@user的用户(或其他任何用户)的案例将被删除,您可以查看“当前”案例的问题。通常,您会发现问题不是唯一的,并且通过解决问题,可以防止将来与他人发生问题。