Использование нескольких UNION ALL для построения представления: есть ли лучший способ? - PullRequest
2 голосов
/ 11 февраля 2010

Есть некоторые ограничения на этот вопрос; У меня нет возможности кардинально изменить структуру базы данных.

Проблема здесь в том, что у меня есть строки в базе данных, которые содержат информацию, которая действительно должна быть в отдельной строке. Несколько упрощенный пример структуры столбца:

[PersonID] [FirstName] [LastName] [FirstNameGuest1] [LastNameGuest1]
 1          Ringo       Starr      John              Lennon
 2          George      Harrison   Paul              McCartney

Мне нужно разделить их следующим образом, чтобы иметь возможность запускать нужные мне отчеты:

[PersonID] [FirstName] [LastName]
 1          Ringo       Starr
 1          John        Lennon
 2          George      Harrison
 2          Paul        McCartney

Поскольку я использую это для создания представления, мне пришлось просто ссылаться на одну и ту же таблицу для каждой группы гостевых столбцов и использовать UNION ALLs, чтобы связать их все вместе.

Однако с тех пор мне приходилось строить запросы все возрастающей сложности поверх производного представления. Каждый уровень сложности заставляет результаты возвращаться все медленнее.

Я придерживаюсь принципиально плохого подхода? Есть ли другие, более правильные способы моделирования данных так, как мне нужно?

Вот часть фактического запроса, чтобы вы могли видеть, с чем я имею дело:

--Primary Record
SELECT
 'Franchisee' AS 'Type', 
 Confirmation AS 'BelongingTo', 
 0 AS 'GuestNo', 
 FirstName, 
 LastName, 
 FF_27557_152972 AS 'HotelChoice', 
 HotelCheckIn, 
 HotelCheckOut, 
 HotelSmoking AS 'Smoking', 
 (CASE FF_27554_1 WHEN 'Yes' THEN 1 ELSE 0 END) AS 'PrimaryRoomHolder', 
 '' AS 'SharingWith', 
 'None' AS 'SharingWithName'
FROM dbo.[Table]
WHERE Type = 'Production' AND Submitted = 1 AND Cancelled = 0 AND Label = 'Primary'

UNION ALL
-- First Guest
SELECT
'Guest' AS 'Type',
Confirmation AS 'BelongingTo',
1 AS 'GuestNo', 
FF_27637_1 AS 'FirstName', 
FF_27637_152806 AS 'LastName', 
FF_27637_152822 AS 'HotelChoice', 
FF_27637_152813 AS 'HotelCheckIn', 
FF_27637_152821 AS 'HotelCheckOut', 
FF_27637_152824 AS 'Smoking', 
(CASE WHEN FF_27637_152822 IS NOT NULL THEN 1 ELSE 0 END) AS 'PrimaryRoomHolder', 
FF_27637_154245 AS 'SharingWith', 
(CASE CAST(FF_27637_154245 AS integer) 
  WHEN 0 THEN FirstName + ' ' + LastName 
  WHEN 1 THEN FF_27637_1 + ' ' + FF_27637_152806 
  WHEN 2 THEN FF_27742_1 + ' ' + FF_27742_153577 
  WHEN 3 THEN FF_27638_1 + ' ' + FF_27638_152814 
  WHEN 4 THEN FF_27639_1 + ' ' + FF_27639_152817 
  WHEN 5 THEN FF_27640_1 + ' ' + FF_27640_152852
  WHEN 6 THEN FF_27641_1 + ' ' + FF_27641_152860 
  WHEN 7 THEN FF_27642_1 + ' ' + FF_27642_152868 
  WHEN 8 THEN FF_27643_1 + ' ' + FF_27643_152877
  WHEN 9 THEN FF_27644_1 + ' ' + FF_27644_152885 
  WHEN 10 THEN FF_27645_1 + ' ' + FF_27645_152893 
  ELSE 'None' END) AS 'SharingWithName'
