基于SELECTed值的单个或多个INSERT

时间:2009-08-25 07:56:45

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

我们正在将我们的网球场的预订信息从我们的SQL数据库中提取到一个简单的结果表中,以帮助我们建立法院使用情况的图片。除了涉及超过一个小时的预订外,它非常简单。

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

目前,我们的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

这样可以更轻松地将结果表映射到电子表格中。

更新2009-08-25:当然,正如前几个答案所示,它不一定是单个查询。一套很好。

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

更新2009-08-27:终于有机会尝试解决方案。整数表和加入以产生解决方案令人大开眼界。特别是使用交叉连接来创建这样的表。这可能是更简洁的SQL方式。

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

我应该注意到,我将Jeff的最大持续时间的想法纳入了while循环计数器,而不是我最初计算持续时间> gt的项目的想法。 1.代码略少。

5 个答案:

答案 0 :(得分:1)

已修改以更正缺失的小时计算

为整数 n 创建一个 n 行的单列临时表 - (我假设最长预订时间是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

编辑 - 澄清其工作原理

连接表时,在输出中为源表中符合连接条件的连接行的每个组合生成一行。

我正在使用临时表通过加入持续时间,将预订和活动中的原始结果集“乘以”预订持续的小时数。这仅适用于整数小时的预订。

如果您想更清楚地看到这一点,请在#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的单个有效组合中产生的。

答案 1 :(得分: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 ...

答案 2 :(得分:0)

这不是微不足道的。首先,您需要另一列“Flag”,即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次来修复所有这些。

答案 3 :(得分:0)

您可以考虑在“结果”表上放置一个INSTEAD OF INSERT触发器,该表将为每个超过一小时的预订插入多行。这确实增加了复杂性,但它可能是一种合理的方法,因为这听起来不像是高容量的OLTP系统。

答案 4 :(得分: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