Цикл SQL - PullRequest
       37

Цикл SQL

0 голосов
/ 17 февраля 2009

Я создаю этот отчет в системе для компании Billboard. У них есть таблица, в которой хранятся все их рекламные щиты, и, помимо других данных, рекламные щиты имеют дату начала и дату окончания (оба могут быть нулевыми). Если по какой-либо причине рекламные щиты перестают использоваться, они устанавливают дату окончания, и после этой даты они станут недоступны для использования. То же самое с датой начала (в случае, если они только установили новую доску, и она будет доступна для использования после установленной даты начала).

В этом отчете я должен получить сумму всех возможных рекламных мест за определенный период времени.

Итак, допустим, что за выбранный период (4 недели, например)
* На 1 неделе 500 доступных досок
* На 2-й неделе стало недоступно одно правление (доступно 498 досок)
* На 3 неделе стали доступны две доски (в наличии 501 доска)
* На 4-й неделе одна доска стала доступной, а другая - недоступной (что сделало 501 доску доступной)

Итак, у меня должно быть в общей сложности 1990 доступных досок за этот период, вот результат, к которому я стремлюсь.

Как я могу получить это в одном запросе?
Недели придут из формы HTML, и мне нужно преобразовать их в даты перед запросом, поэтому у меня просто будут даты, на которые я хочу узнать, сколько досок доступно на сайте. Запрос на получение количества досок на ОДНУ конкретную дату выглядит так:

SELECT  
  COUNT(IdBillboard)  
FROM  
  tBillboards  
WHERE  
  (StartDate IS NULL OR StartDate <= '2009-01-05 00:00:00')  
  AND (FinishDate IS NULL OR FinishDate >= '2009-01-05 00:00:00')  

Я не могу просто добавить условные выражения AND и OR для каждой даты, потому что мне нужно новый пересчет для отдельных дат. Я думал об использовании WHILE, но не мог понять, как именно это сделать для этого случая. Так вот где я застрял ... Я опубликую больше деталей, если они кому-нибудь понадобятся.

Спасибо, Гейб

Ответы [ 7 ]

3 голосов
/ 17 февраля 2009

Не знаю, если это то, что вы ищете, но может быть шагом в правильном направлении.

SELECT  
  SUM(CASE WHEN 
   (StartDate IS NULL OR StartDate <= '2009-01-05 00:00:00')  
      AND (FinishDate IS NULL OR FinishDate >= '2009-01-05 00:00:00')
    THEN 1
    ELSE 0 END)) Week1Count,
  SUM(CASE WHEN 
   (StartDate IS NULL OR StartDate <= '2009-01-12 00:00:00')  
      AND (FinishDate IS NULL OR FinishDate >= '2009-01-12 00:00:00')
    THEN 1
    ELSE 0 END)) Week2Count
FROM  
  tBillboards  
1 голос
/ 17 февраля 2009

Это не ответ на ваш вопрос, но вы должны рассмотреть возможность использования BETWEEN вместо AND s.

WHERE  
  (StartDate IS NULL) OR 
  StartDate BETWEEN '2009-01-05' AND '2009-01-05'

ИМО, это гораздо более читабельно.

0 голосов
/ 17 февраля 2009

Это даст вам общее количество за первые 4 недели 2009 года. Чтобы проверить еще один год, измените два вхождения 2009 в приведенном ниже запросе на интересующий вас год.

select sum(Count) as Total
from (
    SELECT w.Week, Count(*) as Count
    FROM tBillboards b
    inner join (
        select 1 as Week
        union all
        select 2
        union all
        select 3
        union all
        select 4
    ) w on StartDate is null or datepart(ww, StartDate) <= w.Week
        and FinishDate is null or datepart(ww, FinishDate) >= w.Week
    where isnull(year(StartDate), 2009) <= 2009
        and isnull(year(FinishDate), 2009) >= 2009
    group by w.Week
) a 

Если вам нужно больше, чем в первые 4 недели, продолжайте добавлять числа к внутреннему запросу с UNIONs в нем.

0 голосов
/ 17 февраля 2009

Предполагая, что вы используете SQL Server 2005 или более позднюю версию, вы можете сделать это с помощью CTE, как в следующем примере. В моем примере вам нужно установить дату и количество недель, которые вы хотите просмотреть с этой даты:

DECLARE @dtNow DATETIME
DECLARE @iNumberOfWeeks INT
SET @dtNow = '2009-01-21'
SET @iNumberOfWeeks = 2;


WITH CountForPeriod (IdBillBoard, StartDate, FinishDate, NumberOfWeeksBack) AS
(
    SELECT IdBillBoard, StartDate, FinishDate, 1 AS NumberOfWeeksBack
    FROM tBillBoards
    WHERE (StartDate <= @dtNow)
    AND (FinishDate >= @dtNow)

    UNION ALL

    SELECT b.IdBillBoard, b.StartDate, b.FinishDate, 1 + NumberOfWeeksBack
    FROM tBillBoards b
    JOIN CountForPeriod c
    ON (b.StartDate <= DATEADD(dd, -7 * c.NumberOfWeeksBack, @dtNow))
    AND (b.FinishDate >= DATEADD(dd, -7 * c.NumberOfWeeksBack, @dtNow))
)
SELECT NumberOfWeeksBack, COUNT(*)
FROM CountForPeriod
WHERE NumberOfWeeksBack <= @iNumberOfWeeks
GROUP BY NumberOfWeeksBack
0 голосов
/ 17 февраля 2009

В SQL нет WHILE. Чтобы зациклить, вы присоединяетесь к другой таблице.

Вы не указываете это, но я полагаю, что рекламные щиты меняют доступность в один день недели, например, в воскресенье, и они не могут меняться несколько раз в неделю.

Что вам нужно сделать, это создать таблицу всех возможных недель, когда это изменение может произойти, назовем его DT для даты с одним столбцом даты x, который имеет одну дату в неделю (воскресенье?).

Тогда сделайте что-то вроде этого:

select count(*) from tBillboards b
inner join DT d
    on (b.startdate is null or b.startdate <= d.x)
       and (b.finishdate is null or b.finishdate < d.x)
where d.x between period_start and period_end
0 голосов
/ 17 февраля 2009
SELECT  COUNT(*)
FROM
    (
    SELECT DATEADD(ww, num, @initdate) AS wd
    FROM (
        SELECT 0 AS num
        UNION ALL
        SELECT 1
        UNION ALL
        SELECT 2
        UNION ALL
        SELECT 3
        ) w
    ) weeks, Billboards bb
WHERE wd BETWEEN NULLIF(bb.StartDate, wd) AND NULLIF(bb.EndDate, wd)
0 голосов
/ 17 февраля 2009

Если вы можете получить даты из формы в (возможно, временную) таблицу, тогда ваш запрос может быть:

SELECT  
  COUNT(t.IdBillboard)  
FROM  
  tBillboards r, tDates d
WHERE  
  (t.StartDate IS NULL OR StartDate <= d.date)  
  AND (t.FinishDate IS NULL OR FinishDate >= d.date)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...