【问题标题】:Selecting all dates from a table within a date range and including 1 row per empty date从日期范围内的表中选择所有日期,每个空日期包括 1 行
【发布时间】:2012-01-19 12:37:31
【问题描述】:

我正在尝试重构 ASP.Net 网站中的一些代码,但我正在编写的存储过程出现问题。

我想要做的是获取一个日期范围,然后从表格中选择该范围内的所有数据,但是如果日期不存在,我仍然需要选择一行。

正如您在下面的代码中看到的那样,我的想法是创建一个临时表,并用我的日期范围内的所有日期填充它,然后将其加入我从中选择的表中,但这不起作用。我在这里做错了吗?在此连接中 tempDate 列始终为空,但是我检查了该表,其中肯定有数据。

-- Parameters
DECLARE @DutyDate datetime='2012-01-01 00:00:00'
DECLARE @InstructorID nvarchar(2) = N'29'

DECLARE @datesTBL TABLE (tempDate DATETIME)

-- Variables
DECLARE @StartDate DATETIME 
DECLARE @EndDate DATETIME

SELECT 
    @StartDate =StartDate, 
    @EndDate = EndDate
FROM 
    DutyPeriodTbl 
WHERE 
(StartDate <= @DutyDate) 
AND 
(EndDate >= @DutyDate)


DECLARE @d DATETIME = @StartDate 
WHILE @d<=@EndDate
BEGIN
    INSERT INTO @datesTBL VALUES (CONVERT(DATETIME, @d, 102))
    SET @d=DATEADD(day,1,@d)
END

SELECT 
    dt.tempDate ,
    InstructorID,           EventStart, 
    EventEnd,               cancelled, 
    cancelledInstructor, 
    EventType,              DevName, 
    Room,                   SimLocation, 
    ClassLocation,          Event, 
    Duration,               TrainingDesc, 
    Crew,                   Notes, 
    LastAmended,            InstLastAmended, 
    ChangeAcknowledged,     Type, 
    OtherType,              OtherTypeDesc, 
    CourseType 
FROM 
    OpsInstructorEventsView iv
LEFT OUTER JOIN
    @datesTBL dt 
ON
    CONVERT(DATETIME, iv.EventStart, 102) = CONVERT(DATETIME, dt.tempDate, 102) 
WHERE 
    InstructorID = @InstructorID 
AND 
    EventStart BETWEEN CONVERT(DATETIME, @StartDate, 102) AND CONVERT(DATETIME, @EndDate, 102)
ORDER BY 
    EventStart 

【问题讨论】:

标签: sql sql-server-2008 stored-procedures


【解决方案1】:

有几种方法可以处理丢失的行,但都是关于将另一组数据与您当前的结果相结合。

这可能来自您的结果,由 CTE 或其他过程(例如您的示例)创建,或者(我的偏好)通过使用永久模板加入。

您的模板可能只是一个日期表,例如您的@datesTBL。不同之处在于它是提前创建的,例如 100 年的日期。

您的查询可能与您的示例类似,但我会尝试以下...

SELECT 
    dt.tempDate ,
    InstructorID,           EventStart, 
    EventEnd,               cancelled, 
    cancelledInstructor, 
    EventType,              DevName, 
    Room,                   SimLocation, 
    ClassLocation,          Event, 
    Duration,               TrainingDesc, 
    Crew,                   Notes, 
    LastAmended,            InstLastAmended, 
    ChangeAcknowledged,     Type, 
    OtherType,              OtherTypeDesc, 
    CourseType 
FROM 
  @datesTBL dt 
LEFT OUTER JOIN
  OpsInstructorEventsView iv
    ON  iv.EventStart >= dt.tempDate
    AND iv.EventStart <  dt.tempDate + 1
    AND iv.InstructorID = @InstructorID 
WHERE
      dt.tempDate >= @StartDate
  AND dt.tempDate <= @EndDate
ORDER BY
  dt.tempDate,
  iv.EventStart

这会将 日历模板 放在左侧,因此使许多查询更容易,因为您知道日历的日期字段始终被填充,始终是仅日期(无时间部分)值,位于顺序,很容易进行 GROUP BY 等。

【讨论】:

  • +1:Calendar 表是要走的路,易于填充且始终有用。谷歌“celko 日历表”以获取更多信息和以其他方式使用它们的提示。
  • 谢谢这完美地解决了它,我也会考虑使用日历表。感谢您的信息
【解决方案2】:

好吧,想法是一样的,但我会编写函数,它返回包含所有日期的表。看看这个:

Create Function [dbo].[Interval]
(
    @DateFrom Date,
    @DateTo Date
)
Returns @tab Table
    (
        MyDate DateTime
    )
As
Begin
    Declare @Days int
    Declare @i int
    Set @Days = DateDiff(Day, @DateFrom, @DateTo)

    Set @i = 0;
    While (@Days > @i)
    Begin
        Insert Into @tab(MyDate)
            Values (DateAdd(Day, @i, @DateTo))
        Set @i = @i + 1
    End
    return
End

并在需要时重用该功能..

Select *
From [dbo].[Interval]('2011-01-01', GETDATE())

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多