20

私はこの質問を単純化して拡張した方法で再質問しています。

次の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の結果は正しくありません。次のようにsqliteでキャストしようとしましたrealが、それでも2つのレコードが返されます。

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が2つのレコードを返すのはなぜですか?

クイックアップデート

最新のsqliteバージョン(3.7.11)に対してステートメントを実行しましたが、それでも2つのレコードを取得します。

別の更新

この問題についてsqlite-users@sqlite.orgにメールを送信しました。

私自身、VDBEで遊んでいて、何か面白いものを見つけました。の各ループの実行トレースを分割しましたnot exists(平均グループごとに1つ)。

3つの平均グループを作成するために、次のステートメントを使用しました。

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ます。

ここに画像の説明を入力してください

私は今、それがなぜであるかを見ようとしています。

最終編集

だから私はsqliteのソースコードで十分に遊んでいます。私は今、獣をはるかによく理解していますが、元の開発者がすでにそれをしているように見えるので、それを整理させます:

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

興味深いことに、少なくとも私には、新しいバージョン(私が使用しているバージョンの後の場合もあります)は、前述のコミットで追加されたテストケースで使用される複数のレコードの挿入をサポートしているようです。

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

3 に答える 3

1

この2つの方法を見てみましょう。参照データベースとしてpostgres9.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)

次に、サブクエリ内のロジックの一部を移動して、「not」を削除しましょう:(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)

したがって、自分自身に問いかける必要があります。この相関サブクエリを、have句内で実行する場合、デカルト結合を行うプライマリクエリのすべての行に対してすべての行を評価する場合、実際にはどういう意味ですか。 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)) 
于 2012-04-18T06:32:35.330 に答える
1

クエリのいくつかのバリエーションをいじってみました。

sqliteには、ネストされたHAVING式で以前に宣言されたフィールドの使用にエラーがあるようです。

あなたの例avg1では、2番目の下にあることは常に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チケットリストで同様のバグを見つけることができません。

于 2012-04-17T12:36:37.970 に答える
0

このバージョンを試しましたか?:

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 
于 2012-04-17T13:03:20.127 に答える