【问题标题】:SQL Server Recursive querySQL Server 递归查询
【发布时间】:2010-10-25 16:44:08
【问题描述】:

几天前我问了一个关于help on a recursive query的问题。

那个问题的问题是“如何获取一个人的约会历史”。

现在我遇到了一个类似的问题,但它应该回答一个稍微不同的问题:

How to get an Appointment history?

例如,如果 ID = 5 的约会被推迟了一次,并且是另一个约会的推迟,我如何得到以下结果?

AppointmentID         PrevAppointmentID
-----------------    ----------------------
1                     NULL
5                     1
12                    5

感谢您的帮助

更新

这些脚本将有助于为您的试验创建表

CREATE TABLE [dbo].[Appointments](
    [AppointmentID] [int] IDENTITY(1,1) NOT NULL,
    [IssueID] [int] NOT NULL,
    [Location] [varchar](255) NOT NULL,
    [Description] [varchar](255) NOT NULL,
    [AppointmentDate] [datetime] NOT NULL,
    [AppointmentHour] [datetime] NOT NULL,
    [Done] [bit] NOT NULL,
    [PrevAppointmentID] [int] NULL,
 CONSTRAINT [PK_Appointments] PRIMARY KEY CLUSTERED 
(
    [AppointmentID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

【问题讨论】:

  • 如果您可以更改设计,请考虑使用有效日期行
  • @Brad:“有效日期行”是什么意思?
  • 这是一个冗长的解释,所以我添加了另一个答案。

标签: sql-server sql-server-2008 recursive-query


【解决方案1】:

我不想完全劫持他的答案,所以:

使用布拉德的答案

这是获取完整历史记录的查询:

WITH    cte
          AS ( SELECT   AppointmentId ,
                        PrevAppointmentId
               FROM     Appointments
               WHERE    AppointmentId = @AppointmentId
               UNION ALL
               SELECT   prev.AppointmentId ,
                        prev.PrevAppointmentId
               FROM     Appointments prev
                        INNER JOIN cte curr ON prev.AppointmentId = curr.PrevAppointmentId
             ),
        cte1
          AS ( SELECT   AppointmentId ,
                        PrevAppointmentId
               FROM     Appointments
               WHERE    AppointmentId = @AppointmentId
               UNION ALL
               SELECT   prev.AppointmentId ,
                        prev.PrevAppointmentId
               FROM     Appointments prev
                        INNER JOIN cte1 curr ON prev.PrevAppointmentId = curr.AppointmentId
             )
    SELECT  *
    FROM    cte
    UNION
    SELECT  *
    FROM    cte1

【讨论】:

  • @Martin:表示在完成查询之前已经达到最大递归次数100...
  • @Lorenzo - 这是有道理的,因为它会不断地来回前进,来回前进......更新我的答案,但仍然不知道它是否有效。
  • @Martin:未定义内部连接时的 cteAppointments。您是指分别 cteAppointments1 和 2 吗?抱歉,我对 cte 一无所知 :(
  • @Lorenzo - 这将为您提供完整的历史记录,无论您输入的是 12、5 还是 1
  • @Martin:这行得通。百万谢谢!!!!如果没有布拉德的初步工作,它就不会这么快完成。 +1 表扬他!
【解决方案2】:

逻辑:

  1. 获取相关约会
  2. 重复加入父应用程序
  3. 选择所有结果

查询:

DECLARE @appointmentId INT
SET @appointmentId = 3

--
;
WITH  past
      AS ( SELECT   AppointmentId ,
                    PrevAppointmentId
           FROM     Appointments
           WHERE    AppointmentId = @AppointmentId
           UNION ALL
           SELECT   prev.AppointmentId ,
                    prev.PrevAppointmentId
           FROM     Appointments prev
                    INNER JOIN cte curr ON prev.AppointmentId = curr.PrevAppointmentId
         ),
    future
      AS ( SELECT   AppointmentId ,
                    PrevAppointmentId
           FROM     Appointments
           WHERE    AppointmentId = @AppointmentId
           UNION ALL
           SELECT   prev.AppointmentId ,
                    prev.PrevAppointmentId
           FROM     Appointments prev
                    INNER JOIN cte1 curr ON prev.PrevAppointmentId = curr.AppointmentId
         )
SELECT  *
FROM    past OPTION(MAXRECURSION 500)
UNION
SELECT  *
FROM    future OPTION(MAXRECURSION 500)

【讨论】:

  • @Brad:它返回一个错误,说“子查询中不允许递归引用”对应于 EXISTS 中的 WHERE 子句(WHERE prev.AppointmentId = curr.PrevAppointmentId
  • 如果您发布代码或 XML,在文本编辑器中突出显示这些行,然后单击编辑器工具栏上的“代码”按钮 (101 010) 以很好地格式化和语法高亮!
  • 这行得通,我只需要将 prev. 编辑添加到第二个选择中
  • @Brad:现在又出现了一个错误。我已更新问题以包括为您的试验创建脚本
  • @Lorenzo - 立即尝试代码。它适用于我创建的小测试环境。
【解决方案3】:

回答 OP 的问题:

有效约会

有效日期是当您将DateTime 列添加到控制记录何时变为“有效”的表时。然后将该列添加到表的PK,使每个条目成为在给定时间点(生效日期)“有效”内容的记录。使用有效约会,您永远不会DELETEUPDATE,只有INSERT 意味着您始终拥有该对象的完整历史记录。

要查找最有效的记录,请选择具有最大有效但不在未来的行

SELECT *
FROM   Appointments a1
WHERE  EffectiveDate = (SELECT MAX(EffectiveDate)
                        FROM   Appointments a2
                        WHERE  a1.AppointmentId = a2.AppointmentId
                               AND a2.EffectiveDate <= ISNULL(@asOfDate, GETDATE()
                       )

这意味着您还可以提前记录。例如,您今天获准加薪,但在 2 周内不会生效。

因此,要查找约会的历史记录,您只需:

SELECT *
FROM   Appointments
WHERE  AppointmentId = @appointmentId
ORDER BY EffectiveDate

【讨论】:

  • 我知道技巧!但我从来不知道我可以用“有效约会”这个名字来指代它。非常感谢您的解释
猜你喜欢
  • 2014-12-19
  • 2012-12-05
  • 2014-10-10
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-19
相关资源
最近更新 更多