FROM dbo.Event_213_1546 AS Event_213_1546_10
WHERE Type = 'Production' AND Submitted = 1 AND Cancelled = 0 AND Label = 'Primary' AND FF_27637_1 IS NOT NULL
.
.
.
(Iterates through 9 more guests exactly like "First Guest")

Ответы [ 3 ]

2 голосов
/ 11 февраля 2010

Рассматривали ли вы оператора UNPIVOT? Он делает то же самое, но может быть менее болезненным (возможно! :-). Требуется 2005 или более поздняя версия.

http://blogs.msdn.com/craigfr/archive/2007/07/17/the-unpivot-operator.aspx

Пример:

declare @names table ( personid int, 
    firstname1 varchar(50), 
    lastname1 varchar(50),
    firstname2 varchar(50), 
    lastname2 varchar(50),
    firstname3 varchar(50), 
    lastname3 varchar(50)

    -- <etc.>
    )

Insert @names values ( 1, 'Fred', 'Flintstone', 'Barney', 'Rubble', 'Wilma', 'Flintstone' )
Insert @names values ( 2, 'Super', 'Man', 'Aqua', 'Man', 'Wonder', 'Woman' )

select * from @names

select personid, firstnamecol, firstname, lastnamecol, lastname
from @names
unpivot( firstname for firstnamecol in ( firstname1, firstname2, firstname3 ) ) firstnames
unpivot( lastname for lastnamecol in ( lastname1, lastname2, lastname3 ) ) lastnames
where right(firstnamecol, 1) = right( lastnamecol, 1 ) -- This is the tricky bit

Использование нескольких откручиваний в одном выборе довольно сложно; Я получил вышеупомянутое понятие от этих парней:

http://weblogs.sqlteam.com/jeffs/archive/2008/04/23/unpivot.aspx http://mangalpardeshi.blogspot.com/2009/04/unpivot-multiple-columns.html

Важно отметить, что часть, помеченная как "хитрая", будет разрываться с различными числами цифр в именах повторяющихся столбцов (например, FirstName11 и FirstName1 будут проблемой). Вы можете обойти это с некоторыми трюками substring (). Не самая лучшая идея, но ...

Дополнение, 22 февраля: Это феноменальная статья о развороте: http://bradsruminations.blogspot.com/2010/02/spotlight-on-unpivot-part-1.html

2 голосов
/ 11 февраля 2010

Зависит от того, что вы пытаетесь сделать с представлением.
Запрос представления должен выполняться перед любыми операциями, которые вы над ним выполняете. Так что, если вы делаете вещи, которые имеют дело только с 10% результатов, возвращаемых представлением, это пустая трата обработки. Вполне вероятно, что выполненные операции могут быть выполнены одновременно с логикой, уже имеющейся в представлении.

Можете ли вы добавить индексы, если необходимо? Это помогло бы ...

Материализованное представление, называемое индексированным представлением в терминологии SQL Server , является жизнеспособной возможностью, но общеизвестно, что оно не используется.

1 голос
/ 11 февраля 2010

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

Концептуально оператор UNPIVOT моделирует именно то, что я пытаюсь выполнить; еще неизвестно, не было ли у него какого-либо ограничения реализации, которое помешало бы мне использовать его. Поскольку у меня было много столбцов для «распаковки», было два основных подхода к использованию оператора UNPIVOT. (1) Я мог бы запустить отдельный UNPIVOT для каждого рассматриваемого столбца, или (2) , я мог бы запустить один UNPIVOT и затем условно вычислить остальные столбцы на основе этого результата , Я выбрал второй подход, потому что мне было немного легче его понять, но я все еще заинтересован в том, чтобы проверить их в будущем.

Результирующий запрос по-прежнему не элегантно повторяется (он должен повторять один и тот же блок из десяти условий), но он невероятно упрощен по сравнению с чудовищностью, с которой я работал изначально. Самое главное, когда я запускаю это в соответствии со старым запросом, это только 9% от общей стоимости запроса, по сравнению с 91% от старого запроса. Так что, похоже, он также намного эффективнее.

