2012-04-16 9 views
19

Ponownie pytam o to question w sposób uproszczony i rozszerzony.Dlaczego wyniki SELECT różnią się między mysql i sqlite?

Rozważ te 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); 

używasz SQLite powraca select oświadczenie:

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

i powraca MySQL:

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

O ile mogę powiedzieć, MySQL wyniki są poprawne, a sqlite są niepoprawne. Próbowałem rzucić do real z SQLite jak poniżej ale zwraca jeszcze dwa rekordy:

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); 

Dlaczego powrotne sqlite dwa rekordy?

Szybka zmiana:

Pobiegłem oświadczenie przed najnowszymi sqlite wersji (3.7.11) i jeszcze dostać dwa rekordy.

Kolejna aktualizacja:

Wysłałem e-mail do [email protected] o problemie.

Ja sam bawiłem się VDBE i znalazłem coś interesującego. Podzielam ślad wykonania każdej pętli not exists (po jednej dla każdej grupy średniej).

Aby mieć trzy grupy AVG, użyłem następujące oświadczenia:

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); 

Widzimy wyraźnie, że jakoś co powinno być r:4.5 stała i:5:

enter image description here

Jestem teraz próbuje aby zobaczyć, dlaczego tak jest.

Ostateczna edycja:

Więc grałem tyle z kodem źródłowym SQLite. Rozumiem bestię teraz znacznie lepiej, chociaż ja niech original developer rozwiązać to jak zdaje się być już robi:

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

ciekawe, przynajmniej dla mnie, wydaje się, że nowsze wersje (kilka razy po wersji używam) obsługuje wstawianie wielu rekordów stosowany w przypadku badanej dodanej we wspomnianym popełnić:

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

tylko dla zabawy to wpadłem do SQL Server, co przyniosłoby SQL i skarżył 'avg2' i' avg1' nie istnieje. Zamieniłem je na 'MAX (T2.score)' i 'MAX (T1.score)' i dało wynik SQLite. Kiedy stworzyłem tabelę z 'score REAL', otrzymałem wynik MySQL. Być może twój schemat MySQL różni się od sqlites? –

+0

@ ta.speot.is: czy możesz spróbować dodać 'as' jak w' avg (T2.score) jako avg2' (dwa wystąpienia)? –

+0

Nie działa. Całkiem pewne, że SQL Server nie gra w kości, jeśli chodzi o używanie aliasów w 'WHERE',' GROUP BY' lub 'HAVING'. –

Odpowiedz

1

starałem się bałagan w niektórych odmianach zapytania.

Wygląda na to, że podobnie jak sqlite ma błędy w użyciu wcześniej zadeklarowanych pól w zagnieżdżonych wyrażeniach HAVING.

W przykładzie avg1 pod drugi mający zawsze jest równa 5,0

Look:

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); 

Ten zwraca nic, ale wykonanie Następująca kwerenda zwraca zarówno rekordy:

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

Nie mogę znaleźć podobnego błędu pod adresem sqlite tickets list.

+1

Tak, widzę coś bardzo podobnego podczas śledzenia z VDBE. Wysłałem już wiadomość e-mail na temat tego problemu na adres [email protected] –

0

Czy wypróbowałeś tę wersję? :

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)); 

Również ten (co powinno dawać same wyniki):

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); 

Zapytanie może być obsługiwane z tabel pochodnych zamiast podzapytaniu w HAVING punktach:

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 
+0

Po prostu to zrobiłem; same 2 rekordy w sqlite, ten sam 1 rekord w mysql. –

+0

Tak, twoja druga opcja, którą już służyłem jako odpowiedź; zobacz pytanie, które połączyłem na początku tego pytania. –

+0

Co do tego, dlaczego oryginalne zapytanie nie działa zgodnie z oczekiwaniami, przypuszczam, że jest to błąd związany z obsługą zapytań podrzędnych. –

1

Pozwala spojrzeć na to dwa sposoby, użyję postgres 9.0 jak mojej bazy odniesienia

(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) 

następnie przejdźmy niektórych logiki wewnątrz podzapytania, pozbycie się „nie”: (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) 

więc masz zadać sobie pytanie - co masz na myśli, gdy faktycznie to zrobić skorelowanych podkwerendy wewnątrz mającego klauzuli, jeżeli ocenia każdy wiersz przed każdym rzędzie od pierwotnego zapytania czynimy kartezjański dołącz i nie sądzę, że powinniśmy wskazywać finge rs w silniku SQL.

jeśli chcesz każdy wiersz, który jest mniejszy niż maksymalna średnia Co powinieneś powiedzieć to:

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)) 
+0

To zdanie wybrane w moim pytaniu naprawdę nie jest trudne do zrozumienia; Po prostu chcę prawidłowego wyniku ;-) –

+0

Twierdzę, że jest niejednoznaczny i zapewni nieokreślony wynik w różnych technologiach. –

+0

Wygląda na to, że nie jest niejednoznaczny dla MySql, MS Sql, a teraz Postgres; wydaje się również, że główny programista sqlite wprowadza zmiany w odpowiedzi na e-mail wysłany do [email protected] –

Powiązane problemy