我有两个表正在尝试加入查询,但是我在获取所需输出时遇到了一些困难。
表:扫描
ScanId ScanTime
1 8:00
2 8:15
3 9:00
4 9:30
6 10:00
7 10:45
8 11:00
9 11:10
表:回应
ScanId RespA RespB
3 ABC X
7 DEF Y
9 GHI Z
Responses.ScanId上有一个引用Scans.ScanId的外键。
“回复”表可能会也可能没有“扫描”表中每行的相应行。
我需要做的是生成如下所示的输出,Scans表中的每一行都返回最近的先前响应值。
对于Scans.ScanId 1和2,没有先前的响应,因此Response列为空。
对于Scans.ScanId 3,4和6,最近的前一个RespA是ABC,RespB是X(ScanId = 3)
对于Scans.ScanId 7和8,最近的RespA是DEF,RespB是Y(ScanId = 7)
对于Scan.ScanId 9,最近的RespA是GHI,RespB是Z(ScanId = 9)
期望的输出:
ScanId ScanTime RespScanId RespA RespB
1 8:00 NULL NULL NULL
2 8:15 NULL NULL NULL
3 9:00 3 ABC X
4 9:30 3 ABC X
6 10:00 3 ABC X
7 10:45 7 DEF Y
8 11:00 7 DEF Y
9 11:10 9 GHI Z
我很难弄清楚如何为这个编写join子句。它需要在Sql Server 2005及更高版本上运行。
答案 0 :(得分:1)
Here's a solution using a CTE...
DECLARE @Scans TABLE (
ScanID INT,
ScanTime DATETIME
)
DECLARE @Responses TABLE (
ScanID INT,
RespA VARCHAR(50),
RespB VARCHAR(50)
)
INSERT INTO @Scans VALUES (1, '8:00')
INSERT INTO @Scans VALUES (2, '8:15')
INSERT INTO @Scans VALUES (3, '9:00')
INSERT INTO @Scans VALUES (4, '9:30')
INSERT INTO @Scans VALUES (6, '10:00')
INSERT INTO @Scans VALUES (7, '10:45')
INSERT INTO @Scans VALUES (8, '11:00')
INSERT INTO @Scans VALUES (9, '11:10')
INSERT INTO @Responses VALUES (3, 'ABC', 'X')
INSERT INTO @Responses VALUES (7, 'DEF', 'Y')
INSERT INTO @Responses VALUES (9, 'GHI', 'Z')
;WITH ResponsesScan AS
(
SELECT
r.*, s.ScanTime
FROM
@Responses r
JOIN @Scans s ON s.ScanID = r.ScanID
)
SELECT
s.ScanID,
s.ScanTime,
rs.ScanID AS RespScanId,
rs.RespA,
rs.RespB
FROM
@Scans s
LEFT JOIN ResponsesScan rs
ON rs.ScanID = (
SELECT TOP 1 ScanID
FROM ResponsesScan
WHERE ScanTime <= s.ScanTime
ORDER BY ScanTime DESC
)
答案 1 :(得分:0)
使用窗口函数:
SELECT S.ScanId, ScanTime, R.ScanId AS RespScanId, RespA, RespB
FROM Scans S
LEFT OUTER JOIN (SELECT ScanId,
RespA,
RespB,
LEAD(ScanId) OVER(ORDER BY ScanId) AS NextScanId
FROM Responses) R
ON S.ScanId >= R.ScanId
AND (S.ScanId < R.NextScanId OR R.NextScanID IS NULL);
根据结果集在PostgreSQL上返回:
scanid | scantime | respscanid | respa | respb
--------+----------+------------+-------+-------
1 | 08:00:00 | | |
2 | 08:15:00 | | |
3 | 09:00:00 | 3 | ABC | X
4 | 09:30:00 | 3 | ABC | X
6 | 10:00:00 | 3 | ABC | X
7 | 10:45:00 | 7 | DEF | y
8 | 11:00:00 | 7 | DEF | y
9 | 11:10:00 | 9 | GHI | Z
答案 2 :(得分:0)
我的内部查询获取扫描ID和它上面的最小扫描ID ..如果没有其他,则它具有NULL作为“NextScan”值。然后,我加入扫描。我对每个扫描ID都进行了限定,以便扫描ID至少是相关的扫描ID,但比后面的扫描更少。
SELECT
scans.scanID,
scans.scanTime,
rsp3.ScanID,
rsp3.RespA,
rsp3.RespB
from
scans
LEFT JOIN
( SELECT rsp.scanID,
( select MIN( rsp2.scanID )
from responses rsp2
where rsp2.scanID > rsp.ScanID ) as NextScan
from
responses rsp ) PQ
on Scans.ScanID >= PQ.ScanID
AND Scans.ScanID <= case when PQ.NextScan IS NULL
then PQ.ScanID
else PQ.NextScan -1
end
LEFT JOIN Responses rsp3
on PQ.ScanID = rsp3.ScanID
答案 3 :(得分:0)
select s.scanid, s.scantime, xjoin.RespScanId, r.respa, r.respb
from scans s
left join
-- begin trick
(select si.scanid, max(ri.scanid) as RespScanId
from scans si
left join responses ri on si.scanid >= ri.scanid
group by si.scanid) as xjoin
-- end trick
on s.scanid = xjoin.scanid
left join responses r on xjoin.RespScanId = r.scanid
答案 4 :(得分:0)
建立Michael Fredrickson的Common Table Expression,这是select查询的另一个版本。
虽然这个查询无疑更复杂,但它具有不同的性能特征(即它可能表现更好)。但是,如果索引ScanTime列,性能差异是微不足道的。
select
s.ScanId,
s.ScanTime,
rs.ScanId as RespScanId,
rs.RespA,
rs.RespB
from Scans s
left outer join ResponsesScan rs on
-- either there is a response for this specific scan
rs.ScanId = s.ScanId
or (
-- or there is a response from a previous scan
rs.ScanTime < s.ScanTime
-- and there does not exist any other response
and not exists (
select 1
from ResponsesScan rs2
where
-- for this specific scan
rs2.ScanId = s.ScanId
or (
-- or from a previous scan
rs2.ScanTime < s.ScanTime
-- that is later than that response
and rs2.ScanTime > rs.ScanTime
)
)
)