В заявлении несоответствие с ПЕРВИЧНЫМ КЛЮЧОМ - PullRequest
6 голосов
/ 03 июня 2019

Итак, у меня есть простая таблица с именем 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 Server 2008. Мои извинения, если это известный сбой.Это трудно найти, так как для этого требуется оператор in.«Эквивалентный» запрос, использующий объединение , действительно дает правильные результаты для обеих таблиц.

Редактировать: dbfiddle показывает различия: Неожиданные результаты Ожидаемые результаты

1 Ответ

3 голосов
/ 04 июня 2019

Я не могу конкретно ответить на ваш вопрос, но изменение ORDER BY устраняет проблему.partition by value order by value на самом деле не имеет смысла, и похоже, что проблема заключается в том, чтобы «обмануть» 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, и ни одна из них;при этом все возвращается.Не имеет смысла, чтобы оба были одинаковыми, поэтому они должны быть разными.

Тем не менее, эта проблема сохраняется в SQL Server 2017 (и я подозреваю, 2019), поэтому вы могли бы в любом случае вы хотите получить с ними заявку в службу поддержки (но поскольку вы используете 2008 год, не ожидайте, что он будет исправлен, так как ваша поддержка скоро закончится).

Поскольку комментарии могут быть удалены безобратите внимание, я хотел добавить комментарий @ scsimon и мой ответ:

scsimon: Интересно.Изменение rownum = 2 дает ожидаемые результаты без изменения order by.Я думаю, что это ошибка.

Ларну: Я согласен на @scsimon.Я подозреваю, что изменение WHERE на s1.rownum = 2 фактически заставляет механизм обработки данных фактически определять значения rownum, а не предполагать, что каждая строка "равна";как если бы это было так, никто не будет возвращен.Тем не менее, изменение WHERE на s1.rownum = 2 по-прежнему остается «возвращать случайную строку», если предложения PARTITION BY и ORDER BY совпадают

...