【问题标题】:T-SQL script - logic issues with timelineT-SQL 脚本 - 时间线的逻辑问题
【发布时间】:2011-05-11 12:42:27
【问题描述】:

创建并加载了两个临时表...这是架构。

Create table #SH ([date] datetime,
        sched_id int,
        agent_id int)

Create table #SD (sched_id int,
        start_minute int,
        length int,
        exception_code int)

(不幸的是,架构和设计是我无法更改的,两个临时表都是从平面文件中加载的。如果需要,我可以引入和加载新的临时表)。

一点背景 - #SH 标题表将人员日程表保存为“Start_minute”,并以分钟为单位显示“schedule_length”。例如,如果开始分钟和计划长度均为 480,则将读取为上午 8 点(上午 8 点 = 第 480 分钟)并持续到下午 4 点(480 分钟后,下午 4 点 = 第 960 分钟)

#SD 表包含标题的异常。在上面的示例中,此人可能会有一个午餐例外,即 start_minute = 720 和 30 的长度(12:00 - 12:30)。

日期和 agent_id 是我对#SH 唯一感兴趣的,#sd 中的异常信息是我感兴趣的。

此查询有效:

Select [date],#sd.start_minute,#sd.length,#sd.start_minute + #sd.length as 'end_minute',agent_id
from #SH 
inner join #SD on #SD.sched_id = #sh.sched_id

*end_minute 最终是 start+length = end 的计算值

这会返回类似:

   Date     Start  length   end

1 2010-11-11 600    30  630

2 2010-11-11 630    40  670

3 2010-11-11 750    15  765

4 2010-11-11 800    40  840

现在我希望我可以说这已经结束并走开......但是存在数据输入问题。在第 1 行和第 2 行中,第 1 行的结束时间与第 2 行的开始时间一致,应该合并,所以我的结果如下所示:

Date     Start  length     end

1 2010-11-11 600    70  670

2 2010-11-11 750    15  765

3 2010-11-11 800    40  840

关于如何构建此逻辑以便我得到 3 行而不是 4 行的任何想法?我正在努力在#sd1.start + #sd1.length = #sd2.start 上将表格加入到自身中。

更复杂的是...上面的示例是需要合并的 2 行。我遇到了一个连续有 30 个 1 分钟条目的记录,我需要将其制成一个记录。幸运的是它们不能重叠(你不会有 2 条记录占用相同的分钟数),但我认为我上面考虑的 join 语句不适用于此。

【问题讨论】:

  • 对我自己的语法做了一些修改...#SH 中还有很多列,但它们无关紧要。

标签: sql sql-server tsql sql-server-2000


【解决方案1】:

不需要 CTE,您只需要一个辅助表。创建一次,如下所示:

Create Table DayMinute(Minute Integer)
Declare @M Integer
Set @M = 1
While (@M <= 24*60)
Begin
  Insert Into DayMinute(Minute) Values(@M)
  Set @M = @M + 1
End

那么,你需要的只是一点技巧:

Select 
  DM.Minute,
  SD.Sched_ID
Into #MinutesWithException
From 
  DayMinute As DM
  Inner Join #SD As SD
    On DM.Minute Between SD.Start_Minute And SD.Start_Minute + Length

Select
  MWE.Sched_ID,
  SH.[Date],
  SH.Agent_ID,
  [Start_Minute] = MWE.Minute,
  [End_Minute] = (Select Min(Last.Minute) -- First one to have no successor
                  From #MinutesWithException As Last
                  Where Last.Sched_ID = MWE.Sched_ID
                    And Last.Minute > MWE.Minute
                    And Not Exists(Select *
                                   From #MinutesWithException As Next
                                   Where Next.Sched_ID = MWE.Sched_iD
                                     And Next.Minute = Last.Minute + 1))
From 
  #MinutesWithException As MWE
  Inner Join #SH As SH
    On MWE.Sched_ID = SH.Sched_ID
