假设我有两个如下所示的数据表:
Dv测试结果
DVTR_DeviceNo | DVTR_TestedOnAt | DVTR_TesterNo |
---|---|---|
DV00001 | 2022-08-11 14:15:16.000 | 0001 |
DV00001 | 2022-08-19 21:08:16.000 | 空 |
DV00001 | 2022-09-22 08:14:32.000 | 空 |
DV00002 | 2023-06-03 18:18:03.000 | 空 |
DV00002 | 2023-08-15 19:01:36.000 | 0007 |
DV00003 | 2022-12-23 08:04:47.000 | 0014 |
DV00003 | 2023-01-03 10:09:51.000 | 0014 |
DV00003 | 2023-01-09 08:01:33.000 | 0014 |
DV00004 | 2023-03-14 11:49:02.000 | 0298 |
DV00004 | 2023-03-15 09:08:13.000 | 0298 |
DV00005 | 2022-04-28 16:23:14.000 | 空 |
DV00005 | 2022-08-14 08:20:56.000 | 空 |
测试员
T_TesterNo | T_TesterName |
---|---|
0001 | 约翰 |
0007 | 斯泰西 |
0014 | 詹姆斯 |
0298 | 卡洛斯 |
我想找到最后一次测试每个设备的时间以及谁测试了它,按设备编号排序,没有任何设备编号重复。
我有以下代码:
SELECT DvTestResults.DVTR_DeviceNo, max.LastTimeTested, Tester.T_TesterName
FROM DvTestResults
INNER JOIN
(
SELECT DVTR_DeviceNo, MAX(DVTR_TestedOnAt) as LastTimeTested
FROM DvTestResults
GROUP BY DVTR_DeviceNo
) as max
on max.DVTR_DeviceNo = DvTestResults.DVTR_DeviceNo and max.LastTimeTested = DvTestResults.DVTR_TestedOnAt
INNER JOIN Tester ON Tester.TesterNo = DvTestResults.DVTR_TesterNo
ORDER BY DvTestResults.DVTR_DeviceNo
不幸的是,虽然代码在最后一个测试的 DVTR_TesterNo 不为 NULL 的单元上工作正常,但它没有给出它何时为 的值。如果我想列出最后一次测试设备编号以及谁测试了即使它出现了NULL,那将是一个很好的解决方案。优选地,就像本示例中的 DV00001 一样,测试人员通常与最后一个测试它的人(在本例中为 John)同名——即使他们没有登录进行 DV00001 所做的最后一个测试。所以,对于这个例子,我想要 DV00001 的输出:
DVTR_DeviceNo | 最后一次测试 | T_TesterName |
---|---|---|
DV00001 | 2022-09-22 08:14:32.000 | 约翰 |
对于像 DV00005 这样只曾经 匿名测试过的设备,我想要一个输出:
DVTR_DeviceNo | 最后一次测试 | T_TesterName |
---|---|---|
DV00005 | 2022-08-14 08:20:56.000 | 匿名 |
有人能帮忙吗?
获取每个设备的最新测试是一个典型的每组前 1 问题,我们可以通过
row_number()
和过滤来解决这个问题:
select *
from (
select r.*,
row_number() over(partition by DVTR_DeviceNo order by LastTimeTested) rn
from DvTestResults r
) r
where rn = 1
然后我们会
left join
在测试人员的桌子上尝试并带来测试人员的名字。
优选地,就像本例中的 DV00001 一样,测试人员通常与最后测试它的人(在本例中为 John)同名
检索最新的测试器(因此忽略空值)是 SQL Server 中的一项更复杂的任务。我们可以使用
apply
,或者更多的窗口函数。后者将是:
select r.DVTR_DeviceNo, r.DVTR_TestedOnAt, coalesce(t.T_TestName, 'Anonymous') T_TesterName
from (
select r.*,
max(DVTR_TesterNo) over(partition by DVTR_DeviceNo, grp) LastDVTR_TesterNo
from (
select r.*,
row_number() over(partition by DVTR_DeviceNo order by LastTimeTested) rn,
count(DVTR_TesterNo) over(partition by DVTR_DeviceNo order by LastTimeTested) grp
from DvTestResults r
) r
) r
left join Tester t on t.T_TesterNo = r.LastDVTR_TesterNo
where r.rn = 1
相关:How to make
LAG()
ignore NULL
s in SQL Server
根据我对问题的新理解,这应该可行:
SELECT DVTR_DeviceNo, MAX(DVTR_TestedOnAt) As DVTR_TestedOnAt
, coalesce(
(
SELECT T_TestName
FROM (
SELECT DVTR_TesterNo, DVTR_DeviceNo
, row_number() over
(PARTITION BY DVTR_DeviceNo
ORDER BY case when DVTR_TesterNo IS NULL THEN 1 ELSE 0 END
,DVTR_TestedOnAt DESC) rn
FROM DvTestResults
) dtr0
LEFT JOIN Tester t ON t.T_TesterNo = dtr0.DVTR_TesterNo
WHERE dtr0.rn = 1 AND dtr0.DVTR_DeviceNo = dtr.DVTR_DeviceNo
)
, 'Anonymous') T_TestName
FROM DvTestRestuls dtr
GROUP BY DVTR_DeviceNo
对于 SQL Server 2022,我将使用
LAST_VALUE(dvtr_testerno IGNORE NULLS) OVER()
在 DVTestResults
的子查询(我称该子查询为 w_testerno
)中尽可能回填缺失的外键,然后与测试人员一起加入该子查询;由于t_testername
:,
IFNULL()
的结果 NULL 变为“匿名”
WITH
-- your input
dvtestresults(dvtr_deviceno,dvtr_testedonat,dvtr_testerno) AS (
SELECT 'DV00001',TIMESTAMP '2022-08-11 14:15:16.000',0001
UNION ALL SELECT 'DV00001',TIMESTAMP '2022-08-19 21:08:16.000',NULL
UNION ALL SELECT 'DV00001',TIMESTAMP '2022-09-22 08:14:32.000',NULL
UNION ALL SELECT 'DV00002',TIMESTAMP '2023-06-03 18:18:03.000',NULL
UNION ALL SELECT 'DV00002',TIMESTAMP '2023-08-15 19:01:36.000',0007
UNION ALL SELECT 'DV00003',TIMESTAMP '2022-12-23 08:04:47.000',0014
UNION ALL SELECT 'DV00003',TIMESTAMP '2023-01-03 10:09:51.000',0014
UNION ALL SELECT 'DV00003',TIMESTAMP '2023-01-09 08:01:33.000',0014
UNION ALL SELECT 'DV00004',TIMESTAMP '2023-03-14 11:49:02.000',0298
UNION ALL SELECT 'DV00004',TIMESTAMP '2023-03-15 09:08:13.000',0298
UNION ALL SELECT 'DV00005',TIMESTAMP '2022-04-28 16:23:14.000',NULL
UNION ALL SELECT 'DV00005',TIMESTAMP '2022-08-14 08:20:56.000',NULL
)
,
tester(t_testerno,t_testername) aS (
SELECT 0001,'John'
UNION ALL SELECT 0007,'Stacy'
UNION ALL SELECT 0014,'James'
UNION ALL SELECT 0298,'Carlos'
)
-- end of your input. Query starts here, replace following comma with "WITH"
,
w_testerno AS (
SELECT
dvtr_deviceno
, dvtr_testedonat
, LAST_VALUE(dvtr_testerno IGNORE NULLS) OVER(
PARTITION BY dvtr_deviceno ORDER BY dvtr_testedonat
) AS dvtr_testerno
FROM dvtestresults
)
SELECT
dvtr_deviceno
, dvtr_testedonat
, IFNULL(t_testername,'Anonymous') AS t_testermane
FROM w_testerno
LEFT JOIN tester ON t_testerno = dvtr_testerno
ORDER BY 1,2;
dvtr_deviceno | dvtr_testedonat | t_testermane |
---|---|---|
DV00001 | 2022-08-11 14:15:16 | 约翰 |
DV00001 | 2022-08-19 21:08:16 | 约翰 |
DV00001 | 2022-09-22 08:14:32 | 约翰 |
DV00002 | 2023-06-03 18:18:03 | 匿名 |
DV00002 | 2023-08-15 19:01:36 | 斯泰西 |
DV00003 | 2022-12-23 08:04:47 | 詹姆斯 |
DV00003 | 2023-01-03 10:09 7621 :51 | 詹姆斯 |
DV00003 | 2023-01-09 08:01:33 | 詹姆斯 |
DV00004 | 2023-03-14 11:49:02 | 卡洛斯 |
DV00004 | 2023-03-15 09:08:13 | 卡洛斯 |
DV00005 | 2022-04-28 16:23:14 | 匿名 |
DV00005 | 2022-08-14 08:20:56 | 匿名 |