如何根据连接表上的唯一值从表中选择最大值?

问题描述 投票:0回答:2

我有三张桌子。所有都遵循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)
sql-server
2个回答
4
投票

我认为这个查询对你有用。正如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

2
投票

如果我正确得到逻辑:

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;
© www.soinside.com 2019 - 2024. All rights reserved.