Одна или несколько вставок на основе значений, выбранных - PullRequest
2 голосов
/ 25 августа 2009

Мы извлекаем информацию о бронировании наших теннисных кортов из нашей базы данных 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:00, приведет к появлению трех строк в таблице результатов, одна в 9:00, одна в 10:00 и одна в 11:00, каждая продолжительностью 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-способ ведения дел.

Однако, в конце концов, я остановился на решении Аарона, включающем флаг и простой алгоритм. Я улучшил его, поместив его алгоритм в цикл while, чтобы продолжать итерацию до тех пор, пока не останется больше 1. Это было быстро и легко осуществить. Это также подчеркнуло, что у нас было около 10 часов бронирования, поэтому мне не нужно было жестко задавать ограничение здесь.

Следует отметить, что я включил идею максимальной длительности Джеффа в счетчик цикла while, а не свою первоначальную идею подсчета элементов с длительностью> 1. Чуть меньше кода.

Ответы [ 5 ]

1 голос
/ 26 августа 2009

Достаточно небольшого изменения вашего оригинала, если вы введете таблицу целых чисел (или 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 ...
1 голос
/ 25 августа 2009

Отредактировано для корректировки расчета недостающего часа

Создайте временную таблицу из одного столбца с 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

Это должно прояснить, что каждая строка в результирующем наборе создается из одной действительной комбинации Bookings, Activity и # t.

0 голосов
/ 26 августа 2009

У меня не было возможности отладить это, но что-то вроде этого должно сделать это для вас:

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
0 голосов
/ 25 августа 2009

Возможно, вы захотите поместить триггер INSTEAD OF INSERT в таблицу «Результаты», которая будет вставлять несколько строк для каждого бронирования более чем на час. Это добавляет сложности, но это может быть разумным подходом, поскольку это не похоже на систему с большим объемом OLTP.

0 голосов
/ 25 августа 2009

Это не тривиально. Во-первых, вам нужен еще один столбец «Флаг», который равен 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 раз, чтобы исправить все из них.

...