sql-server - IN 语句与 PRIMARY KEY 不一致

标签 sql-server sql-server-2008 primary-key

所以我有一个名为 temp 的简单表,可以通过以下方式创建:

CREATE TABLE temp (value int, id int not null primary key);
INSERT INTO temp
VALUES(0,1),
      (0,2),
      (0,3),
      (0,4),
      (1,5),
      (1,6),
      (1,7),
      (1,8);

我有第二个表 temp2,可以通过以下方式创建:

CREATE TABLE temp (value int, id int);
INSERT INTO temp
VALUES(0,1),
      (0,2),
      (0,3),
      (0,4),
      (1,5),
      (1,6),
      (1,7),
      (1,8);

temp 和 temp2 唯一的区别是 temp 中的 id 字段是主键,而 temp2 没有主键。我不知道如何,但我通过以下查询得到不同的结果:

select * from temp
where id in (
    select id
    from (
        select id, ROW_NUMBER() over (partition by value order by value) rownum
        from temp
    ) s1
    where rownum = 1
)

这是 temp 的结果:

value       id
----------- -----------
0           1
0           2
0           3
0           4
1           5
1           6
1           7
1           8

这就是当 temp 被 temp2 替换时我得到的结果(正确的结果):

value       id
----------- -----------
0           1
1           5

运行最内层查询 (s1) 时,将检索预期结果:

id          rownum
----------- --------------------
1           1
2           2
3           3
4           4
5           1
6           2
7           3
8           4

当在两者上运行 in 语句查询时,我也得到了预期的结果:

id
-----------
1
5

我无法弄清楚这可能是什么原因。这是一个错误吗?

注释:temp2 是通过简单的 select * into temp2 from temp 创建的。我正在运行 SQL Server 2008。如果这是一个已知的故障,我深表歉意。搜索它很困难,因为它需要 in 语句。使用联接的“等效”查询确实在两个表上生成正确的结果。

编辑:dbfiddle 显示差异: Unexpected Results Expected Results

最佳答案

我无法具体回答您的问题,但更改 ORDER BY 可以解决问题。 按值排序按值分区实际上没有任何意义,看起来问题是在“欺骗”SQL Server;当您按与排序所依据的值相同的值对行进行分区时,每一行都是“行号 1”,因为它们可能都位于开头。不要忘记,表是一个无序的堆; 甚至当它有主键(聚集或非聚集)时。

如果您将 ORDER BY 更改为 id,问题就会消失。

SELECT *
FROM temp2 t2
WHERE t2.id IN (SELECT s1.id
                FROM (SELECT sq.id,
                             ROW_NUMBER() OVER (PARTITION BY sq.value ORDER BY sq.id) AS rownum
                      FROM temp2 sq) s1
                WHERE s1.rownum = 1);

事实上,将 ORDER BY 子句更改为其他任何内容可以解决问题:

SELECT *
FROM temp2 t2
WHERE t2.id IN (SELECT s1.id
                FROM (SELECT sq.id,
                             ROW_NUMBER() OVER (PARTITION BY sq.value ORDER BY (SELECT NULL)) AS rownum
                      FROM temp2 sq) s1
                WHERE s1.rownum = 1);

所以问题是您的 PARTITION BY 和 ORDER BY 子句使用相同的表达式(列);意味着这些行中的任何一行都可以是行号 1,但也不能是行号 1;因此全部返回。两者相同是没有意义的,因此它们应该不同。

不过,这个问题在 SQL Server 2017 中确实存在(我怀疑 2019 年),所以你可能想向他们提出支持票(但由于你使用的是 2008,所以不要期望它会得到修复,因为您的支持即将结束)。

由于评论可能会在不通知的情况下被删除,因此我想添加 @scsimon 的评论和我的回复:

scsimon: Interesting. Changing rownum = 2 gives expected results without changing order by. I think it's a bug.

Larnu: I agree at @scsimon. I suspect that changing the WHERE to s1.rownum = 2 effectively forces the data engine to actually determine the values of rownum, rather than assume every row is "equal"; as if that were the case none would be returned.
Even so, changing the WHERE to s1.rownum = 2 is still resigning to "return a random row", if the PARTITION BY and ORDER BY clauses are the same

关于sql-server - IN 语句与 PRIMARY KEY 不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56434232/

相关文章:

sql-server-2008 - 使用 MS Access 2010 将文件上传到 SQL Server 2008 数据库

sql - 在 SQL 中使用人类可读的主键是否安全?

Mysql复合主键

c# - 如何将两个表连接成一个新表以在 C# 中显示?

sql-server - 从本地服务模拟sqlcmd.exe:用户 'NT AUTHORITY\ANONYMOUS LOGON'登录失败

python - ODBC DSN 使 MSSQL 减慢至超时

sql-server - 使用 TransactionScope 执行包含 BEGIN TRANS - COMMIT TRANS 的多个 SP

sql-server-2008 - 在 SQL Server Management Studio 中查看结果集的架构

sql - 在 SQL Server 2017 中使用标识列间隙

sql - 删除...输出计数(已删除。*)