Where
  Not Exists(Select * -- All those without predecessor
             From #MinutesWithException As Previous
             Where Previous.Sched_ID = MWE.Sched_ID
               And Previous.Minute = MWE.Minute - 1)

请记住,很多 SQL 问题可以通过改写它们来解决。不要问“哪些范围没有间隔”,而要问“哪些分钟有间隔”。其余的从那里开始。

【讨论】:

  • 我对那个 Stu 也有同样的想法……新的必须有另一种看待它的方式。我必须做一些修改以适应脚本的其余部分,我会让你知道它是怎么回事。哇,处理器密集型的。
  • 不确定更高效...当我运行它时,它会将我的处理器固定在 100%。它似乎至少可以解决问题,我想知道当我向它提供 20k 条记录而不是 4 条记录时它会如何反应..
  • 将其固定为 100%?约扎。我什至无法让一个核心在十分之一秒左右的时间内飙升超过 70%,它在这里运行。你有什么,386? :-)
  • 顺便说一句,如果 20K 记录的性能不好,请在 DayMinute(Minute) 上创建一个聚集索引,并在 #MinutesWithException(Sched_ID, Minute) 上创建一个
  • 嘿,它是一个相当糟糕的开发服务器......真的希望它至少是 486 ;) 当我用大约 1k 条记录运行它时,它会将处理器固定在 100%......我认为 DM 上的索引会有所帮助。这个开发服务器上的资源监视器也有点不稳定......当这个脚本运行时,它似乎无法监控很多。嗯……这家公司的营业额太多,给这些服务器留下了太多的未知数。
【解决方案2】:

如果您使用递归 CTE 来组合上述查询的结果,则最多可以将 32767 条记录链接在一起。如果您认为自己永远不会接近这个数量,您可以考虑采用这种方法。

我创建了一个工作示例,因为我不确定。您的分组会有所不同,但这是一般的想法:

CREATE TABLE times
(
[Date] datetime,
[start] int,
[length] int,
[end] int
)
INSERT INTO times([Date], [Start], [length], [End])
VALUES ('11/11/2010',600,30,630)
INSERT INTO times([Date], [Start], [length], [End])
VALUES ('11/11/2010',630,40,670)
INSERT INTO times([Date], [Start], [length], [End])
VALUES ('11/11/2010',750,15,765)
INSERT INTO times([Date], [Start], [length], [End])
VALUES ('11/11/2010',800,40,840)

;WITH chaintimes AS
(
    SELECT t1.Date, t1.start, t1.length, t1.[end]
    FROM times t1 LEFT JOIN times t2 ON t1.start = t2.[end]
    WHERE t2.[end] IS NULL
    UNION ALL
    SELECT times.Date, chaintimes.start, chaintimes.length + times.length AS length, times.[end]
    FROM times INNER JOIN chaintimes ON times.start = chaintimes.[end]
)
, start_maxlength AS
(
    SELECT date, start, max(length) AS maxlength
    FROM chaintimes
    group by date, start
)
SELECT * FROM chaintimes ct
INNER JOIN start_maxlength ml
ON ct.Date = ml.Date AND ct.start = ml.start AND ct.length = ml.maxlength

【讨论】:

  • 我应该在这个...MSSQL 2000 上提到我的环境。这看起来是 PL/SQL。我会试一试,但我怀疑有错误。我想我至少理解你在这里的逻辑......让我尝试使用这种逻辑风格修改我当前的代码
  • 啊,是的,CTE 是在 MS SQL 2005 中引入的,无需费心。
  • 虽然我非常不同意缺乏上下文是 downvote 的一个很好的理由。
  • 这里是混合环境...让我看看能不能找到一个 2005 年的机器来运行它。 Stu 提供的解决方案似乎有效,但这对我来说似乎是一个很好的学习机会
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-18
  • 2010-09-14
  • 1970-01-01
  • 2016-10-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多