2009-08-25 54 views
2

我们将我们的网球场的预订信息从我们的SQL数据库提取到简单的结果表中,以帮助我们构建法庭使用情况图。除非预订时间超过一小时,否则它非常简单。基于值的单个或多个INSERT选择

目前,每个预订结果在我们的结果表中连续排列。每行包含开始时间,持续时间和法庭号码。我们希望将此表格直接映射到电子表格或数据透视表中,以便我们可以看到我们的法院预订了多少个小时以及一天中的哪个小时。

目前,我们的SQL查询看起来是这样的:

INSERT INTO Results (year, month, day, hour, duration, court) 
SELECT DATEPART (yy, b.StartDateTime), 
     DATEPART (mm, b.StartDateTime), 
     DATEPART (dd, b.StartDateTime), 
     DATEPART (hh, b.StartDateTime), 
     a.Duration, 
     a.Court 
FROM Bookings b 
INNER JOIN Activities a 
ON b.ActivityID = a.ID 

我们的问题是,2,3小时或以上时间预订只能在结果表中的一行,即。预订的第一个小时。这是因为预订的时间长度会在持续时间数据中被捕获。我们可以对数据进行一些后处理来实现我们的目标,但如果在我们的SQL查询中可能会更容易一些。

该查询是否可以以某种方式进行修改,以便根据持续时间(可以是1,2,3 ......小时)将相应的行数插入到结果表中,每个持续时间为1 。因此,从上午9点开始的3小时预约将在结果表中产生三行,一个在上午9点,一个在上午10点,另一个在上午11点,每个持续时间为1小时。

因此,而不是在结果表中的一行:

Year, Month, Day, Hour, Duration, Court 
2009, 08, 25, 09,  3,  1 

我们得到如下行:

Year, Month, Day, Hour, Duration, Court 
2009, 08, 25, 09,  1,  1 
2009, 08, 25, 10,  1,  1 
2009, 08, 25, 11,  1,  1 

这会使结果表映射到电子表格中要容易得多。

UPDATE 2009-08-25:当然,正如第一对答案所示,它不一定是单个查询。一套很好。

更新2009-08-26:已被侧面追踪,还没有机会尝试提出的解决方案。希望尽快这样做,并根据结果选择答案。

UPDATE 2009-08-27:终于有机会尝试解决方案。整体表格和加入以生成解决方案让人大开眼界。特别是使用交叉连接来创建这样的表格。这可能是干净利落的SQL方式。

然而,最后,我采用了Aaron的解决方案,涉及标志和简单的算法。我确实通过在while循环中包装他的算法来增强它,以保持迭代直到没有持续时间大于1。这很快且容易实施。它还强调我们确实有10个小时的预订,所以我不需要在这里硬编码一个限制。

我应该注意到,我将Jeff的最大持续时间的想法并入了while循环计数器,而不是我对持续时间> 1的项目进行计数的原始想法。代码略少。

+0

哪个版本的SQL Server? – 2009-08-25 08:03:50

+0

SQL Server 2000.这会有所作为吗? – dave 2009-08-25 08:05:25

+0

SQL 2005和更高版本中有一些功能使这种查询更容易。 – 2009-08-25 08:14:10

回答

0

这不是微不足道的。首先,你需要另一列“标志”,这是0:

INSERT INTO Results (year, month, day, hour, duration, court, Flag) 
SELECT DATEPART (yy, b.StartDateTime), 
     DATEPART (mm, b.StartDateTime), 
     DATEPART (dd, b.StartDateTime), 
     DATEPART (hh, b.StartDateTime), 
     a.Duration, 
     a.Court, 
     0 
FROM Bookings b 
INNER JOIN Activities a 
ON b.ActivityID = a.ID 

你需要运行这些查询多次:

-- Copy all rows with duration > 1 and set the flag to 1 
insert into results(year, month, day, hour, duration, court, Flag) 
select year, month, day, hour+1, duration-1, court, 1 
from result 
where duration > 1 
; 
-- Set the duration of all copied rows to 1 
update result 
set duration = 1 
where flag = 0 and duration > 1 
; 
-- Prepare the copies for the next round 
update result 
set flag = 0 
where flag = 1 

这将为每个duration > 1其他条目。我的猜测是,你不能分配一个法庭8小时以上,所以你只需要运行这三个8次,解决所有问题。

+0

我想我遵循这个。但是,您的第二个查询不需要将持续时间减少1,而不是将其设置为1? – dave 2009-08-25 10:29:12

+0

@dave:不,它通过将持续时间设置为1来将图片中的所有进程查询都带出。所有带有“flag = 1”的结果将包含新的“duration-1”。这就是为什么我需要国旗(要知道“删除”的持续时间) – 2009-08-25 12:40:22

+0

@Aaron,我现在明白了。一旦我在脑海中开始一个例子,很明显。我假设“迭代”将基于“父”行,即。持续时间> 1的原始行。但是您的解决方案会创建一个子行来迭代。这很聪明。也许我会在while循环中包装整个事物,检查是否有剩余时间大于1的行。这样,我不必担心可以预订或浪费迭代的最大小时数。 – dave 2009-08-25 21:56:53

1

编辑以纠正失踪数计算

