我们有一个包含唯一代码的表格。为了生成一个新的唯一代码,我们使用了一种在下面的 SQL 语句中找到的方法,并发现了 NOT EXISTS
语句似乎允许存在的行的情况。
没有并发问题,因为这已在沙箱中使用针对 SQL Server 2016 运行的单个查询得到证明。如果我们放置 ORDER BY
语句,它会突然按预期运行。看起来好像没有 ORDER BY
这个查询有条件地忽略了 WHERE
子句。如果所有代码发生冲突,我希望 @code
为 NULL 或保持其初始状态 0。
DECLARE @code int = 0;
select @code = Code from (
SELECT top 1 randoms.Code
FROM (
VALUES
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT))
) randoms (Code)
WHERE NOT EXISTS (SELECT 1 FROM TEST_Codes uc WHERE uc.Code = randoms.Code)
) c;
SELECT
c.code,
ud.*
FROM (VALUES (@code)) as c(Code)
LEFT OUTER JOIN TEST_Codes ud
ON ud.Code = c.Code
此语句将允许返回重复项,这由于 WHERE NOT EXISTS
语句而令人费解。
如果我们将 View c
的定义更改为 ) c ORDER BY c.Code
它突然就可以工作了。这是为什么?
最佳答案
Sql Server 不保证它将执行计算标量和类似表达式的次数。可能 where 中的引用使用的值与所选值不同,但是当您添加订单时,它会具体化它并且每行只计算一次。
如果您是 2014 年或以上,您可以在 query_trace_column_values
上使用扩展事件 session 看到这一切发生。
DECLARE @TestCodes TABLE(Code int)
dbcc traceon(2486);
set statistics xml on;
select Code from (
SELECT randoms.Code
FROM (
VALUES
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT))
) randoms (Code)
WHERE NOT EXISTS (SELECT 1 FROM @TestCodes uc WHERE uc.Code = randoms.Code)
) c
option(recompile);
set statistics xml off;
dbcc traceoff(2486);
Union1005
列是右上角常量扫描的输出。它也在连接谓词中再次被引用。此时它被重新评估并返回一个不同的数字。
您也许可以修改查询并让它只被评估一次,但没有任何保证。唯一 100% 安全的方法是在进行检查之前预先具体化随机数(例如放入临时表),这样您就可以保证它们不会被重新计算和更改。
下面是一个使用 SQL 进行黑客攻击以获得不确定结果的示例。我不会使用它,因为它有缺点,它仍然不能保证任何东西,而且即使它有效,如果你选择前 1 个,你的“随机”数字将不再分布良好。它引入了对较低数字的偏见。
select Code from (
SELECT TOP 5 randoms.Code
FROM (
VALUES
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT)),
(CAST((abs(CHECKSUM(newid())) % 1000000) AS INT))
) randoms (Code)
order by Code
) T
WHERE NOT EXISTS (SELECT 1 FROM @TestCodes uc WHERE uc.Code = T.Code)
这实现了它,并且排序输出的值与嵌套循环谓词中使用的值相同。
关于sql - 没有 ORDER BY 问题的 SELECT TOP 1 忽略 WHERE 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42988853/