IN语句与PRIMARY KEY不一致

Jas*_*son 6 sql-server primary-key sql-server-2008

因此,我有一个名为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);
Run Code Online (Sandbox Code Playgroud)

我有第二个表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);
Run Code Online (Sandbox Code Playgroud)

temp和temp2之间的唯一区别是id字段是temp中的主键,而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
)
Run Code Online (Sandbox Code Playgroud)

这是temp的结果:

value       id
----------- -----------
0           1
0           2
0           3
0           4
1           5
1           6
1           7
1           8
Run Code Online (Sandbox Code Playgroud)

这就是将temp替换为temp2(正确的结果)时得到的结果

value       id
----------- -----------
0           1
1           5
Run Code Online (Sandbox Code Playgroud)

运行最里面的查询(s1)时,将检索预期的结果:

id          rownum
----------- --------------------
1           1
2           2
3           3
4           4
5           1
6           2
7           3
8           4
Run Code Online (Sandbox Code Playgroud)

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

id
-----------
1
5
Run Code Online (Sandbox Code Playgroud)

我不知道这可能是什么原因。这是错误吗?

注意:temp2是使用simple创建的select * into temp2 from temp。我正在运行SQL Server2008。如果这是已知的故障,我深表歉意。由于需要in声明,因此很难搜索。使用联接的“等效”查询的确会在两个表上产生正确的结果。

编辑:dbfiddle显示差异: 意外结果 预期结果

use*_*983 3

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

如果您将其更改ORDER BYid,问题就会消失。

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);
Run Code Online (Sandbox Code Playgroud)

事实上,将该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);
Run Code Online (Sandbox Code Playgroud)

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

尽管如此,这个问题在 SQL Server 2017 中仍然存在(我怀疑是 2019 年),因此您可能想向他们提出支持票(但由于您使用的是 2008,所以不要指望它会得到解决,因为您的支持大约是结束)。

由于评论可能会被删除,恕不另行通知,我想添加 @scsimon 的评论和我的回复:

西西蒙:有趣。不改变rownum = 2就能得到预期的结果order by。我认为这是一个错误。

Larnu:我同意@scsimon。我怀疑更改有效WHEREs1.rownum = 2迫使数据引擎实际确定 的值rownum,而不是假设每一行都是“相等”;就好像情况确实如此,没有人会被归还。
即便如此,如果和子句相同,则更改WHEREtos1.rownum = 2仍然会放弃“返回随机行”PARTITION BYORDER BY