【问题标题】:TSQL : the top records of a given partition (conditional)TSQL :给定分区的顶部记录(条件)
【发布时间】:2016-11-01 21:38:06
【问题描述】:

我需要获取TABLE_A 中的所有记录,其中至少 2 最后一个Status 是空的(相对于Inspection_Date)并且Room_ID 在@987654325 中不存在@。

这是我用作示例的简化表:

TABLE_A

  Room_Id   Status    Inspection_Date
  -------------------------------------
    1        vacant      5/15/2015
    2        occupied    5/21/2015
    2        vacant      1/19/2016
    1        occupied   12/16/2015
    4        vacant      3/25/2016
    3        vacant      8/27/2015
    1        vacant      4/17/2016
    3        vacant     12/12/2015
    3        vacant      3/22/2016
    4        occupied    2/2/2015
    4        vacant      3/24/2015

TABLE_B

  Room_Id   Status    Inspection_Date
  ------------------------------------
    1        vacant       5/15/2015
    2        occupied     5/21/2015
    2        vacant       1/19/2016
    1        vacant      12/16/2015
    1        vacant       4/17/2016

我的结果应该是这样的:

   Room_Id  Status  Inspection_Date
   ---------------------------------
    3       vacant      8/27/2015
    3       vacant     12/12/2015
    3       vacant      3/22/2016
    4       occupied    2/2/2015
    4       vacant      3/24/2015
    4       vacant      3/25/2016

我已经尝试过这种方式,它适用于示例,但不适用于我的数据..逻辑不完整:

 With cteA As
(
Select *, Row_Number() Over (Partition By Room_ID, Status Order By     Inspection_Date Desc) RowNum From Table_A 
)
Select * From Table_A Where Room_Id In
(
Select Room_Id 
    From cteA
    Where Room_Id Not In (Select Room_Id From Table_B) 
        And Status = 'vacant' And RowNum > 1 
)
    Order By Room_Id, Inspection_Date

这是架构:

CREATE TABLE TABLE_A (`Room_Id` int, 
                      `Status` varchar(55), 
                      `Inspection_Date` Date
                     );

INSERT INTO TABLE_A (Room_Id, Status, Inspection_Date)
VALUES  (1, 'vacant',      '5/15/2015'),
        (2, 'occupied',    '5/21/2015'),
        (2, 'vacant',      '1/19/2016'),
        (1, 'occupied',   '12/16/2015'),
        (4, 'vacant',      '3/25/2016'),
        (3, 'vacant',      '8/27/2015'),
        (1, 'vacant',      '4/17/2016'),
        (3, 'vacant',     '12/12/2015'),
        (3, 'vacant',      '3/22/2016'),
        (4, 'occupied',       '2/2/2015'),
        (4, 'vacant',      '3/24/2015');

CREATE TABLE TABLE_B (`Room_Id` int, 
                      `Status` varchar(55),         
                      `Inspection_Date` Date
                     );

INSERT INTO TABLE_B (Room_Id, Status, Inspection_Date)
VALUES
        (1, 'vacant',      '5/15/2015'),
        (2, 'occupied',    '5/21/2015'),
        (2, 'vacant',      '1/19/2016'),
        (1, 'vacant',      '12/16/2015'),
        (1, 'vacant',      '4/17/2016'),;