Вот переработанный запрос (все еще длинный, но, вероятно, только около 20% от длины старого)

SELECT
    Case(FN) WHEN 'FirstName' THEN 'Primary' ELSE 'Guest' END as RegType,
    (Case(FN) WHEN 'FirstName' THEN 0
        WHEN 'FF_27637_1' THEN 1
        WHEN 'FF_27742_1' THEN 2
        WHEN 'FF_27638_1' THEN 3
        WHEN 'FF_27639_1' THEN 4
        WHEN 'FF_27640_1' THEN 5
        WHEN 'FF_27641_1' THEN 6
        WHEN 'FF_27642_1' THEN 7
        WHEN 'FF_27643_1' THEN 8
        WHEN 'FF_27644_1' THEN 9
        WHEN 'FF_27645_1' THEN 10 END) as GuestNo,  
    Confirmation, 
    FirstNames as FirstName,
    (Case(FN) WHEN 'FirstName' THEN LastName
        WHEN 'FF_27637_1' THEN FF_27637_152806
        WHEN 'FF_27742_1' THEN FF_27742_153577
        WHEN 'FF_27638_1' THEN FF_27638_152814
        WHEN 'FF_27639_1' THEN FF_27639_152817
        WHEN 'FF_27640_1' THEN FF_27640_152852
        WHEN 'FF_27641_1' THEN FF_27641_152860
        WHEN 'FF_27642_1' THEN FF_27642_152868
        WHEN 'FF_27643_1' THEN FF_27643_152877
        WHEN 'FF_27644_1' THEN FF_27644_152885
        WHEN 'FF_27645_1' THEN FF_27645_152893 END) as LastName,
    (Case(FN) WHEN 'FirstName' THEN Email
        WHEN 'FF_27637_1' THEN FF_27637_152807
        WHEN 'FF_27742_1' THEN FF_27742_153578
        WHEN 'FF_27638_1' THEN FF_27638_152815
        WHEN 'FF_27639_1' THEN FF_27639_152818
        WHEN 'FF_27640_1' THEN FF_27640_152853
        WHEN 'FF_27641_1' THEN FF_27641_152861
        WHEN 'FF_27642_1' THEN FF_27642_152869
        WHEN 'FF_27643_1' THEN FF_27643_152878
        WHEN 'FF_27644_1' THEN FF_27644_152886
        WHEN 'FF_27645_1' THEN FF_27645_152894 END) as Email,
    (Case(FN) WHEN 'FirstName' THEN HotelCheckOut
        WHEN 'FF_27637_1' THEN FF_27637_152821
        WHEN 'FF_27742_1' THEN FF_27645_152896
        WHEN 'FF_27638_1' THEN FF_27742_153580
        WHEN 'FF_27639_1' THEN FF_27638_152847
        WHEN 'FF_27640_1' THEN FF_27639_152842
        WHEN 'FF_27641_1' THEN FF_27640_152855
        WHEN 'FF_27642_1' THEN FF_27641_152863
        WHEN 'FF_27643_1' THEN FF_27642_152871
        WHEN 'FF_27644_1' THEN FF_27643_152880
        WHEN 'FF_27645_1' THEN FF_27644_152888 END) as HotelChoice,
    (Case(FN) WHEN 'FirstName' THEN HotelCheckIn
        WHEN 'FF_27637_1' THEN FF_27637_152813
        WHEN 'FF_27742_1' THEN FF_27742_153579
        WHEN 'FF_27638_1' THEN FF_27638_152816
        WHEN 'FF_27639_1' THEN FF_27639_152819
        WHEN 'FF_27640_1' THEN FF_27640_152854
        WHEN 'FF_27641_1' THEN FF_27641_152862
        WHEN 'FF_27642_1' THEN FF_27642_152870
        WHEN 'FF_27643_1' THEN FF_27643_152879
        WHEN 'FF_27644_1' THEN FF_27644_152887
        WHEN 'FF_27645_1' THEN FF_27645_152895 END) as HotelCheckIn,
    (Case(FN) WHEN 'FirstName' THEN HotelCheckOut
        WHEN 'FF_27637_1' THEN FF_27637_152821
        WHEN 'FF_27742_1' THEN FF_27742_153580
        WHEN 'FF_27638_1' THEN FF_27638_152847
        WHEN 'FF_27639_1' THEN FF_27639_152842
        WHEN 'FF_27640_1' THEN FF_27640_152855
        WHEN 'FF_27641_1' THEN FF_27641_152863
        WHEN 'FF_27642_1' THEN FF_27642_152871
        WHEN 'FF_27643_1' THEN FF_27643_152880
        WHEN 'FF_27644_1' THEN FF_27644_152888
        WHEN 'FF_27645_1' THEN FF_27645_152896 END) as HotelCheckOut,
    (Case(FN) WHEN 'FirstName' THEN HotelRoomPreference
        WHEN 'FF_27637_1' THEN FF_27637_152823
        WHEN 'FF_27742_1' THEN FF_27742_153582
        WHEN 'FF_27638_1' THEN FF_27638_152849
        WHEN 'FF_27639_1' THEN FF_27639_152844
        WHEN 'FF_27640_1' THEN FF_27640_152857
        WHEN 'FF_27641_1' THEN FF_27641_152865
        WHEN 'FF_27642_1' THEN FF_27642_152874
        WHEN 'FF_27643_1' THEN FF_27643_152882
        WHEN 'FF_27644_1' THEN FF_27644_152890
        WHEN 'FF_27645_1' THEN FF_27645_152898 END) as RoomType,
    (Case(FN) WHEN 'FirstName' THEN HotelSmoking
        WHEN 'FF_27637_1' THEN FF_27637_152824
        WHEN 'FF_27742_1' THEN FF_27742_153583
        WHEN 'FF_27638_1' THEN FF_27638_152850
        WHEN 'FF_27639_1' THEN FF_27639_152845
        WHEN 'FF_27640_1' THEN FF_27640_152858
        WHEN 'FF_27641_1' THEN FF_27641_152866
        WHEN 'FF_27642_1' THEN FF_27642_152875
        WHEN 'FF_27643_1' THEN FF_27643_152883
        WHEN 'FF_27644_1' THEN FF_27644_152891
        WHEN 'FF_27645_1' THEN FF_27645_152899 END) as Smoking,
    (Case(FN) WHEN 'FirstName' THEN NULL
        WHEN 'FF_27637_1' THEN FF_27637_154245
        WHEN 'FF_27742_1' THEN FF_27742_154247
        WHEN 'FF_27638_1' THEN FF_27638_154249
        WHEN 'FF_27639_1' THEN FF_27639_154251
        WHEN 'FF_27640_1' THEN FF_27640_154253
        WHEN 'FF_27641_1' THEN FF_27641_154255
        WHEN 'FF_27642_1' THEN FF_27642_154257
        WHEN 'FF_27643_1' THEN FF_27643_154259
        WHEN 'FF_27644_1' THEN FF_27644_154261
        WHEN 'FF_27645_1' THEN FF_27645_154263 END) as SharingWith,

FROM Event
UNPIVOT (FirstNames for FN in (FirstName, FF_27637_1, FF_27742_1, FF_27638_1, FF_27639_1, FF_27640_1, FF_27641_1, FF_27642_1, FF_27643_1, FF_27644_1, FF_27645_1)) as FirstNames
WHERE Audience = 'Primary' and Submitted = 1 and Cancelled = 0 and Type = 'Production' ORDER BY Confirmation

В качестве примечания, если я пытаюсь запустить это как представление, я получаю сообщение об ошибке, что UNPIVOT не поддерживается. Если я помещу ту же самую вещь в представление и сделаю ее производной таблицей - SELECT * FROM (...query...)) - я получу то же самое предупреждение в Studio Manager, но он возвращает результаты просто отлично. Weird.

Еще раз спасибо за все ваши ответы.

...