Внешний запрос SQLite возвращает результаты, не найденные во внутреннем запросе - PullRequest
4 голосов
/ 02 февраля 2011

Мне просто интересно, сталкивался ли кто-нибудь с делом в SQLite (3.7.4), где запрос вернул бы один набор результатов, а когда он стал подзапросом, результаты были совершенно другими?Я нашел проблему в более сложном запросе, но вот более простой пример, демонстрирующий то же поведение:

Настройка базы данных:

CREATE TABLE "test" ("letter" VARCHAR(1) PRIMARY KEY, "number" INTEGER NOT NULL);

INSERT INTO "test" ("letter", "number") VALUES('b', 1);
INSERT INTO "test" ("letter", "number") VALUES('a', 2);
INSERT INTO "test" ("letter", "number") VALUES('c', 2);

Исходный запрос:

SELECT "letter", "number" FROM "test" ORDER BY "letter", "number" LIMIT 1;

Возвращает a|2, вторую строку результатов, как и следовало ожидать, учитывая, что мы сортируем по букве, а не по номеру.Однако вот чего я не ожидал:

Первоначальный запрос как подзапрос:

SELECT DISTINCT "number" FROM (SELECT "letter", "number" FROM "test" ORDER BY "letter", "number" LIMIT 1) AS "test";

Это возвращает 1, что совсем не то, что я ожидал,То, что я ожидал увидеть, это 2.Я понимаю, как работает подзапрос, что он должен возвращать те же результаты , как если бы внутренний запрос был материализован, и внешний запрос был применен к этим результатам (даже если я понимаю, что базы данных идут на очень большиедо получения результатов).

Мое предположение неверно?Я протестировал один и тот же запрос в PostgreSQL и MySQL, и он работал, как я ожидал (т.е. он возвратил 2).Мне кажется, что я столкнулся с ошибкой в ​​том, как SQLite свернул подзапросы, но я не уверен.

Просто повторюсь, приведенный выше пример упрощен по сравнению с тем, что я на самом деле делаю.Я не просто использую DISTINCT для подзапроса, который возвращает одну строку, но скорее он возвращает много строк, некоторые из которых имеют одинаковое значение для столбца, поэтому я нуждаюсь в DISTINCT.Приведенный выше пример - самый простой способ продемонстрировать, что происходит.

РЕДАКТИРОВАТЬ: я смог отключить неправильное сворачивание подзапроса, добавив OFFSET 0 к внутреннему запросу, например,

SELECT DISTINCT "number" FROM (SELECT "letter", "number" FROM "test" ORDER BY "letter", "number" LIMIT 1 OFFSET 0) AS "test";

Я буду сообщать об этом как об ошибке в списке рассылки SQLite и обойти это.

1 Ответ

1 голос
/ 02 февраля 2011

Я могу убедиться, что это происходит с надстройкой SQLite для Firefox.

Если утешительно, эта форма работает:

SELECT DISTINCT "number" FROM (SELECT "letter", "number" FROM "test"
ORDER BY "letter", "number") AS "test" ORDER BY "letter" LIMIT 1;

Я считаю, что спецификация SQLite игнорирует предложение LIMIT во внутренних запросах и переносит его наружу. Без лимита:

SELECT DISTINCT "number" FROM (SELECT "letter", "number" FROM "test"
ORDER BY "letter", "number") AS "test";

Возвращает

1
2
(2 rows)

Интересно отметить, что это также возвращает правильные результаты

SELECT number FROM (SELECT letter, number FROM test
ORDER BY letter, number LIMIT 1) AS test;

Два плана можно сравнить с помощью EXPLAIN.
DESCRIBE добавляет много операций, вставляет и оптимизирует внутренний запрос (неправильно).

...