SQL SELECT: Kombinieren und Gruppieren von Daten zwischen drei Tabellen mit Unterabfragen
Frage
Sorry für eine lange Frage und nicht sehr aussagekräftige Titel, aber mein Problem ist sehr schwierig, kurz zu erklären.
Ich habe drei Datenbanktabellen:
TABLE A:
AID PK
STATUS VARCHAR
TABLE B:
BID PK
AID FK
CID FK
TABLE C:
CID PK
CREATIONTIME DATE
Für jeden STATUS = ‚OK‘ Zeile in Tabelle A I die entsprechende Zeile in C finden will, die die neueste Kreation Zeit hat.
Zuerst kann ich alle Zeilen aus Tabelle A holen, wo STATUS = ‚OK‘.
Weiter kann ich alle entsprechenden Zeilen aus Tabelle B.
holen
Aber wie von dort weiter?
Zum Beispiel:
select AID, CID from B where AID in (select AID from A where STATUS = 'OK')
zurückkehren könnte so etwas wie:
AID, CID
1 1
2 2
2 3
3 4
4 5
4 6
Lassen Sie uns sagen, dass CID 2 hat später Erstellungszeit als CID 3 und CID 6 ist neuer als CID 5. Dies bedeutet, dass das richtige Ergebnis Reihen wäre 1, 2, 4 und 6 in Tabelle C.
Gibt es eine Möglichkeit, dies mit einer Abfrage auszudrücken?
EDIT: Sorry, dass ich nicht spezifisch genug war. Was will ich aus der Tabelle C ist die CIDs erhalten.
EDIT:
Ich zählte Reihen mit den verschiedenen Lösungen zurück. Die Ergebnisse waren sehr interessant - und diversifiziert:
HAINSTECH: 298 473 Zeilen
JMUCCHIELLO: 298 473 Zeilen
RUSS CAM: 290 121 Zeilen
CHRIS: 344 093 Zeilen
Tyrannosaurier: 290 119 Zeilen
Ich hatte noch nicht die Zeit zurückgegebenen Zeilen in der Tiefe zu analysieren, aber ich würde wirklich zu schätzen, Ansichten, auf denen der Abfragen ist „gebrochen“ und warum.
Lösung
So etwas, wenn ich Sie richtig verstanden habe
SELECT
MAX(CREATIONTIME),
A.AID
FROM
A
INNER JOIN
B
ON
A.AID = B.AID
INNER JOIN
C
ON
B.CID = C.CID
WHERE
A.STATUS = 'OK'
GROUP BY
A.AID
EDIT:
ich jetzt überprüft haben, die in SQL Server folgende (ich würde das gleiche Ergebnis in Oracle epxect), und es gibt die CID
für den C
Datensatz mit dem Maximum CREATIONTIME
wo die STATUS
für den verknüpften Datensatz in A
id 'OK'
.
SELECT C.CID
FROM
C C
INNER JOIN
B B
ON
C.CID = B.CID
INNER JOIN
(
SELECT
MAX(C.CREATIONTIME) CREATIONTIME,
A.AID
FROM
A A
INNER JOIN
B B
ON
A.AID = B.AID
INNER JOIN
C C
ON
B.CID = C.CID
WHERE
A.STATUS = 'OK'
GROUP BY
A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME
Nachgewiesene mit der folgenden T-SQL
DECLARE @A TABLE(AID INT IDENTITY(1,1), STATUS VARCHAR(10))
DECLARE @B TABLE(BID INT IDENTITY(1,1), AID INT, CID INT)
DECLARE @C TABLE(CID INT IDENTITY(1,1), CREATIONTIME DATETIME)
INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('NOT OK')
INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('NOT OK')
INSERT INTO @C VALUES ('10 MAR 2008')
INSERT INTO @C VALUES ('13 MAR 2008')
INSERT INTO @C VALUES ('15 MAR 2008')
INSERT INTO @C VALUES ('17 MAR 2008')
INSERT INTO @C VALUES ('21 MAR 2008')
INSERT INTO @B VALUES (1,1)
INSERT INTO @B VALUES (1,2)
INSERT INTO @B VALUES (1,3)
INSERT INTO @B VALUES (2,2)
INSERT INTO @B VALUES (2,3)
INSERT INTO @B VALUES (2,4)
INSERT INTO @B VALUES (3,3)
INSERT INTO @B VALUES (3,4)
INSERT INTO @B VALUES (3,5)
INSERT INTO @B VALUES (4,5)
INSERT INTO @B VALUES (4,1)
INSERT INTO @B VALUES (4,2)
SELECT C.CID
FROM
@C C
INNER JOIN
@B B
ON
C.CID = B.CID
INNER JOIN
(
SELECT
MAX(C.CREATIONTIME) CREATIONTIME,
A.AID
FROM
@A A
INNER JOIN
@B B
ON
A.AID = B.AID
INNER JOIN
@C C
ON
B.CID = C.CID
WHERE
A.STATUS = 'OK'
GROUP BY
A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME
Ergebnisse in der folgenden
CID
-----------
3
4
5
EDIT 2:
Als Antwort auf Ihre Kommentare über jede der Aussagen zu unterschiedlichen Ergebnissen zu geben, ich habe einige der verschiedenen Antworten hier über SQL Server 2005 lief meine Testdaten unter Verwendung von oben (I schätzen Sie Oracle). Hier sind die Ergebnisse
--Expected results for CIDs would be
--CID
-----------
--3
--4
--5
--As indicated in the comments next to the insert statements
DECLARE @A TABLE(AID INT IDENTITY(1,1), STATUS VARCHAR(10))
DECLARE @B TABLE(BID INT IDENTITY(1,1), AID INT, CID INT)
DECLARE @C TABLE(CID INT IDENTITY(1,1), CREATIONTIME DATETIME)
INSERT INTO @A VALUES ('OK') -- AID 1
INSERT INTO @A VALUES ('OK') -- AID 2
INSERT INTO @A VALUES ('NOT OK')
INSERT INTO @A VALUES ('OK') -- AID 4
INSERT INTO @A VALUES ('NOT OK')
INSERT INTO @C VALUES ('10 MAR 2008')
INSERT INTO @C VALUES ('13 MAR 2008')
INSERT INTO @C VALUES ('15 MAR 2008')
INSERT INTO @C VALUES ('17 MAR 2008')
INSERT INTO @C VALUES ('21 MAR 2008')
INSERT INTO @B VALUES (1,1)
INSERT INTO @B VALUES (1,2)
INSERT INTO @B VALUES (1,3) -- Will be CID 3 For AID 1
INSERT INTO @B VALUES (2,2)
INSERT INTO @B VALUES (2,3)
INSERT INTO @B VALUES (2,4) -- Will be CID 4 For AID 2
INSERT INTO @B VALUES (3,3)
INSERT INTO @B VALUES (3,4)
INSERT INTO @B VALUES (3,5)
INSERT INTO @B VALUES (4,5) -- Will be CID 5 FOR AID 4
INSERT INTO @B VALUES (4,1)
INSERT INTO @B VALUES (4,2)
-- Russ Cam
SELECT C.CID, ABC.CREATIONTIME
FROM
@C C
INNER JOIN
@B B
ON
C.CID = B.CID
INNER JOIN
(
SELECT
MAX(C.CREATIONTIME) CREATIONTIME,
A.AID
FROM
@A A
INNER JOIN
@B B
ON
A.AID = B.AID
INNER JOIN
@C C
ON
B.CID = C.CID
WHERE
A.STATUS = 'OK'
GROUP BY
A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME
-- Tyrannosaurs
select A.AID,
max(AggC.CREATIONTIME)
from @A A,
@B B,
( select C.CID,
max(C.CREATIONTIME) CREATIONTIME
from @C C
group by CID
) AggC
where A.AID = B.AID
and B.CID = AggC.CID
and A.Status = 'OK'
group by A.AID
-- jmucchiello
SELECT c.cid, max(c.creationtime)
FROM @B b, @C c
WHERE b.cid = c.cid
AND b.aid IN (SELECT a.aid FROM @A a WHERE status = 'OK')
GROUP BY c.cid
-- hainstech
SELECT agg.aid, agg.cid
FROM (
SELECT a.aid
,c.cid
,max(c.creationtime) as maxcCreationTime
FROM @C c INNER JOIN @B b ON b.cid = c.cid
INNER JOIN @A a on a.aid = b.aid
WHERE a.status = 'OK'
GROUP BY a.aid, c.cid
) as agg
--chris
SELECT A.AID, C.CID, C.CREATIONTIME
FROM @A A, @B B, @C C
WHERE A.STATUS = 'OK'
AND A.AID = B.AID
AND B.CID = C.CID
AND C.CREATIONTIME =
(SELECT MAX(C2.CREATIONTIME)
FROM @C C2, @B B2
WHERE B2.AID = A.AID
AND C2.CID = B2.CID);
Die Ergebnisse sind wie folgt
--Russ Cam - Correct CIDs (I have added in the CREATIONTIME for reference)
CID CREATIONTIME
----------- -----------------------
3 2008-03-15 00:00:00.000
4 2008-03-17 00:00:00.000
5 2008-03-21 00:00:00.000
--Tyrannosaurs - No CIDs in the resultset
AID
----------- -----------------------
1 2008-03-15 00:00:00.000
2 2008-03-17 00:00:00.000
4 2008-03-21 00:00:00.000
--jmucchiello - Incorrect CIDs in the resultset
cid
----------- -----------------------
1 2008-03-10 00:00:00.000
2 2008-03-13 00:00:00.000
3 2008-03-15 00:00:00.000
4 2008-03-17 00:00:00.000
5 2008-03-21 00:00:00.000
--hainstech - Too many CIDs in the resultset, which CID has the MAX(CREATIONTIME) for each AID?
aid cid
----------- -----------
1 1
1 2
1 3
2 2
2 3
2 4
4 1
4 2
4 5
--chris - Correct CIDs, it is the same SQL as mine
AID CID CREATIONTIME
----------- ----------- -----------------------
1 3 2008-03-15 00:00:00.000
2 4 2008-03-17 00:00:00.000
4 5 2008-03-21 00:00:00.000
Ich würde empfehlen, jede der gegebenen Antworten gegen eine kleinere Anzahl von Datensätzen ausgeführt wird, so dass Sie, ob die Suchresultates zurück feststellen kann die erwartete ist.
Andere Tipps
SQL> create table a (aid,status)
2 as
3 select 1, 'OK' from dual union all
4 select 2, 'OK' from dual union all
5 select 3, 'OK' from dual union all
6 select 4, 'OK' from dual union all
7 select 5, 'NOK' from dual
8 /
Tabel is aangemaakt.
SQL> create table c (cid,creationtime)
2 as
3 select 1, sysdate - 1 from dual union all
4 select 2, sysdate - 2 from dual union all
5 select 3, sysdate - 3 from dual union all
6 select 4, sysdate - 4 from dual union all
7 select 5, sysdate - 6 from dual union all
8 select 6, sysdate - 5 from dual
9 /
Tabel is aangemaakt.
SQL> create table b (bid,aid,cid)
2 as
3 select 1, 1, 1 from dual union all
4 select 2, 2, 2 from dual union all
5 select 3, 2, 3 from dual union all
6 select 4, 3, 4 from dual union all
7 select 5, 4, 5 from dual union all
8 select 6, 4, 6 from dual union all
9 select 7, 5, 6 from dual
10 /
Tabel is aangemaakt.
SQL> select a.aid
2 , max(c.cid) keep (dense_rank last order by c.creationtime) cid
3 , max(c.creationtime) creationtime
4 from a
5 , b
6 , c
7 where b.aid = a.aid
8 and b.cid = c.cid
9 and a.status = 'OK'
10 group by a.aid
11 /
AID CID CREATIONTIME
---------- ---------- -------------------
1 1 30-04-2009 09:26:00
2 2 29-04-2009 09:26:00
3 4 27-04-2009 09:26:00
4 6 26-04-2009 09:26:00
4 rijen zijn geselecteerd.
Wählen Sie das Feld, das Sie suchen eine Verknüpfung aller drei Tabellen und begrenzen die Ergebnisse dann zu denen, wo die creation die jüngste ist.
SELECT A.AID, C.CID, C.CREATIONTIME
FROM A A, B B, C C
WHERE A.STATUS = 'OK'
AND A.AID = B.AID
AND B.CID = C.CID
AND C.CREATIONTIME =
(SELECT MAX(C2.CREATIONTIME)
FROM C C2, B B2
WHERE B2.AID = A.AID
AND C2.CID = B2.CID);
EDIT: Meine bisherige Antwort war Unsinn. Dies ist nun eine komplette Neufassung
Dies ist tatsächlich ein Problem, das mich während meiner gesamten SQL Leben abgehört hat. Die Lösung, die ich werde euch geben, ist wie die Hölle chaotisch, aber es funktioniert und ich würde schätzen jemand entweder sagen „ja, das wie die Hölle ist etwas chaotisch, aber es ist der einzige Weg, es zu tun“, oder sagen: „Nein, dies zu tun ... “.
Ich denke, das Unbehagen von der Teilnahme an zwei Terminen kommt. So wie es hier geschieht, es ist kein Problem, da sie eine genaue Übereinstimmung sein (sie haben genau die gleichen Stammdaten), aber es fühlt sich immer noch falsch ...
Wie auch immer, diese nach unten zu brechen, müssen Sie dies in zwei Stufen tun.
1) Die erste ist eine Ergebnismenge [HILFE] zurückzukehren, [früheste Creation] geben Sie die früheste creation für jede AID.
2) Sie können dann latestCreationTime verwenden, um die CID Sie wollen ziehen.
So zum Teil (1), würde ich persönlich eine Ansicht erstellen, es zu tun nur Dinge ordentlich zu halten. Es ermöglicht Ihnen, diesen Teil zu testen und bekommen es funktioniert, bevor Sie es mit den anderen Sachen zusammenführen.
create view LatestCreationTimes
as
select b.AID,
max(c.CreationTime) LatestCreationTime
from TableB b,
TableC c
where b.CID = c.CID
group by b.AID
Beachten Sie, haben wir nicht berücksichtigt den Status an dieser Stelle.
Sie dann beitreten müssen, dass zu TableA (um den Status) und TableB und TableC (um die CID zu erhalten). Sie müssen alle offensichtlichen Verbindungen (AID, CID) zu tun und auch die LatestCreationTime Spalte in der Ansicht auf die Spalte in Creation TableC zu verbinden. Sie auch nicht vergessen, den Blick auf AID beizutreten sonst wo zwei Datensätze werden zur gleichen Zeit für verschiedene A-Datensätze erstellt werden Sie Probleme bekommen.
select A.AID,
C.CID
from TableA a,
TableB b,
TableC c,
LatestCreationTimes lct
where a.AID = b.AID
and b.CID = c.CID
and a.AID = lct.AID
and c.CreationTime = lct.LatestCreationTime
and a.STATUS = 'OK'
Ich bin sicher, das funktioniert - ich habe es ausprobiert, gezwickt Daten, erneut getestet, und es verhält. Wenigstens tut es das, was ich es soll tun glauben.
Es ist jedoch nicht mit der Möglichkeit von zwei identischen CreationTimes in Tabelle C für denselben Datensatz beschäftigen. Ich vermute, dass dies jedoch nicht geschehen soll, wenn Sie irgendwann geschrieben haben, dass es absolut zwingt es braucht zu bilanzieren.
Um dies zu tun, muss ich eine Annahme machen, über die man Sie bevorzugen würden. In diesem Fall werde ich sagen, dass wenn es zwei CIDs sind, die übereinstimmen, würden Sie lieber die höhere haben (es ist höchstwahrscheinlich mehr auf dem neuesten Stand).
select A.AID,
max(C.CID) CID
from TableA a,
TableB b,
TableC c,
LatestCreationTimes lct
where a.AID = b.AID
and b.CID = c.CID
and c.CreationTime = lct.LatestCreationTime
and a.STATUS = 'OK'
group by A.AID
Und das, glaube ich, sollte für Sie arbeiten. Wenn Sie es als eine Abfrage wollen, anstatt mit der Ansicht, dann:
select A.AID,
max(C.CID) CID
from TableA a,
TableB b,
TableC c,
(select b.AID,
max(c.CreationTime) LatestCreationTime
from TableB b,
TableC c
where b.CID = c.CID
group by b.AID) lct
where a.AID = b.AID
and b.CID = c.CID
and c.CreationTime = lct.LatestCreationTime
and a.STATUS = 'OK'
group by A.AID
(Ich habe gerade die Ansicht in der Abfrage eingebettet, da sonst das Haupt ist genau das gleiche).
Es besteht keine Notwendigkeit für eine Unterabfrage, die Aggregation der neueste cid Erstellungszeit ist einfach zu bestimmen:
SELECT a.aid
,c.cid
,max(c.creationtime) as maxcCreationTime
FROM c INNER JOIN b ON b.cid = c.cid
INNER JOIN a on a.aid = b.aid
WHERE a.status = 'OK'
GROUP BY a.aid, c.cid
Wenn Sie wirklich nicht die creation in Ihrer Zeile setzen möchten, können Sie einfach wickeln in einer Unterabfrage und legen Sie es aus der Projektion:
SELECT agg.aid, agg.cid
FROM (
SELECT a.aid
,c.cid
,max(c.creationtime) as maxcCreationTime
FROM c INNER JOIN b ON b.cid = c.cid
INNER JOIN a on a.aid = b.aid
WHERE a.status = 'OK'
GROUP BY a.aid, c.cid
) as agg
Codierung in der Web-Seite, entschuldigen Sie bitte Syntaxfehler. Auch ich bin ein mssql Kerl so dass ich hoffe, es ist nichts anderes in der Oracle-Welt für diesen ..
Beachten Sie, dass das Schema, das Sie zur Verfügung gestellt haben, nicht Einzigartigkeit CREATION pro cid erzwingen. Wenn es je zwei cid Werte sind, die mit dem gleichen creation zu einem bestimmten Beihilfewert zuzuordnen, werden sie beide ausgegeben werden. Wenn Sie auf das Paar cid verlassen, creation einzigartig sein, können Sie es deklarativ mit einer Einschränkung erzwingen sollte.
Bin ich etwas fehlt? Was ist falsch an:
EDIT: Okay, ich sehe, Sie tatsächlich mit Hilfe gruppieren möchten
.SELECT c.cid FROM b, c,
(SELECT b.aid as aid, max(c.creationtime) as creationtime
FROM b, c
WHERE b.cid = c.cid
AND b.aid IN (SELECT a.aid FROM a WHERE status = 'OK')
GROUP BY b.aid) as z
WHERE b.cid = c.cid
AND z.aid = b.aid
AND z.creationtime = c.creationtime