创建ñ行整数ň单列临时表 - (我认为最大的售票时间为8小时)。

create table #t 
(id int 
,addHour int 
) 

insert #t 
select 1,0 
union all select 2,0 
union all select 2,1 
union all select 3,0 
union all select 3,1 
union all select 3,2 
union all select 4,0 
union all select 4,1 
union all select 4,2 
union all select 4,3 
union all select 5,0 
union all select 5,1 
union all select 5,2 
union all select 5,3 
union all select 5,4 
union all select 6,0 
union all select 6,1 
union all select 6,2 
union all select 6,3 
union all select 6,4 
union all select 6,5 
union all select 7,0 
union all select 7,1 
union all select 7,2 
union all select 7,3 
union all select 7,4 
union all select 7,5 
union all select 7,6 
union all select 8,0 
union all select 8,1 
union all select 8,2 
union all select 8,3 
union all select 8,4 
union all select 8,5 
union all select 8,6 
union all select 8,7 

您可以验证该临时表与下面的查询正确的行数:

select id, count(1) 
from #t 
group by id 
order by id 

修改您的查询,包括联接到临时表:

INSERT INTO Results (year, month, day, hour, duration, court) 
SELECT DATEPART (yy, b.StartDateTime), 
     DATEPART (mm, b.StartDateTime), 
     DATEPART (dd, b.StartDateTime), 
     DATEPART (hh, b.StartDateTime) + addHour, 
     1 AS Duration, 
     a.Court 
FROM Bookings b 
INNER JOIN Activities a 
ON b.ActivityID = a.ID 
INNER JOIN #t AS t 
ON t.id = a.Duration 

编辑 - 这是如何工作的

澄清10

当联接表,一排在输出对于其中满足连接标准的源表的行接合的每个组合产生的。

我使用的是临时表为“正片叠底”的原始结果的小时数从预订和活动设置预约持续通过连接上持续时间。这只适用于预订时间为整个小时数的情况。

如果你想看到这个更清楚,添加第二个栏#T唯一标识每一行,并在输出中包含的结果集:

create table #t 
(id int 
,unique_id int identity 
) 

INSERT #t (id) 
select 1 
union all select 2 
... etc 

SELECT DATEPART (yy, b.StartDateTime), 
     DATEPART (mm, b.StartDateTime), 
     DATEPART (dd, b.StartDateTime), 
     DATEPART (hh, b.StartDateTime) + addHour, 
     1 AS Duration, 
     a.Court, 
     t.unique_id 
FROM Bookings b 
INNER JOIN Activities a 
ON b.ActivityID = a.ID 
INNER JOIN #t AS t 
ON t.id = a.Duration 

这应该澄清的是,在结果的每一行集合是由预订,活动和#t的单个有效组合生成的。

+0

你能解释一下它是如何工作的吗?我了解临时表的创建,但不了解#t的联结如何产生期望的结果。 – dave 2009-08-25 10:34:05

+0

这不会在结果中显示正确的小时数。 – 2009-08-25 12:41:37

+0

@Aaron - 好点。我已经修改了查询来处理这个问题。 – 2009-08-25 13:08:56

0

你可能会考虑把一个INSTEAD OF INSERT触发器在桌子上“结果”,将插入多行每个更比一小时预订。这确实增加了复杂性,但这可能是一种合理的方法,因为这听起来不像大容量的OLTP系统。

1

一个稍微的改变你原来的就足够了,如果你介绍一个integers表(或VIEW)作为一个系列发电机:

INSERT INTO Results (year, month, day, hour, duration, court) 
SELECT DATEPART (yy, b.StartDateTime), 
     DATEPART (mm, b.StartDateTime), 
     DATEPART (dd, b.StartDateTime), 
     DATEPART (hh, b.StartDateTime) + (a.Duration - i.iii - 1) 
     1, 
     a.Court 
FROM Bookings b 
INNER JOIN Activities a 
    ON b.ActivityID = a.ID 
INNER JOIN Integers999 i  -- N.B.: Integers999 (iii INT), all numbers 0 .. 999 
    ON a.Duration > i.iii;  -- So, a true Duration of 1 appears once, of 2 twice ... 
+0

你忘了修复开始时间。 – 2009-08-26 07:11:58

+0

@Aaron,所以我做了,谢谢。固定。 – pilcrow 2009-08-26 12:29:42

0

我还没有机会来调试这一点,但像这应该为你做:

DECLARE @maxDuration INTEGER 
DECLARE @curDuration INTEGER 

SELECT @MaxDuration = SELECT MAX(Duration) FROM Activities 
SET @curDuration = 1 

WHILE @curDuration <= @MaxDuration 
BEGIN 
    INSERT INTO Results (year, month, day, hour, duration, court) 
    SELECT DATEPART (yy, b.StartDateTime), 
      DATEPART (mm, b.StartDateTime), 
      DATEPART (dd, b.StartDateTime), 
      DATEPART (hh, b.StartDateTime) + @curDuration - 1, 
      a.Duration, 
      a.Court 
    FROM Bookings b 
    INNER JOIN Activities a 
    ON b.ActivityID = a.ID 
    WHERE a.Duration <= @MaxDuration 

    SET @curDuration = @curDuration + 1 
END