SQL - 如何根据不断变化的条件获取组的第一行,而不选择其下的行?
SQL - How do I grab the top row of a group based on a changing condition, without selecting rows under it?
假设我使用了以下查询:
select * from GenericTable order by ID, DATE
并收到以下 table.
ID: DATE: VALUE1: VALUE2:
----------------------------------------
1 6/1/18 22 24
1 7/1/18 23 25
1 8/1/18 24 25
2 6/28/18 31 46
2 6/30/18 33 11
2 7/1/18 21 10
我需要根据不断变化的日期条件 (DATE <= @DateVar)
将该查询细化为 select 每个 ID
只有一个数据行。例如,if @DateVar = '6/15/18'
,我应该得到以下 table:
ID: DATE: VALUE1: VALUE2:
----------------------------------------
1 6/1/18 22 24
If @DateVar = 6/29
,我应该得到:
ID: DATE: VALUE1: VALUE2:
----------------------------------------
1 6/1/18 22 24
2 6/28/18 31 46
如果我这样做 @DateVar = 7/15
,我应该得到:
ID: DATE: VALUE1: VALUE2:
----------------------------------------
1 7/1/18 23 25
2 7/1/18 21 10
通过分区跟踪 Row#
和 selecting Row# = 1
是我最初的解决方案,但是我将如何制作一个只占 DATE <= @DateVar
的分区,这样说,对于 @DateVar = 6/30
,我将从 select Row# = 1
中得到以下内容?
ID: DATE: VALUE1: VALUE2: ROW#:
--------------------------------------------------
1 7/1/18 23 25 1
1 8/1/18 24 25 2
2 6/30/18 33 11 1
2 7/1/18 21 10 2
试一试...
USE tempdb;
GO
IF OBJECT_ID('tempdb.dbo.Table_1', 'U') IS NOT NULL
BEGIN DROP TABLE dbo.Table_1; END;
CREATE TABLE dbo.Table_1 (
ID INT NOT NULL
CONSTRAINT pk_table1 PRIMARY KEY
);
INSERT dbo.Table_1 (ID) VALUES (1), (2);
IF OBJECT_ID('tempdb.dbo.Table_2', 'U') IS NOT NULL
BEGIN DROP TABLE dbo.Table_2; END;
CREATE TABLE dbo.Table_2 (
t1_ID INT NOT NULL
CONSTRAINT fk_table2_t1ID FOREIGN KEY REFERENCES dbo.Table_1 (ID),
SomeDate date NOT NULL,
Value1 INT NOT NULL,
Value2 INT NOT NULL,
CONSTRAINT pk_table2 PRIMARY KEY (t1_ID, SomeDate)
);
INSERT dbo.Table_2 (t1_ID, SomeDate, Value1, Value2) VALUES
(1, '20180601', 22, 24),
(1, '20180701', 23, 25),
(1, '20180801', 24, 25),
(2, '20180628', 31, 46),
(2, '20180630', 33, 11),
(2, '20180701', 21, 10);
--==================================================================
DECLARE @DateVar DATE = '20180710';
SELECT
t1.ID,
tx.SomeDate,
tx.Value1,
tx.Value2
FROM
dbo.Table_1 t1
CROSS APPLY (
SELECT TOP (1)
t2 .t1_ID,
t2.SomeDate,
t2.Value1,
t2.Value2
FROM
dbo.Table_2 t2
WHERE
t1.ID = t2.t1_ID
AND t2.SomeDate <= @DateVar
ORDER BY
t2.SomeDate DESC
) tx;
结果...
ID SomeDate Value1 Value2
----------- ---------- ----------- -----------
1 2018-07-01 23 25
2 2018-07-01 21 10
我会使用 row_number:
select top 1 with ties *
from GenericTable
where [DATE] <= @DateVar
order by row_number() over (partition by ID order by [DATE] desc)
以下是 row_number 帮助您的方法...每当您需要 return 整行时,如果只需要对行的一部分进行聚合,则 row_number(或其他 windows 个函数)开始发挥作用。
将 row_number 移至 select 部分并去掉前 1 名,以查看数据集的真实情况。
大多数人会这样写,但我喜欢让它看起来更紧凑:
;with cte as
(
select *,rn = row_number() over (partition by ID order by date desc)
from [table]
where [DATE] <= @DateVar
)
select *
from cte
where rn=1
假设我使用了以下查询:
select * from GenericTable order by ID, DATE
并收到以下 table.
ID: DATE: VALUE1: VALUE2:
----------------------------------------
1 6/1/18 22 24
1 7/1/18 23 25
1 8/1/18 24 25
2 6/28/18 31 46
2 6/30/18 33 11
2 7/1/18 21 10
我需要根据不断变化的日期条件 (DATE <= @DateVar)
将该查询细化为 select 每个 ID
只有一个数据行。例如,if @DateVar = '6/15/18'
,我应该得到以下 table:
ID: DATE: VALUE1: VALUE2:
----------------------------------------
1 6/1/18 22 24
If @DateVar = 6/29
,我应该得到:
ID: DATE: VALUE1: VALUE2:
----------------------------------------
1 6/1/18 22 24
2 6/28/18 31 46
如果我这样做 @DateVar = 7/15
,我应该得到:
ID: DATE: VALUE1: VALUE2:
----------------------------------------
1 7/1/18 23 25
2 7/1/18 21 10
通过分区跟踪 Row#
和 selecting Row# = 1
是我最初的解决方案,但是我将如何制作一个只占 DATE <= @DateVar
的分区,这样说,对于 @DateVar = 6/30
,我将从 select Row# = 1
中得到以下内容?
ID: DATE: VALUE1: VALUE2: ROW#:
--------------------------------------------------
1 7/1/18 23 25 1
1 8/1/18 24 25 2
2 6/30/18 33 11 1
2 7/1/18 21 10 2
试一试...
USE tempdb;
GO
IF OBJECT_ID('tempdb.dbo.Table_1', 'U') IS NOT NULL
BEGIN DROP TABLE dbo.Table_1; END;
CREATE TABLE dbo.Table_1 (
ID INT NOT NULL
CONSTRAINT pk_table1 PRIMARY KEY
);
INSERT dbo.Table_1 (ID) VALUES (1), (2);
IF OBJECT_ID('tempdb.dbo.Table_2', 'U') IS NOT NULL
BEGIN DROP TABLE dbo.Table_2; END;
CREATE TABLE dbo.Table_2 (
t1_ID INT NOT NULL
CONSTRAINT fk_table2_t1ID FOREIGN KEY REFERENCES dbo.Table_1 (ID),
SomeDate date NOT NULL,
Value1 INT NOT NULL,
Value2 INT NOT NULL,
CONSTRAINT pk_table2 PRIMARY KEY (t1_ID, SomeDate)
);
INSERT dbo.Table_2 (t1_ID, SomeDate, Value1, Value2) VALUES
(1, '20180601', 22, 24),
(1, '20180701', 23, 25),
(1, '20180801', 24, 25),
(2, '20180628', 31, 46),
(2, '20180630', 33, 11),
(2, '20180701', 21, 10);
--==================================================================
DECLARE @DateVar DATE = '20180710';
SELECT
t1.ID,
tx.SomeDate,
tx.Value1,
tx.Value2
FROM
dbo.Table_1 t1
CROSS APPLY (
SELECT TOP (1)
t2 .t1_ID,
t2.SomeDate,
t2.Value1,
t2.Value2
FROM
dbo.Table_2 t2
WHERE
t1.ID = t2.t1_ID
AND t2.SomeDate <= @DateVar
ORDER BY
t2.SomeDate DESC
) tx;
结果...
ID SomeDate Value1 Value2
----------- ---------- ----------- -----------
1 2018-07-01 23 25
2 2018-07-01 21 10
我会使用 row_number:
select top 1 with ties *
from GenericTable
where [DATE] <= @DateVar
order by row_number() over (partition by ID order by [DATE] desc)
以下是 row_number 帮助您的方法...每当您需要 return 整行时,如果只需要对行的一部分进行聚合,则 row_number(或其他 windows 个函数)开始发挥作用。
将 row_number 移至 select 部分并去掉前 1 名,以查看数据集的真实情况。
大多数人会这样写,但我喜欢让它看起来更紧凑:
;with cte as
(
select *,rn = row_number() over (partition by ID order by date desc)
from [table]
where [DATE] <= @DateVar
)
select *
from cte
where rn=1