2012-03-09 4 views
0

Я пытаюсь написать SQL-запрос для моей базы данных, который будет извлекать все конкретные события пользователей. Если они отмечены как повторяющиеся, я хочу, чтобы они показывались еженедельно в течение 52 недель после даты начала. Есть ли лучший способ сделать это? Мой текущий код кажется довольно неуклюжим.Ошибка с одним SQL-запросом

Мои таблицы построены следующим образом:

event (
    event_id INT NOT NULL AUTO_INCREMENT, (PK) 
    title VARCHAR(80) NOT NULL, 
    description VARCHAR(200), 
    event_start DATETIME, 
    event_end DATETIME, 
    group_id INT NOT NULL, 
    recurring BOOLEAN 
); 

    Users{ 
    UserID (PK) 
    Username 
    } 

    Groups{ 
    GroupID (PK) 
    GroupName 
    } 

    Membership{ 
    UserID (FK) 
    GroupID (FK) 
    } 

Вот запрос у меня есть на данный момент, он не будет работать в данный момент, может кто-нибудь, пожалуйста, помогите мне с этим?

var result = db.Query(
      @"SELECT e.event_id, e.title, e.description, e.event_start, e.event_end, e.group_id, e.recurring 
      FROM event e 
      JOIN Membership m ON m.GroupID = e.group_id 
      WHERE e.recurring 
      AND m.UserID = ? 
      AND e.event_start >= ? 
      AND e.event_end <= ? 
      UNION ALL 
      SELECT e.event_id, e.title, e.description, DATEADD(week, w.weeks, e.event_start), 
        DATEADD(week, w.weeks, e.event_end), e.group_id, e.recurring 
      FROM event e 
      JOIN Membership m ON m.GroupID = e.group_id, 
      (SELECT row_number() OVER (ORDER BY Object_ID) AS weeks 
        FROM SYS.OBJECTS) AS w 
      WHERE NOT e.recurring 
      AND m.user_id = ? 
      AND e.event_start >= ? 
      AND e.event_end <= ?", username, start, end, username, start, end 
     ); 

Может кто-нибудь помочь мне адаптироваться мой код так, что он выбирает все события, которые не повторяющееся и выбирает повторяющиеся события, в то же время в течение 52 недель?

+0

Как примечание стороны, помните, что год не имеет 52 недель. –

+0

52.177457 недель! Легче округлить, хотя, нет ?! :) –

+1

Я видел системы, которые получают еще пару уродливых хаков в слепой панике каждый раз, когда в текущем году нет 52 недель. –

ответ

1

может выглядеть следующим образом:

SELECT e.event_id, e.title, e.description, e.start_time, e.end_time 
    , e.group_id, e.recurring 
FROM Events e 
JOIN Membership m ON m.groupid = e.group_id 
WHERE e.recurring = 0 
AND m.user_id = ? 
AND e.start_time >= ? 
AND e.end_time <= ? 

UNION ALL 
SELECT e.event_ID, e.title, e.description 
     ,DATEADD(week, w.weeks, e.start_time) -- AS event_start 
     ,DATEADD(week, w.weeks, e.end_time) -- AS event_end 
     ,e.group_id, e.recurring 
FROM Events e 
JOIN Membership m ON m.groupid = e.group_id 
     ,(SELECT row_number() OVER (ORDER BY Object_ID) AS weeks 
     FROM SYS.OBJECTS) AS w 
WHERE e.recurring = 1 
AND m.user_id = ? 
AND e.start_time >= ? 
AND e.end_time <= ? 
  • Использование UNION ALL вместо UNION.

  • event_start и event_end вместо start_time и end_time (??) я изменил это.

  • я пропустил, что тоже на первом: user_id в таблице Membership, так что вам нужно JOIN что, если вы хотите использовать его в ИНЕКЕ. Внешние столбцы ключей выглядят как group_id/groupid. (Я бы унифицировал правописание!)

  • В первой части запроса у вас было SELECT *. Лучше указать имена столбцов в таком запросе, или более поздние изменения в таблице ниже будут разбиты на удивление.

  • Мой первый проект имел вспомогательную функцию generate_series(), чтобы сгенерировать набор чисел 1-52 от here, но ваше исходное решение лучше, поэтому я вернул это изменение.

+0

Где я должен добавить функцию generate_series в свой код, чтобы интегрировать это? Большое спасибо за ответ - это просто немного по сравнению с моей головой, когда вы показываете вспомогательную функцию! :) –

+0

@SimonKiely: Извините, эта вспомогательная функция на самом деле не нужна. Ваш простой раствор проще. Я вернул это изменение. –

+0

Brandstetter, Привет, Я попытался интегрировать этот код в свою систему и получить ошибку: «Произошла ошибка при анализе запроса. [Номер строки токена = 4, смещение линии токена = 13, токен с ошибкой = И]] « –

1

Попробуйте это. Вы использовали event_start и event_stop в своем SQL, но они не были определены в таблице Events. Единственное, что вы перепутали, было start_time < = @CurrentTime AND end_time> = @CurrentTime. Я не думаю, что ваш код неуклюж. UNION ALL работает лучше, чем UNION (UNION работает как DISTINCT: сначала ORDER BY, а затем удаляет дубликаты).

DROP TABLE #Events 
CREATE TABLE #Events (
    event_id INT NOT NULL, 
    title VARCHAR(80) NOT NULL, 
    description VARCHAR(200), 
    start_time DATETIME, 
    end_time DATETIME, 
    group_id INT NOT NULL, 
    recurring bit 
); 

INSERT INTO #Events(event_id, title, description, start_time, end_time, group_id, recurring) VALUES 
(1, 'meeting1', 'meeting1', '20000101', '20000102', 1, 0), 
(2, 'meeting2', 'meeting2', '20120309', '20120310', 1, 0), 
(3, 'meeting3', 'meeting3', '20120311', '20120312', 1, 0), 
(10, 'meeting10', 'meeting10', '20000101', '20000102', 1, 1), 
(20, 'meeting20', 'meeting20', '20120309', '20120310', 1, 1), 
(30, 'meeting30', 'meeting30', '20120311', '20120312', 1, 1) 

DECLARE @CurrentTime DATETIME 
SET @CurrentTime = GETDATE() 

SELECT event_id, 
    title, 
    description, 
    start_time, 
    end_time, 
    group_id, 
    recurring 
FROM #Events 
WHERE recurring = 0 AND 
-- user_id = ? AND 
    start_time <= @CurrentTime AND 
    end_time >= @CurrentTime 

UNION ALL 

SELECT event_ID, 
    title, 
    description, 
    DATEADD(WEEK, Interval, start_time) [event_start], 
    DATEADD(WEEK, Interval, end_time) [event_end], 
    group_id, 
    recurring 
FROM #Events, 
    (SELECT ROW_NUMBER() OVER(ORDER BY Object_ID) [Interval] 
    FROM SYS.OBJECTS) as T 
WHERE recurring = 1 AND 
    Interval <= 52 AND 
-- user_id = ? AND 
    start_time <= @CurrentTime AND 
    end_time >= @CurrentTime 
; 
+0

Привет, спасибо за ответ. Это очень полезно. Проблема, которую я сейчас имею в виду, заключается в том, что столбец user_id содержится в таблице членства. Я хотел бы получить только события, связанные с пользователями, где group_id содержит свой user_id, поэтому таблица членства будет содержать group_id и несколько user_id, показывая всех пользователей, которые являются членами определенной группы. Если group_id используется в событии, я хотел бы только получить событие для одного из этих пользователей. –

+0

> Если в событии используется group_id, я хотел бы только получить это событие для одного из этих пользователей. Wim