Подтвердить что ты не робот

Почему результаты SELECT отличаются между mysql и sqlite?

Я перепрошу этот question в упрощенном и расширенном виде.

Рассмотрим эти операторы sql:

create table foo (id INT, score INT);

insert into foo values (106, 4);
insert into foo values (107, 3);
insert into foo values (106, 5);
insert into foo values (107, 5);

select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
having not exists (
    select T2.id, avg(T2.score) avg2
    from foo T2
    group by T2.id
    having avg2 > avg1);

Используя sqlite, оператор select возвращает:

id          avg1      
----------  ----------
106         4.5       
107         4.0       

и mysql возвращает:

+------+--------+
| id   | avg1   |
+------+--------+
|  106 | 4.5000 |
+------+--------+

Насколько я могу судить, результаты mysql верны, а sqlite неверны. Я попытался использовать real с sqlite как в следующем, но он возвращает две записи:

select T1.id, cast(avg(cast(T1.score as real)) as real) avg1
from foo T1
group by T1.id
having not exists (
    select T2.id, cast(avg(cast(T2.score as real)) as real) avg2
    from foo T2
    group by T2.id
    having avg2 > avg1);

Почему sqlite возвращает две записи?

Быстрое обновление:

Я запустил инструкцию против последней версии sqlite (3.7.11) и все еще получаю две записи.

Другое обновление:

Я отправил электронное письмо на адрес [email protected]

Я сам, я играл с VDBE и нашел что-то интересное. Я разбил след выполнения каждого цикла not exists (по одному для каждой группы avg).

Чтобы иметь три группы avg, я использовал следующие утверждения:

create table foo (id VARCHAR(1), score INT);

insert into foo values ('c', 1.5);
insert into foo values ('b', 5.0);
insert into foo values ('a', 4.0);
insert into foo values ('a', 5.0);

PRAGMA vdbe_listing = 1;
PRAGMA vdbe_trace=ON;

select avg(score) avg1
from foo
group by id
having not exists (
    select avg(T2.score) avg2
    from foo T2
    group by T2.id
    having avg2 > avg1);

Мы ясно видим, что каким-то образом r:4.5 стало i:5:

enter image description here

Теперь я пытаюсь понять, почему.

Окончательное редактирование:

Итак, я играл достаточно с исходным кодом sqlite. Я понимаю, что зверь намного лучше теперь, хотя я дам оригинальный разработчик разобраться, как он, кажется, уже делает это:

http://www.sqlite.org/src/info/430bb59d79

Интересно, что, по крайней мере, мне кажется, что более новые версии (несколько раз после версии, которые я использую) поддерживают вставку нескольких записей, используемых в тестовом примере, добавленном в вышеупомянутый commit:

CREATE TABLE t34(x,y);
INSERT INTO t34 VALUES(106,4), (107,3), (106,5), (107,5);  
4b9b3361

Ответ 1

Я пытался использовать некоторые варианты запроса.

Кажется, что sqlite имеет ошибки в использовании предыдущих объявленных полей во вложенных выражениях HAVING.

В вашем примере avg1 при втором есть всегда 5,0

Облик:

select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
having not exists (
    SELECT 1 AS col1 GROUP BY col1 HAVING avg1 = 5.0);

Это ничего не возвращает, но выполнение следующего запроса возвращает обе записи:

...
having not exists (
    SELECT 1 AS col1 GROUP BY col1 HAVING avg1 <> 5.0);

Я не могу найти подобную ошибку в списке билетов sqlite.

Ответ 2

Давайте рассмотрим два способа: я буду использовать postgres 9.0 в качестве моей базы данных ссылок

(1)

-- select rows from foo 

select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
-- where we don't have any rows from T2
having  not exists (
-- select rows from foo
select T2.id, avg(T2.score) avg2
from foo T2
group by T2.id
-- where the average score for any row is greater than the average for 
-- any row in T1
having avg2 > avg1);

 id  |        avg1        
-----+--------------------
 106 | 4.5000000000000000
(1 row)

затем переместите некоторую логику внутри подзапроса, избавившись от "нет": (2)

-- select rows from foo 
select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
-- where we do have rows from T2
having  exists (
-- select rows from foo
select T2.id, avg(T2.score) avg2
from foo T2
group by T2.id
-- where the average score is less than or equal than the average for any row in T1
having avg2 <= avg1);
-- I think this expression will be true for all rows as we are in effect doing a
--cartesian join 
-- with the 'having' only we don't display the cartesian row set

 id  |        avg1        
-----+--------------------
 106 | 4.5000000000000000
 107 | 4.0000000000000000
(2 rows)

поэтому вы должны спросить себя: что вы на самом деле имеете в виду, когда вы выполняете этот коррелированный подзапрос внутри предложения, если он оценивает каждую строку по каждой строке из основного запроса, мы делаем декартовое соединение, и я не знаю, Думаю, мы должны указывать пальцами на движок SQL.

если вы хотите, чтобы каждая строка была меньше максимального значения. Что вы должны сказать:

select T1.id, avg(T1.score) avg1 
from foo T1 group by T1.id
having avg1 not in 
(select max(avg1) from (select id,avg(score) avg1 from foo group by id)) 

Ответ 3

Вы пробовали эту версию?

select T1.id, avg(T1.score) avg1
from foo T1
group by T1.id
having not exists (
    select T2.id, avg(T2.score) avg2
    from foo T2
    group by T2.id
    having avg(T2.score) > avg(T1.score));

Также этот (который должен давать одинаковые результаты):

select T1.*
from
  ( select id, avg(score) avg1
    from foo 
    group by id
  ) T1
where not exists (
    select T2.id, avg(T2.score) avg2
    from foo T2
    group by T2.id
    having avg(T2.score) > avg1);

Запрос можно также обрабатывать с помощью производных таблиц вместо подзапроса в разделе HAVING:

select ta.id, ta.avg1
from 
  ( select id, avg(score) avg1
    from foo
    group by id
  ) ta
  JOIN
  ( select avg(score) avg1
    from foo 
    group by id
    order by avg1 DESC
    LIMIT 1
  ) tmp
  ON tmp.avg1 = ta.avg1