Sql server 如何根据联接表上的唯一值从表中选择最大值?
我有三张桌子。插入和更新都遵循CRUD模式。我需要做一个查询,根据另一个表的唯一值选择一个表中的所有最新更新,并通过联接两次删除:(我在示例中省略了两个表上的CRUD结构) 表格副本\u历史记录Sql server 如何根据联接表上的唯一值从表中选择最大值?,sql-server,Sql Server,我有三张桌子。插入和更新都遵循CRUD模式。我需要做一个查询,根据另一个表的唯一值选择一个表中的所有最新更新,并通过联接两次删除:(我在示例中省略了两个表上的CRUD结构) 表格副本\u历史记录 COPY_ID | DATA_ID | STATUS | TIME | 1 | A | open | 10:34 | 1 | A | locked | 10:37 | 2 | A | open | 10:3
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_ID | LOCATION |
A | 88 |
B | 77 |
C | 88 |
D | 99 |
E | 88 |
F | 88 |
表格复制规则
LOCATION_FROM | LOCATION_TO
55 | 110
66 | 120
77 | 120
88 | 130
99 | 130
我需要做的是从复制规则表中获取每个位置的状态。如果执行复制,它将在复制历史记录中注册,并且复制将始终覆盖某个位置的所有数据(110120130)
在这种情况下,位置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
现在,我已尝试根据位置选择“复制历史记录”中的最大值,但我在使位置唯一化方面遇到了僵局
我尝试:
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)
select max time语句不完整。这两条线一点作用也没有。我唯一能做的就是去掉COPY_HISTORY中的第一行,这意味着我可以获得每个COPY_ID的所有最大时间,但我不知道如何过滤它们,从而只获得每个位置的最大时间
通过执行以下操作,我成功地从一个位置获得了最大值:
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 | STATUS | TIME |
120 | open | 11:29 |
130 | open | 10:38 |
副本历史记录中的有效行为第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)
如果我的逻辑正确:
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;
如果我的逻辑正确:
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;
我想这个问题对你会有用的。正如Ryan在评论中提到的,您可以使用ROW_NUMBER分析函数按时间(ORDER BY)对位置记录(PARTITION BY)进行排序,然后只返回每个位置的第一行(ROW ORDER=1)
我想这个问题对你会有用的。正如Ryan在评论中提到的,您可以使用ROW_NUMBER分析函数按时间(ORDER BY)对位置记录(PARTITION BY)进行排序,然后只返回每个位置的第一行(ROW ORDER=1)
对逻辑的描述不是坏事,但它可能有助于您的问题根据上面显示的示例表显示准确的预期输出。使用
ROW\u NUMBER()OVER(PARTITION BY)
按降序排列记录并对其编号,然后抓取行数等于1的每条记录,我已经更清楚地指定了所需的输出。感谢您的输入。我想在上一个示例中,通过将cr2.LOCATION\u切换为=2180,将cr2.LOCATION\u切换为=cr.LOCATION\u,我可能已经了解了一些东西,但我已经为此奋斗了很长时间,即使它似乎给出了期望的结果,我不相信我做得对。@RyanWilson很遗憾,我不理解上下文。对逻辑的描述不是一件坏事,但它可能有助于你的问题根据上面显示的示例表显示准确的预期输出。使用ROW_NUMBER()OVER(PARTITION BY)
若要按降序对记录进行排序并对其编号,只需抓取行数等于1的每条记录,我已更清楚地指定了所需的输出。感谢您的输入。我想在上一个示例中,通过将cr2.LOCATION\u切换为=2180,将cr2.LOCATION\u切换为=cr.LOCATION\u,我可能已经了解了一些东西,但我已经为此奋斗了很长时间,即使它似乎给出了期望的结果,“我没有信心我做对了。”RyanWilson不幸的是,我不理解上下文。这很有效!它确实有效。它简单易懂,很棒。它甚至可以在我的实时代码上运行,其结构要复杂得多。我现在很高兴。非常感谢@干得好+我也感谢你@RyanWilson。我在你编辑答案的时候读了你的答案,很接近。我只是缺少了包装选择think@KjetilNordin没问题,我很乐意帮忙。是的,我试图在编程的时候解决这个问题,哈哈。对我来说不太好。这个很有效!它确实有效。它简单易懂,很棒。它甚至可以在我的实时代码上运行,其结构要复杂得多。我现在很高兴。非常感谢@干得好+我也感谢你@RyanWilson。我在你编辑答案的时候读了你的答案,很接近。我只是缺少了包装选择think@KjetilNordin没问题,我很乐意帮忙。是的,我试图在编程的时候解决这个查询,哈哈。对我来说不太好。很好地使用了常用的表表达式,+1我也必须更仔细地阅读这个。似乎从这里也可以学到:)我已经在行数排序中添加了额外的逻辑,因为我不确定您是否希望状态为“打开”的最新行优先于状态为“锁定”的最新行优先。我只需要显示位置是否有当前不应覆盖的数据集。用于显示是否测试