很抱歉这是一个很长的问题而不是一个非常具有描述性的标题,但我的问题很难简单解释。
我有三个数据库表:
TABLE A:
AID PK
STATUS VARCHAR
TABLE B:
BID PK
AID FK
CID FK
TABLE C:
CID PK
CREATIONTIME DATE
对于表A中的每个STATUS ='OK'行,我想在C中找到具有最新创建时间的相应行。
首先,我可以从表A中获取STATUS ='OK'的所有行
接下来,我可以从表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比CID 5更新。这意味着正确的结果将是表C中的第1,2,4和6行。
有没有办法用查询表达这个?
编辑: 对不起,我不够具体。我想得到的是表C中的CID。
编辑:
我计算了返回的行与不同的解决方案。结果非常有趣 - 并且多样化:
HAINSTECH:298 473行
JMUCCHIELLO:298 473行
RUSS CAM:290 121行
克里斯:344 093行
TYRANNOSAURS:290 119行
我还没有时间深入分析返回的行,但我真的很欣赏有关哪些查询被“破坏”的原因以及原因。
答案 0 :(得分:4)
这样的事情,如果我理解正确的话
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中查看相同的结果)并返回CID
记录的C
,其中CREATIONTIME
最大值为STATUS
{1}} A
中的相关记录{1}}。
'OK'
使用以下 T-SQL
进行演示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
以下结果
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
编辑2:
在回复您对每个语句给出不同结果的评论时,我通过SQL Server 2005使用上面的测试数据运行了一些不同的答案(我感谢您使用的是Oracle)。结果如下
CID
-----------
3
4
5
结果如下
--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);
我建议针对较少数量的记录运行每个给定的答案,以便您可以确定返回的结果集是否是预期的结果。
答案 1 :(得分:1)
这实际上是一个在我的SQL生活中一直困扰着我的问题。我要给你的解决方案是乱糟糟的,但是它有效并且我会感激任何人都说“是的,这是混乱的,但这是唯一的方法”或者说“不,做这个... ”
我认为这种不安来自加入两个日期。它在这里发生的方式不是问题,因为它们将完全匹配(它们具有完全相同的根数据)但它仍然感觉不对...
无论如何,打破这个,你需要分两个阶段。
1)第一个是返回结果集[AID],[最早的CreationTime]为每个AID提供最早的创建时间。
2)然后,您可以使用latestCreationTime来提取所需的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和TableC(以获取CID)。您需要执行所有明显的链接(AID,CID),并将视图中的LatestCreationTime列连接到TableC中的CreationTime列。不要忘记加入AID上的视图,否则在同一时间为不同的A记录创建了两条记录,你会遇到问题。
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中两个相同CreationTimes对同一记录的可能性。我猜这不应该发生,但是除非你写了一些绝对限制它的时间,否则需要考虑它。
要做到这一点,我需要假设你更喜欢哪一个。在这种情况下,我会说,如果有两个匹配的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
(我刚刚在查询中嵌入了视图,否则主体完全相同)。
答案 2 :(得分:1)
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 :(得分:1)
使用所有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);
答案 4 :(得分:0)
我错过了什么吗?有什么问题:
编辑:好的,我发现你确实希望通过援助进行分组。
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
答案 5 :(得分:0)
不需要子查询,确定最新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
如果您真的不想在行集中创建时间,可以将其包装在子查询中并将其从投影中删除:
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世界没有什么不同的。
请注意,您提供的架构并未强制每个cid的CREATIONTIME唯一性。如果有两个cid值映射到具有相同创建时间的给定辅助值,则它们都将被输出。如果你依赖这对cid,创建时间是唯一的,你应该用约束声明性地强制执行它。