TSQL 根据提供的记录集获取以前的和任何进行的记录

Posted

技术标签:

【中文标题】TSQL 根据提供的记录集获取以前的和任何进行的记录【英文标题】:TSQL get the previous and any proceeding records based on recordset provided 【发布时间】:2021-11-11 15:04:28 【问题描述】:

我有以下疑问:

SELECT 
    fs.FILOID currentfilo,
    ISNULL(LEAD(af.FILOID) OVER (ORDER BY StartTransactionTimeUTC), fs.Next_FILOID) Nextfilo,
    ISNULL(LAG(af.FILOID) OVER (ORDER BY StartTransactionTimeUTC), fs.Previous_FILOID) lf 
FROM 
    [DataWarehouseCore].[DWC_FILOSummary] fs 
JOIN 
    [AttendanceCore].[AC_FILO] af ON af.FILOID = fs.FILOID
WHERE 
    fs.Employee_ID = 15049
    AND Client_ID = 306

返回以下结果:

currentfilo Nextfilo    lf
----------------------------
5           2           NULL
2           3           5
3           6           2
6           7           3
7           1           6
1           NULL        7

如果传入了 ID 6,我只想查看 currentfilo 3, 6, 7 , 1。因此,与其返回结果集中的内容,不如只返回实际上基于日期字段的上一个和下一个记录.

这是一些数据示例,我希望看到的是父表:AC_FILO

FILOID  ClientID    EmployeeID  StartTransactionTimeUTC
-----------------------------------------------------------
5       306         15049       2021-08-29 02:53:00.0000000
2       306         15049       2021-09-01 06:46:00.0000000
3       306         15049       2021-09-02 07:50:00.0000000
6       306         15049       2021-09-06 08:56:00.0000000
7       306         15049       2021-09-10 07:58:00.0000000
1       306         15049       2021-09-15 07:45:00.0000000

我想要在我的新表中是当前 FILOID 的有序列表,下一个 FILOID 和前一个 FILOID 是基于 StartTransactionTimeUTC

例如,当插入 FILOID 6 时,我希望查看前一个 FILOID 到 FILOID 6,然后是基于日期的所有正在进行的 FILOID!

例如我想看看

CurrentFilo NextFilo LastFilo
-----------------------------
3           6        2
6           7        3
7           1        6
1           NULL     7

【问题讨论】:

您需要使用子查询和STRING_AGG 来实现此目的,并且由于三角连接,此查询不太可能高效。 in the order they are in this table数据在表中没有订单,如果没有提供order by,数据库引擎将返回未排序的数据,所以你的方法是行不通的 在这个数据和这个结果集中,当你说“ID = 6”时,“ID”是什么。我对您的数据了解不够,无法在此处提供指导。我假设FILOID 但它存在于两个表中,并且存在于结果集中的所有三列中。这是该数据中的父/子关系吗?如果是这样,递归 CTE 将解决您的问题。 【参考方案1】:

目前尚不完全清楚您在追求什么,但考虑到最终所需的结果和 6 的输入值,我认为这意味着您想要由 StartTransactionTimeUTC 排序的 (CurrentFilo, NextFilo,LastFilo) 集合,从以下点开始鉴于NextFilo 发生,因为6 输入与预期结果中该位置的6 匹配。


作为第一步,从不关心传入的任何内容开始,只为整个表创建下一个/最后一个结果。我们可以使用LAG()LEAD() 窗口函数来做到这一点。

您已经在问题的第一个代码清单中完成了大部分工作,但我们现在还包括 StartTransactionTimeUTC 字段,以便我们能够在后面的步骤中重现正确的顺序。我现在也在使用一个简化的模式,所以我可以在 SQL Fiddle 上重现它:

