SQL:結合:いくつかの要件を満たすすべてのオブジェクトを検索します


8

オブジェクトのリストとそれらが満たす要件を含むテーブルがあります。次に、タスクのリストと、タスクを実行するためにオブジェクトが満たす必要のある要件を含むテーブルがあります。クエリを実行します。タスクを指定すると、そのタスクを実行できるすべてのオブジェクトが表示され、オブジェクトを指定すると、そのオブジェクトが実行できるすべてのタスクが表示されます。

例:

task_reqテーブル

tasks   |    reqs
-----------------
taskA   |    req1
taskA   |    req2
taskA   |    req3
taskB   |    req4
taskB   |    req5
taskB   |    req6

したがって、この表は、taskAを実行するには、要件req1、req2、およびreq3が必要であることを示しています。

obj_reqsテーブル

object  |   reqs
----------------
obj1    |   req3
obj1    |   req4
obj2    |   req1
obj2    |   req2
obj2    |   req3
obj2    |   req4

だから私は質問をすることができます:どのオブジェクトがtaskAを実行できますか?答えは1行だけです。

tasks   |   objects
-------------------
taskA   |   object2

obj2は要件req1、req2、req3を満たす唯一のものだからです。別の質問:どのオブジェクトがtaskBを実行できますか?要件はreq4、req5、req6のオブジェクトがないため、答えはありません。クエリは、複数の行を返すことによって1つのタスクを複数のオブジェクトで実行できるロジックを処理する必要があります。

問題は次のとおりです。これはどのクエリですか。

私の問題は、そのようなクエリをなんとか見つけることができたことですが、複雑すぎるようです。クエリは基本的には次のとおりです。A)obj_reqsテーブルを含む内部結合task_reqsテーブル、タスクとobjでグループ化し、個別の要件をカウントします。B)タスクを選択し、タスクごとにgroup_task(distinct(reqs))からグループ化します。 taskとcount(distinct(reqs))の両方で。

このクエリを実行する簡単な方法は確かにありますよね?

SQLコードの下に貼り付けて、テーブルとクエリを生成しています。

create table task_reqs (task varchar, req varchar);
create table obj_reqs (object varchar, req varchar);
insert into task_reqs values ('taskA', 'req1');
insert into task_reqs values ('taskA', 'req2');
insert into task_reqs values ('taskA', 'req3');
insert into task_reqs values ('taskB', 'req4');
insert into task_reqs values ('taskB', 'req5');
insert into task_reqs values ('taskB', 'req6');
insert into obj_reqs values ('obj1','req1');
insert into obj_reqs values ('obj1','req3');
insert into obj_reqs values ('obj2','req1');
insert into obj_reqs values ('obj2','req2');
insert into obj_reqs values ('obj2','req3');
insert into obj_reqs values ('obj2','req4');

そして私のクエリ:

select t.task,t.object,n.n_reqs
from (
    select task,object,count(distinct(obj_reqs.req)) as n_reqs
    from task_reqs
    inner join obj_reqs on task_reqs.req=obj_reqs.req
    group by task,object
) t
inner join (
    select task,count(distinct(req)) as n_reqs
    from task_reqs
    group by task
) n
on n.n_reqs=t.n_reqs and n.task=t.task;

これは次を返します:

 task  | object | n_reqs 
-------+--------+--------
 taskA | obj2   |      3

確かにもっと簡単な方法があります。


問題とは無関係distinctです、関数ではありませんdistinctかっこで使用される列を囲むことは何も変更せず、役に立ちません。count(distinct (a))と同じですcount(distinct a)
a_horse_with_no_name

回答:


1

これが可能な簡単な方法です:

select t.task, o.object, count(t.req) n_reqs
  from task_reqs t left join obj_reqs o on t.req = o.req
  group by t.task, o.object
  having o.object is not null and count(t.req) = (select count(req) from task_reqs where 
  task = t.task)

デモ


これが私の推奨する解決策です。他のレスポンダのように、SQLフィドルの例を追加していただけますか?
シェフ、

承知しました。追加されました。
rad

2

テーブルのクロス結合でそれを行うことができます:

select t.task, o.object, count(distinct t.req) n_reqs 
from task_reqs t cross join obj_reqs o
where t.task = 'taskA'
group by t.task, o.object
having count(distinct t.req) = count(case when t.req = o.req then 1 end)

デモをご覧ください。
結果:

| task  | object | n_reqs |
| ----- | ------ | ------ |
| taskA | obj2   | 3      |

クロスジョインは本当に悪いのではないですか?
シェフ、

2つの大規模なテーブルの場合、それは良くありません。
forpas

1

クエリは問題ないようです。これは、結合基準や述部が両方reqreq一致の数の両方に依存するようになるので、どのように処理しても複雑になると思います。

元のクエリからテーブルスキャンを削除できるため、ウィンドウ関数ここで処理時間を短縮できます。

SELECT DISTINCT task, object
FROM
  (
    SELECT task, 
      object, 
      COUNT(*) OVER (PARTITION BY task, object) matchCount,
      trqs.reqCount
    FROM (SELECT task, req, count(*) OVER (PARTITION BY task) as reqcount FROM task_reqs) trqs
      INNER JOIN obj_reqs orqs
        ON trqs.req = orqs.req
   ) taskreqcounter
WHERE matchCount = reqCount 

あなたがインデックスを持っているobj_reqs.reqなら、このクエリもかなり速いと思うでしょう。特定のタスクのみに関心がある場合は、それをWHERE最も内側のサブクエリ(trqs)の句に追加できます。

ここSQLFiddle

このロジックを逆にすると、質問2で機能します

SELECT DISTINCT task, object
FROM
  (
    SELECT task, 
      object, 
      COUNT(*) OVER (PARTITION BY task, object) matchCount,
      orqs.reqCount
    FROM (SELECT object, req, count(*) OVER (PARTITION BY object) as reqcount FROM obj_reqs) orqs
      INNER JOIN task_reqs trqs
        ON orqs.req = trqs.req
   ) taskreqcounter
WHERE matchCount = reqCount

ここSQLFiddle

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.