SQL: join: Finde alle Objekte, die bestimmte Anforderungen erfüllen

8

Ich habe eine Tabelle, die eine Liste von Objekten enthält und welche Anforderungen sie erfüllen. Dann habe ich eine Tabelle, die eine Liste von Aufgaben enthält und welche Anforderungen ein Objekt erfüllen muss, um die Aufgabe ausführen zu können. Ich möchte folgende Fragen stellen: Zeigen Sie mir bei gegebener Aufgabe alle Objekte, die diese Aufgabe ausführen können, und zeigen Sie mir bei gegebenem Objekt alle Aufgaben, die das Objekt ausführen kann:

Beispiel:

task_req Tabelle

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

In dieser Tabelle heißt es also, dass für die Ausführung von Aufgabe A die Anforderungen req1, req2 und req3 erforderlich sind.

obj_reqs Tabelle

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

Ich könnte also die Frage stellen: Welche Objekte können taskA ausführen? Die Antwort sollte nur eine Zeile sein:

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

weil obj2 das einzige ist, das die Anforderungen req1, req2, req3 erfüllt. Andere Frage: Welche Objekte können Aufgabe B ausführen? Die Antwort lautet keine, da es kein Objekt mit den Anforderungen req4, req5, req6 gibt. Die Abfrage sollte eine Handle-Logik sein, bei der eine Aufgabe von mehreren Objekten ausgeführt werden kann, indem mehrere Zeilen zurückgegeben werden.

Die Frage ist: Welche Abfrage macht das?

Mein Problem ist, dass ich es geschafft habe, eine solche Abfrage zu finden, aber es scheint mir zu kompliziert. Die Abfrage führt im Wesentlichen Folgendes aus: A) innere Verknüpfung task_reqs-Tabelle mit obj_reqs-Tabelle, Gruppieren nach Aufgaben und objs und Zählen unterschiedlicher Anforderungen, B) Auswählen von Aufgaben, Zählen (unterscheiden (Anforderungen)) von task_reqs-Gruppieren nach Aufgaben, C) innerer Join A und B. sowohl für die Aufgabe als auch für die Anzahl (verschieden (Anforderungen)).

Sicher gibt es einen einfacheren Weg, um diese Abfrage durchzuführen, oder?

Ich füge unter dem SQL-Code ein, um die Tabellen und meine Abfrage zu generieren.

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

und meine Anfrage:

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;

was zurückgibt:

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

Sicher gibt es einen einfacheren Weg.

cheif
quelle
Bezieht sich nicht auf Ihr Problem, aber: distinctist keine Funktion. Das Einschließen einer Spalte in distinctKlammern ändert nichts und ist nutzlos. count(distinct (a))ist das gleiche wiecount(distinct a)
a_horse_with_no_name

Antworten:

1

Hier ist ein möglicher einfacher Weg:

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)

Demo

rad
quelle
Dies ist meine bevorzugte Lösung. Würden Sie bitte ein SQL-Geigenbeispiel hinzufügen, wie es die anderen Antwortenden getan haben?
Cheif
Sicher. Jetzt wird es hinzugefügt.
Rad
2

Sie können dies mit einem Cross-Join der Tabellen tun:

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)

Siehe die Demo .
Ergebnisse:

| task  | object | n_reqs |
| ----- | ------ | ------ |
| taskA | obj2   | 3      |
Forpas
quelle
Ist Cross Join nicht wirklich schlecht?
cheif
Für 2 massive Tische ist es schlecht.
Forpas
1

Ihre Anfrage scheint in Ordnung zu sein. Ich glaube, dies wird kompliziert, egal wie Sie es verfolgen, da die Join-Kriterien und / oder wo Prädikate von beiden reqund der Anzahl der reqÜbereinstimmungen abhängen .

Fensterfunktionen können hier die Verarbeitungszeit verkürzen, da Sie einen Tabellenscan aus Ihrer ursprünglichen Abfrage entfernen können.

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 

Wenn Sie einen Index für haben obj_reqs.req, würde diese Abfrage meiner Meinung nach auch ziemlich schnell sein. Wenn Sie nur an einer bestimmten Aufgabe interessiert sind, können Sie diese der WHEREKlausel in der innersten Unterabfrage ( trqs) hinzufügen .

SQLFiddle hier

Das Umkehren dieser Logik funktioniert für Frage 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 hier

JNevill
quelle