【问题讨论】:

    标签: sql sql-server tsql group-by partitioning


    【解决方案1】:

    普通

    1. 为 TABLE_A 中的每个房间选择最后日期(作为 lastDate)

    2. 为 TABLE_A 中的每个房间选择上一个日期(作为 prevLastDate)

    3. 从 lastDate 获取状态为“vacant”的 room_ids(如 lastDateVacant)

    4. 从状态为“空置”的 prevLastDate 获取 room_ids(如 prevLastDateVacant)

    5. 过滤 TABLE_A 以仅包含 lastDateVacant 和 prevLastDateVacant(内部)中的 ID

    6. 过滤 TABLE_A 以仅包含不在 TABLE_B 中的 ID(左外 + IS NULL)

    结果你有:

    WITH lastDate AS (
        SELECT room_id AS room,MAX(inspection_date) AS date
        FROM "TABLE_A"
        GROUP BY room_id
    ), prevLastDate AS (
        SELECT room_id AS room,MAX(inspection_date) AS date
        FROM "TABLE_A" a
        INNER JOIN lastDate ON a.room_id = lastDate.room and a.inspection_date < lastDate.date
        GROUP BY room_id
    ), lastDateVacant AS (
        SELECT room_id AS room FROM "TABLE_A"
        WHERE (room_id,inspection_date) IN (
            SELECT room, date FROM lastDate
        ) AND status = 'vacant' 
    ), prevLastDateVacant AS (
        SELECT room_id AS room FROM "TABLE_A"
        WHERE (room_id,inspection_date) IN (
            SELECT room, date FROM prevLastDate
        ) AND status = 'vacant' 
    )
    
    SELECT a.* FROM "TABLE_A" a 
    INNER JOIN lastDateVacant 
        ON a.room_id = lastDateVacant.room
    INNER JOIN prevLastDateVacant 
        ON a.room_id = prevLastDateVacant.room
    LEFT OUTER JOIN "TABLE_B" AS b 
        ON a.room_id = b.room_id    
    WHERE b.room_id IS NULL 
    ORDER BY a.room_id ASC, a.inspection_date DESC
    

    窗口函数

    不确定 TSQL 的语法是否相同,但这里是较短的变体:

    1. 按房间分区和/或按日期排序

    2. 检查排名为 1 和 2 的 ID 是否处于“空缺”状态,按 ID 分组并多次出现

    房间为 ( 从 ( 选择 room_id 作为房间,状态,inspection_date 作为日期, RANK() OVER (PARTITION BY room_id ORDER BY Inspection_date DESC) AS RANK 来自“TABLE_A” ) 其中(排名在(1,2)和状态='空缺') 按房间分组 计数() > 1 ) 从“TABLE_A”中选择一个。 INNER JOIN 房间 ON a.room_id = room.room 左外连接“TABLE_B”作为 b ON a.room_id = b.room_id
    其中 b.room_id 为空 ORDER BY a.room_id ASC, a.inspection_date DESC

    【讨论】:

    • 仅供参考 - 语法确实有点不对,但逻辑有效。如果你不介意我会编辑它。再次感谢 ! connect.microsoft.com/SQLServer/feedback/details/299231/…
    • 在 prod 中使用它后,我发现了一些差异,我发现了一个不同的代码,我将在发布此评论后发布它。
    【解决方案2】:

    您的条件几乎直接转化为查询。您可以将窗口函数用于空计数,将not exists 用于与table_b 的关系:

    select a.*
    from (select a.*,
                 sum(case when status = 'vacant' then 1 else 0 end) over (partition by room_id) as num_vacant
          from table_a a
          where not exists (select 1
                            from table_b b
                            where b.room_id = a.room_id
                           )
        ) a
    where num_vacant >= 2;
    

    编辑:

    如果您希望最后两个是空的,您可以找到最后一个非空的记录,然后计算比这更大的记录:

    select a.*
    from (select a.*,
                 sum(case when a2.max_nonvacant > a.inspection_date then 0 else 1) over (partition by room_id) as num_vacant_last
          from table_a a outer apply
               (select max(inspection_date) as max_nonvacant
                from table_a a2
                where a2.room_id = a.room_id and a2.status <> 'vacant'
               ) a2
          where not exists (select 1
                            from table_b b
                            where b.room_id = a.room_id
                           )
        ) a
    where num_vacant_last >= 2;
    

    【讨论】:

    • 非常感谢您的快速响应,不幸的是它不起作用,请记住最后(到今天的最近日期)2 个实例必须始终空置..
    【解决方案3】:

    这对我有用,我已经检查了一遍又一遍。

    with Rooms as (
    select
        Room_Id, Status,
        row_number() over (partition by Room_Id order by Inspection_Date desc) as rn
    from TABLE_A
    ), Candidates as (
    select Room_Id from Rooms group by Room_Id
    having sum(case when rn in (1, 2) and Status = 'vacant' then 1 else null end) = 2
    )
    select * from TABLE_A
    where Room_Id in (select Room_Id from Candidates except select Room_Id from TABLE_B)
    order by Room_Id, Inspection_Date desc
    

    【讨论】:

      【解决方案4】:

      我做了这个测试: 提取考虑到与inspection_date(降序)相关的最后两个Status(相等状态)的所有room_id:

      select * from TABLE_A WHERE [Room_Id] IN
      (
          SELECT [Room_Id] FROM 
          (SELECT ROW_NUMBER() OVER(PARTITION BY [Room_Id] ORDER BY [Inspection_Date] DESC ) AS id,   
            [Room_Id],[Status],[Inspection_Date]
            FROM TABLE_A 
            ) AA
          WHERE AA.ID <=2
      --selecting the last two Inspection_Date
       and [Status] = 'vacant'  
                  GROUP BY [Room_Id],[Status] HAVING COUNT(*) >1          
      ) 
      AND 
      [Room_Id] NOT IN (SELECT Room_Id FROM TABLE_B)
      order by Room_Id, Inspection_Date desc
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多