SELECT  FILOID as CurrentFILO,
    LEAD(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as NextFILO,
    LAG(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as LastFILO,
    StartTransactionTimeUTC
FROM AC_FILO

现在,对于给定的 NextFILO 值,例如 6,我们可以使用公用表表达式 (CTE) 找到该记录所需的 StartTransactionTimeUTC 时间,如下所示:

WITH FILOTree AS (
    SELECT  FILOID as CurrentFILO,
        LEAD(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as NextFILO,
        LAG(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as LastFILO,
        StartTransactionTimeUTC
    FROM AC_FILO
)
SELECT StartTransactionTimeUTC
FROM FILOTree
WHERE NextFILO = 6

我们也可以使用嵌套的内部 SELECT 来代替 CTE。重要的是我们需要在初始查询周围设置一层间接层,以便将WHERE 子句与LAG()LEAD() 等窗口函数一起使用。但在这种情况下,我更喜欢 CTE,原因稍后会清楚。

一旦我们知道正确的StartTransactionTimeUTC,我们就可以使用相同的技术获得最终结果:

WITH FILOTree AS (
    SELECT  FILOID as CurrentFILO,
        LEAD(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as NextFILO,
        LAG(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as LastFILO,
        StartTransactionTimeUTC
    FROM AC_FILO
)
SELECT CurrentFILO, NextFILO, LastFILO
FROM FILOTree
WHERE StartTransactionTimeUTC >= '2021-09-02 07:50:00'
ORDER BY StartTransactionTimeUTC 

这会产生正确的结果,但输入错误。但是,现在我们可以使用相同的 CTE(啊哈!)将两个部分放在同一个查询中,而无需为初始当前/下一个/最后一个投影重复代码,如下所示:

WITH FILOTree As (
   SELECT  FILOID as CurrentFILO,
       LEAD(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as NextFILO,
       LAG(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as LastFILO,
       StartTransactionTimeUTC
   FROM AC_FILO
)
SELECT CurrentFILO, NextFILO, LastFILO
FROM FILOTree 
WHERE StartTransactionTimeUTC >= (
   SELECT StartTransactionTimeUTC 
   FROM FILOTree
   WHERE NextFILO = 6
)
ORDER BY StartTransactionTimeUTC 

在这里查看它的工作原理:

http://sqlfiddle.com/#!18/24979/2


最后,正如我之前所说,这些示例使用来自实际生产系统的简化模式。因此,让我们将原始查询插入 CTE:

WITH FILOTree As (
    SELECT fs.FILOID currentfilo 
        , ISNULL(LEAD(af.FILOID) over (order by StartTransactionTimeUTC), fs.Next_FILOID) Nextfilo
        , ISNULL(LAG(af.FILOID) over (order by StartTransactionTimeUTC), fs.Previous_FILOID) LastFILO
        , StartTransactionTimeUTC
    FROM [DataWarehouseCore].[DWC_FILOSummary] fs 
    JOIN [AttendanceCore].[AC_FILO] af ON af.FILOID = fs.FILOID
    WHERE fs.Employee_ID = 15049
        AND Client_ID = 306
)
SELECT CurrentFILO, NextFILO, LastFILO
FROM FILOTree 
WHERE StartTransactionTimeUTC >= (
    SELECT StartTransactionTimeUTC 
    FROM FILOTree
    WHERE NextFILO = 6
)
ORDER BY StartTransactionTimeUTC 

我必须从最初的 SQL 命令更改的唯一事情是将 StartTransactionTimeUTC 添加到 SELECT 列表和 LastFILO 列的别名。

请注意,这运行 CTE 两次,并且它不会缓存两次运行之间的任何结果,因此请注意这是性能问题。

【讨论】:

这正是我想到的,非常感谢!

以上是关于TSQL 根据提供的记录集获取以前的和任何进行的记录的主要内容,如果未能解决你的问题,请参考以下文章

TSQL 在 IN 运算符中获取丢失的记录

如何使用 TSQL 而不是 linq 获取和跳过记录 [重复]

TSQL 分组集(Grouping Sets)

SQL 问题:根据操作代码获取以前的数据

Access 2013 - VBA - 记录集插入获取 ID

TSQL:根据孩子的条件选择父母