我有三张桌子。所有都遵循CRUD模式进行插入和更新。我需要进行查询qhich根据另一个表的唯一值选择一个表中的所有最新更新,通过连接两次删除:(我在示例中的两个表上省略了CRUD结构)
表COPY_HISTORY
COPY_ID | DATA_ID | STATUS | TIME |
1 | A | open | 10:34 |
1 | A | locked | 10:37 |
2 | A | open | 10:38 |
3 | B | open | 11:29 |
4 | C | open | 10:37 |
5 | D | locked | 09:34 |
表DATA_SET
DATA_ID | LOCATION |
A | 88 |
B | 77 |
C | 88 |
D | 99 |
E | 88 |
F | 88 |
表COPY_RULES
LOCATION_FROM | LOCATION_TO
55 | 110
66 | 120
77 | 120
88 | 130
99 | 130
我需要做的是从复制规则表中获取每个LOCATION_TO的状态。如果执行副本,它将在副本历史记录中注册,副本将始终覆盖某个位置的所有数据(110,120,130)。
在这种情况下,位置110从未发生过复制,并且110根本不应该包括在返回的数据中。即使它存在于规则中,也是不相关的。 120将从66和77的数据集中接收副本。但是,我们尚未在66上设置数据,因此只有77个是相关的。 B位于77,数据集B具有在11:29插入的复制历史记录,状态为打开。由于这是属于120的唯一状态,因此状态为120打开,并且位置120可以覆盖。
然而,位置130从88和99接收副本。这意味着数据集A,C,D,E和F都将复制到130,因为它们都位于88或99.我们有两个A的复制历史日志,C和D各一个。意思是我只想要最新注册的状态,在本例中为10:38。
现在,我尝试根据LOCATION_TO从COPY_HISTORY中选择最大值,但我陷入僵局让LOCATION_TO变得独一无二
我尝试:
SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY
WHERE COPY_ID = ch.COPY_ID
AND ch.DATA_ID = ds.DATA_ID
AND ds.LOCATION = cr.LOCATION_FROM)
选择最大时间语句不完整。两条AND线根本没有任何作用。我唯一能做的就是摆脱COPY_HISTORY中的第一行,这意味着我可以获得每个COPY_ID的所有最大时间,但我无法弄清楚如何过滤它们以获得每个LOCATION_TO的最大值。
我设法从一个LOCATION_TO获得最大值,通过这样做:
SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME FROM COPY_HISTORY ch
JOIN DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
WHERE ch.TIME = (SELECT MAX(TIME) FROM COPY_HISTORY ch2, DATA_SET ds2, COPY_RULES cs2
WHERE ch2.DATA_ID = ds2.DATA_ID
AND ds2.LOCATION = cr2.LOCATION_FROM
AND cr2.LOCATION_TO = 2180)
但是,这并没有解决我获取所有LOCATION_TO的状态列表的问题。
期望的输出:
LOCATION_TO | STATUS | TIME |
120 | open | 11:29 |
130 | open | 10:38 |
COPY_HISTORY中的有效行将是第3行和第4行
这是对实际数据库结构的极端简化的尝试,因此在这个问题中存在拼写错误的风险。
DDL
create table #COPY_HISTORY (COPY_ID int, DATA_ID char(1), [STATUS] varchar(16), [TIME] time)
create table #DATA_SET (DATA_ID char(1), [LOCATION] int)
create table #COPY_RULES (LOCATION_FROM int, LOCATION_TO int)
insert into #COPY_HISTORY
values
(1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34')
insert into #DATA_SET
values
('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88)
insert into #COPY_RULES
values
(55,110),
(66,120),
(77,120),
(88,130),
(99,130)
我认为这个查询对你有用。正如Ryan在评论中提到的,您可以使用ROW_NUMBER分析函数按时间(ORDER BY)对您的位置记录(PARTITION BY)进行排名,然后仅返回每个位置的第一行(RowOrder = 1)。
SELECT * FROM (
SELECT cr.LOCATION_TO, ch.STATUS, ch.TIME, ROW_NUMBER() OVER (PARTITION BY cr.LOCATION_TO ORDER BY ch.TIME desc) AS RowOrder
FROM #COPY_HISTORY ch
JOIN #DATA_SET ds ON ch.DATA_ID = ds.DATA_ID
JOIN #COPY_RULES cr ON cr.LOCATION_FROM = ds.LOCATION
) ordered_set
WHERE RowOrder = 1
如果我正确得到逻辑:
DECLARE @COPY_HISTORY TABLE
(
COPY_ID INT NOT NULL,
DATA_ID CHAR(1) NOT NULL,
[STATUS] VARCHAR(50) NOT NULL,
[TIME] TIME NOT NULL
);
DECLARE @DATA_SET TABLE
(
DATA_ID CHAR(1) NOT NULL,
LOCATION INT NOT NULL
);
DECLARE @COPY_RULES TABLE
(
LOCATION_FROM INT NOT NULL,
LOCATION_TO INT NOT NULL
);
INSERT INTO @COPY_HISTORY
VALUES (1,'A','open','10:34'),
(1,'A','locked','10:37'),
(2,'A','open','10:38'),
(3,'B','open','11:29'),
(4,'C','open','10:37'),
(5,'D','locked','09:34');
INSERT INTO @DATA_SET
VALUES ('A',88),
('B',77),
('C',88),
('D',99),
('E',88),
('F',88);
INSERT INTO @COPY_RULES
VALUES (55,110),
(66,120),
(77,120),
(88,130),
(99,130);
WITH CTE
AS
(
SELECT CR.LOCATION_TO,
CH.STATUS,
CH.TIME,
ROW_NUMBER() OVER(PARTITION BY CR.LOCATION_TO ORDER BY CASE WHEN CH.Status = 'OPEN' THEN 1 ELSE 0 END DESC, CH.TIME DESC) AS RN
FROM @COPY_HISTORY AS CH
INNER
JOIN @DATA_SET AS DS
ON CH.DATA_ID = DS.DATA_ID
INNER
JOIN @COPY_RULES AS CR
ON CR.LOCATION_FROM = DS.LOCATION
)
SELECT C.LOCATION_TO,
C.STATUS,
C.TIME
FROM CTE AS C
WHERE RN = 1;