IN语句与主键

时间:2019-06-03 20:37:02

标签: 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之间的唯一区别是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
)

这是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 Server2008。如果这是已知的故障,我深表歉意。由于需要in语句,因此很难搜索。使用联接的“等效”查询会在两个表上产生正确的结果。

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

1 个答案:

答案 0 :(得分:3)

我无法具体回答您的问题,但是更改ORDER BY可以解决此问题。 partition by value order by value毫无意义,而且看起来问题出在“愚弄” SQL Server。当您按照要排序的相同值对行进行分区时,每一行都是“行号1”,因为它们可能都在开始处。别忘了,表是无序的堆。 even (具有主键)(无论是否集群)。

如果您将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 BYORDER BY子句都使用相同的表达式(列);意味着这些行中的任何一行都可以是行号1,而它们都不是;因此,全部归还。两者相同没有意义,因此它们应该不同。

仍然,此问题在SQL Server 2017中仍然存在(我怀疑是2019年),因此您可能无论如何都希望向他们提出支持请求(但由于您使用的是2008年,因此请不要期待它会变得固定,因为您的支持即将结束。

由于注释可以删除,恕不另行通知,因此我想添加@scsimon的注释和我的回复:

  

scsimon:有趣。更改rownum = 2可获得预期结果,而无需更改order by。我认为这是一个错误。

     

Larnu:我同意@scsimon。我怀疑将WHERE更改为s1.rownum = 2会有效地迫使数据引擎实际确定rownum的值,而不是假设每一行都是“相等的”。好像情况一样,一无所获。
  即便如此,如果WHEREs1.rownum = 2子句相同,将PARTITION BY更改为ORDER BY仍然表示“返回随机行”