多対多リレーションに対するMySQLクエリ:ユニオン?
-
05-07-2019 - |
質問
この質問に加えて、複数の列に一致する明確な結果を提供するSQLクエリ 非常にきちんとした解決策を持っていた、私は次のステップがどのように見えるか疑問に思っていました:
DOCUMENT_ID | TAG
----------------------------
1 | tag1
1 | tag2
1 | tag3
2 | tag2
3 | tag1
3 | tag2
4 | tag1
5 | tag3
したがって、タグ1および2を持つすべてのdocument_idを取得するには、次のようなクエリを実行します。
SELECT document_id
FROM table
WHERE tag = 'tag1' OR tag = 'tag2'
GROUP BY document_id
HAVING COUNT(DISTINCT tag) = 2
さて、興味深いのは、タグ1と2を持つすべての個別のdocument_idと、それに加えてタグ3を持つidを取得する方法です。 同じクエリを作成し、それらの結合を実行することを想像できます。
SELECT document_id
FROM table
WHERE tag = "tag1" OR tag = "tag2"
GROUP BY document_id
HAVING COUNT(DISTINCT tag) = 2
UNION
SELECT document_id
FROM table
WHERE tag = "tag3"
GROUP BY document_id
しかし、その条件が追加されたら、別の初期クエリを考えることができるかどうか疑問に思いました。私は多くの「組合」を持つことを想像しています。異なるタグとタグカウントのようなものです。 そのような結合のチェーンを作成することは、パフォーマンスの点で非常に悪いことではありませんか?
解決
これは、まだ種類の結合を使用しますが、読みやすく、制御しやすいかもしれません。大規模なデータセットでのこのクエリの速度に非常に興味があるので、その速度を教えてください。小さなデータセットを入力すると、0.0001秒かかりました。
SELECT DISTINCT (dt1.document_id)
FROM
document_tag dt1,
(SELECT document_id
FROM document_tag
WHERE tag = 'tag1'
) AS t1s,
(SELECT document_id
FROM document_tag
WHERE tag = 'tag2'
) AS t2s,
(SELECT document_id
FROM document_tag
WHERE tag = 'tag3'
) AS t3s
WHERE
(dt1.document_id = t1s.document_id
AND dt1.document_id = t2s.document_id
)
OR dt1.document_id = t3s.document_id
これにより、各タグの結果セットがすでに指定されているため、新しいパラメーターを簡単に追加できます。
追加例:
OR dt1.document_id = t2s.document_id
最後まで、document_id 2も取得します
他のヒント
これを単一で実行することは可能ですが、選言を使用するにはWHERE句をhaving句に昇格させる必要があります。
正しいです。追加のUNION句で検索する新しいタグを追加すると、ますます遅くなります。各UNION句は、計画して実行する必要がある追加のクエリです。さらに、完了したらソートすることはできません。
あなたは、基本的なデータウェアハウジング手法を探しています。最初に、1つの追加テーブルを使用してスキーマを再作成します。
create table a (document_id int, tag varchar(10));
insert into a values (1, 'tag1'), (1, 'tag2'), (1, 'tag3'), (2, 'tag2'),
(3, 'tag1'), (3, 'tag2'), (4, 'tag1'), (5, 'tag3');
create table b (tag_group_id int, tag varchar(10));
insert into b values (1, 'tag1'), (1, 'tag2'), (2, 'tag3');
表bには「タググループ」が含まれています。グループ1にはtag1とtag2が含まれ、グループ2にはtag3が含まれます。
テーブルbを変更して、関心のあるクエリを表すことができます。クエリの準備ができたら、一時テーブルを作成して集計データを保存します。
create temporary table c
(tag_group_id int, count_tags_in_group int, tags_in_group varchar(255));
insert into c
select
tag_group_id,
count(tag),
group_concat(tag)
from b
group by tag_group_id;
create temporary table d (document_id int, tag_group_id int, document_tag_count int);
insert into d
select
a.document_id,
b.tag_group_id,
count(a.tag) as document_tag_count
from a
inner join b on a.tag = b.tag
group by a.document_id, b.tag_group_id;
現在、cにはタググループのタグの数が含まれ、dには各ドキュメントが各タググループに対して持つタグの数が含まれています。 cの行がdの行と一致する場合、その文書はそのタググループのすべてのタグを持っていることを意味します。
select
d.document_id as "Document ID",
c.tags_in_group as "Matched Tag Group"
from d
inner join c on d.tag_group_id = c.tag_group_id
and d.document_tag_count = c.count_tags_in_group
このアプローチの優れた点の1つは、「これらの各タググループに50%以上のタグを持つドキュメントの数」などのレポートを実行できることです
select
d.document_id as "Document ID",
c.tags_in_group as "Matched Tag Group"
from d
inner join c on d.tag_group_id = c.tag_group_id
and d.document_tag_count >= 0.5 * c.count_tags_in_group