Кросс-таблица - хранение разных дат (Встреча1, Встреча2, Встреча 3 и т. Д.) В одном столбце - PullRequest
4 голосов
/ 16 сентября 2009

Мне нужно отслеживать разные даты (динамические). Таким образом, для конкретной задачи у вас может быть Х число дат для отслеживания (например, дата собрания DDR1, дата собрания DDR2, срок выполнения и т. Д.).

Моя стратегия состояла в том, чтобы создать одну таблицу (DateTypeID, DateDescription), в которой будет храниться описание каждой даты. Тогда я мог бы создать основную таблицу (ID, TaskDescription, DateTypeID). Таким образом, все даты будут в одном столбце, и вы сможете узнать, что эта дата представляет, посмотрев на TypeID. Проблема заключается в отображении его в сетке. Я знаю, что должен использовать перекрестный запрос, но не могу заставить его работать. Например, я использую оператор Case в SQL Server 2000, чтобы повернуть таблицу так, чтобы каждое имя столбца было именем типа даты. Если у нас есть следующие таблицы:

Таблица DateType

DateTypeID | DateDescription

 1           | DDR1
 2           | DDR2
 3           | DueDate


Таблица задач

ID | TaskDescription

1 | Create Design
2 | Submit Paperwork


Таблица Tasks_DateType

TasksID | DateTypeID | Дата

1       |     1         | 09/09/2009
1       |     2         | 10/10/2009
2       |     1         | 11/11/2009
2       |     3         | 12/12/2009


РЕЗУЛЬТАТ ДОЛЖЕН БЫТЬ:

TaskDescription | DDr1 | DDR2 | DueDate

Create Design     |09/09/2009 | 10/10/2009 | null
Submit Paperwork  |11/11/2009 | null       | 12/12/2009

Если кто-нибудь знает, как я могу исследовать это, я ценю это. Причина, по которой я делаю это вместо создания столбца для каждой даты, связана с возможностью позволить пользователю в будущем добавлять столько дат, сколько он хочет, без необходимости вручную добавлять столбцы в таблицу и редактировать HTML-код. Это также позволяет использовать простой код для сравнения дат или отображения предстоящих задач по их типу (например, «Дата создания DDR1 для дизайна»). Если кто-то может указать мне правильное направление, я ценю это.

Ответы [ 4 ]

2 голосов
/ 16 сентября 2009

Вот правильный ответ, проверенный вашими данными. Я использовал только первые два типа дат, но в любом случае вы создали бы это на лету.

Select 
    Tasks.TaskDescription,     
    Min(Case DateType.DateDescription When 'DDR1' Then Tasks_DateType.Date End) As DDR1,     
    Min(Case DateType.DateDescription When 'DDR2' Then Tasks_DateType.Date End) As DDR2
From
    Tasks_DateType
    INNER JOIN Tasks ON Tasks_DateType.TaskID = Tasks.TaskID
    INNER JOIN DateType ON Tasks_DateType.DateTypeID = DateType.DateTypeID
Group By
    Tasks.TaskDescription

EDIT

Ван упомянул, что задачи без дат не будут отображаться. Это правильно. Использование левых объединений (опять же, упомянутое van) и небольшая реструктуризация запроса вернут все задачи, даже если в данный момент это не нужно.

Select 
    Tasks.TaskDescription,     
    Min(Case DateType.DateDescription When 'DDR1' Then Tasks_DateType.Date End) As DDR1,     
    Min(Case DateType.DateDescription When 'DDR2' Then Tasks_DateType.Date End) As DDR2
From
    Tasks   
    LEFT OUTER JOIN Tasks_DateType ON Tasks_DateType.TaskID = Tasks.TaskID
    LEFT OUTER  JOIN DateType ON Tasks_DateType.DateTypeID = DateType.DateTypeID
Group By
    Tasks.TaskDescription
0 голосов
/ 16 сентября 2009

Если поворотные столбцы неизвестны (динамические), то вам придется создать запрос вручную в ms-sql 2000 или 2005, т. Е. Без PIVOT.

Это включает в себя либо выполнение динамического SQL в хранимой процедуре (обычно нет-нет), либо запрос представления с динамическим SQL. Последний подход, с которым я обычно согласен.

Для поворота я предпочитаю метод Розенштейна, а не операторы case, как объяснено здесь:

http://www.stephenforte.net/PermaLink.aspx?guid=2b0532fc-4318-4ac0-a405-15d6d813eeb8

EDIT

Вы также можете сделать это в linq-to-sql, но он генерирует довольно неэффективный код (по крайней мере, когда я просматриваю его через linqpad), поэтому я не рекомендую его. Если вам все еще интересно, я могу опубликовать пример того, как это сделать.

0 голосов
/ 16 сентября 2009

Версия-1: + простой, -обязательно менять при каждом добавлении DateType. Так что не подходит для динамического решения:

SELECT      tt.ID,
            tt.TaskDescription,
            td1.Date AS DDR1,
            td2.Date AS DDR2,
            td3.Date AS DueDate
FROM        Tasks tt
LEFT JOIN   Tasks_DateType td1
        ON  td1.TasksID = tt.ID AND td1.DateTypeID = 1
LEFT JOIN   Tasks_DateType td2
        ON  td2.TasksID = tt.ID AND td2.DateTypeID = 2
LEFT JOIN   Tasks_DateType td3
        ON  td3.TasksID = tt.ID AND td3.DateTypeID = 3

Версия-2 : полностью динамическая (с некоторыми ограничениями, но они могут быть обработаны - просто Google для этого):

Динамическое создание сводного запроса. См. Динамические кросс-таблицы / сводные таблицы : вам нужно создать один SP UDF, а затем использовать его для нескольких целей. Это оригинальный пост, на который вы можете найти множество ссылок и улучшений.

Версия-3 : просто оставьте код своего клиента для обработки . Я не хотел бы, чтобы мой SQL возвращал динамический набор данных, а обрабатывал его на клиенте (уровень представления). Я просто не хотел бы обрабатывать некоторые динамические столбцы, которые появляются в результате моего запроса, где мне нужно угадать, что это такое. Единственная причина, по которой я использую Версия-2 , заключается в том, что результат представляется непосредственно в виде таблицы для отчета. Во всех других случаях для действительно динамических данных я использую код клиента. Например: если у вас есть структура, как вы будете прикреплять логику, чтобы поле DueDate было обязательным - вы не можете использовать ограничения БД; как вы убедитесь, что DDR1 не выше, чем DDR2? Если это не отдельные (статические) столбцы в базе данных (где вы можете использовать CONSTRAINTS), то именно клиентский код проверяет согласованность ваших данных.

Удачи!

0 голосов
/ 16 сентября 2009

У меня нет личного опыта с оператором pivot, это может обеспечить лучшее решение.

Но в прошлом я использовал оператор case

SELECT 
    TaskDescription, 
    CASE(DateTypeID = 1, Tasks_DateType.Date) AS DDr1, 
    CASE(DateTypeID = 2, Tasks_DateType.Date) AS DDr2,
    ...
FROM Tasks 
    INNER JOIN Tasks_DateType  ON Tasks.ID = Tasks_DateType.TasksID
    INNER JOIN DateType ON Tasks_DateType.DateTypeID = DateType.DateTypeID
GROUP BY TaskDescription

Это будет работать, но потребует от вас изменения SQL всякий раз, когда будет добавлено больше описаний задач, поэтому это не идеально.

EDIT:

Похоже, что ключевое слово PIVOT было добавлено в SqlServer 2005, в этом примере показано, как выполнить сводный запрос в 2000 и 2005 годах, но оно похоже на мой ответ.

...