SQL Select : 하위 쿼리를 사용하여 3 개의 테이블간에 데이터를 결합하고 그룹화
문제
긴 질문에 대해 죄송하고 설명적인 제목이 아니지만 내 문제는 간단히 설명하기가 매우 어렵습니다.
세 가지 데이터베이스 테이블이 있습니다.
TABLE A:
AID PK
STATUS VARCHAR
TABLE B:
BID PK
AID FK
CID FK
TABLE C:
CID PK
CREATIONTIME DATE
각 상태에 대해 = 'OK'행에 테이블 AI의 최신 생성 시간이있는 C에서 해당 행을 찾으려고합니다.
먼저 테이블 A에서 모든 행을 가져올 수 있습니다.
다음으로 표 B에서 해당하는 모든 행을 가져올 수 있습니다.
하지만 거기에서 계속하는 방법?
예를 들어:
select AID, CID from B where AID in (select AID from A where STATUS = 'OK')
다음과 같은 것을 반환 할 수 있습니다.
AID, CID
1 1
2 2
2 3
3 4
4 5
4 6
CID 2가 CID 3과 CID 6보다 나중에 생성 시간을 가지고 있다고 가정 해 봅시다. 이는 올바른 결과가 표 C에서 행 1, 2, 4 및 6임을 의미합니다.
쿼리로 이것을 표현하는 방법이 있습니까?
편집 : 내가 충분히 구체적이지 않아서 죄송합니다. 내가 얻고 싶은 것은 테이블 C의 CID입니다.
편집 : 다른 솔루션으로 반품 행을 계산했습니다. 결과는 매우 흥미롭고 다각화되었습니다.
Hainstech : 298 473 행
Jmucchiello : 298 473 행
Russ Cam : 290 121 행
Chris : 344 093 행
Tyrannosaurs : 290 119 행
아직 반품 행을 깊이 분석 할 시간이 없었지만 쿼리 중 어느 것이 "파손 된"이유에 대한 견해에 정말 감사합니다.
해결책
내가 당신을 올바르게 이해했다면 이와 같은 것
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
편집하다:
이제 SQL Server에서 다음을 확인했습니다 (Oracle에서 동일한 결과를 epxect). CID
용 C
최대로 기록하십시오 CREATIONTIME
어디에 STATUS
관련 레코드의 경우 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
다음과 함께 시연되었습니다 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
결과를 초래합니다
CID
-----------
3
4
5
편집 2 :
다른 결과를 제공하는 각 진술에 대한 귀하의 의견에 대한 응답으로 위의 테스트 데이터를 사용하여 SQL Server 2005를 통해 다른 답변 중 일부를 실행했습니다 (Oracle을 사용해 주셔서 감사합니다). 결과는 다음과 같습니다
--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);
결과는 다음과 같습니다
--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
더 적은 수의 레코드에 대해 주어진 답변을 실행하는 것이 좋습니다. 결과 세트가 예상되는 것인지 확인할 수 있습니다.
다른 팁
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.
3 개의 테이블의 결합을 사용하여 원하는 필드를 선택한 다음 결과를 CreationDate가 가장 최근에 한 결과로 제한하십시오.
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);
편집 : 나의 이전 대답은 말도 안됩니다. 이것은 이제 완전한 재 작성입니다
이것은 실제로 내 SQL 생활을 통해 나를 괴롭힌 문제입니다. 내가 당신에게 줄 솔루션은 지옥처럼 지저분하지만 효과가 있으며 "그렇습니다. 이것은 지옥처럼 지저분하지만 그렇게하는 유일한 방법입니다"라고 말하거나 "아니요, 이걸 ... ".
불안감은 두 날짜에 참여한 것 같습니다. 여기서 일어나는 방식은 정확히 일치 할 것이기 때문에 문제가되지 않지만 (정확히 동일한 루트 데이터를 가지고 있음) 여전히 틀린 느낌이 듭니다 ...
어쨌든, 이것을 무너 뜨리려면, 당신은 두 단계로 이것을해야합니다.
1) 첫 번째는 [AID], [초기 창조 시간] 세트 세트를 반환하여 각 원조에 대한 최초의 창조 시간을 제공하는 것입니다.
2) 그런 다음 최신 제품을 사용하여 원하는 CID를 가져올 수 있습니다.
그래서 (1) 부분의 경우, 나는 개인적으로 물건을 깔끔하게 유지하기 위해 그것을하는 견해를 만들었습니다. 그것은 당신 이이 부분을 테스트하고 다른 것들과 합병하기 전에 작동하게 할 수 있습니다.
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
이 시점에서 상태를 고려하지 않았습니다.
그런 다음 Tablea (상태를 얻으려면) 및 TableB 및 Table (CID를 얻으려면)에이를 결합해야합니다. 모든 명백한 링크 (AID, CID)를 수행하고 테이블의 CreationTime 열을보기에서 최신 스테이션 타임 열에 가입해야합니다. 또한 다른 레코드에 대해 두 개의 기록이 동시에 생성 된 경우에 문제가 발생할 수있는 도구에 대한보기에 참여하는 것을 잊지 마십시오.
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'
나는 그것이 효과가 있다고 확신한다 - 나는 그것을 테스트하고, 데이터를 조정하고, 그것을 다시 테스트하고, 그것이 동작한다. 적어도 그것은 내가해야한다고 믿는 것을 수행합니다.
그러나 동일한 레코드에 대해 표 C에서 두 개의 동일한 창조 시간의 가능성을 다루지 않습니다. 나는 당신이 언젠가는 그것을 절대적으로 제한하는 것을 작성하지 않는 한, 이것이 일어나지 않아야한다고 생각합니다.
이를 위해서는 원하는 것을 가정해야합니다. 이 경우 일치하는 두 개의 CID가 있다면 오히려 더 높은 CID가있을 것입니다 (최신 정보가 가장 많을 것입니다).
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
그리고 그것은 당신을 위해 일해야한다고 믿습니다. 보기보다는 하나의 쿼리로 원한다면 :
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
(방금 쿼리에 뷰를 내장했습니다. 그렇지 않으면 교장이 정확히 동일합니다).
하위 쿼리가 필요하지 않으며 최신 CID 생성 시간을 결정하기위한 집계가 간단합니다.
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
당신이 정말로 당신의 행 세트에서 CreationTime을 원하지 않는다면, 당신은 그것을 서브 쿼리로 랩핑하고 프로젝션에서 떨어 뜨릴 수 있습니다.
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
웹 페이지에서 코딩하면 구문 실수를 실례합니다. 또한, 나는 MSSQL 사람이므로 Oracle World에서 이것에 대해 다른 것이 없기를 바랍니다 ..
제공 한 스키마는 CID 당 창조 시간의 독창성을 시행하지 않습니다. 동일한 창조 시간으로 주어진 원조 값에 매핑되는 두 가지 CID 값이 있다면 둘 다 출력됩니다. CID 쌍에 의존하면 CreationTime이 독특 해지면 제약으로 선언적으로 시행해야합니다.
내가 뭔가를 놓치고 있습니까? 무엇이 문제가 있습니까 :
편집 : 알겠습니다. 실제로 원조로 그룹화하고 싶어합니다.
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