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