TSQL Pivot без агрегатной функции

146

У меня такая таблица ...

CustomerID   DBColumnName   Data
--------------------------------------
1            FirstName      Joe
1            MiddleName     S
1            LastName       Smith
1            Date           12/12/2009
2            FirstName      Sam
2            MiddleName     S
2            LastName       Freddrick
2            Date           1/12/2009
3            FirstName      Jaime
3            MiddleName     S
3            LastName       Carol
3            Date           12/1/2009

И я хочу это ...

Возможно ли это с помощью PIVOT?

CustomerID  FirstName   MiddleName          LastName        Date
----------------------------------------------------------------------
1           Joe             S               Smith           12/12/2009
2           Sam             S               Freddrick       1/12/2009
3           Jaime           S               Carol           12/1/2009
ctrlShiftBryan
источник

Ответы:

106

Вы можете использовать агрегат MAX, он все равно будет работать. Максимум одного значения = это значение ..

В этом случае вы также можете 5 раз самостоятельно присоединиться к Customerid, фильтруя по dbColumnName по ссылке на таблицу. Может получится лучше.

gbn
источник
1
это на самом деле не сработает, если у вас есть 2 покупателя с одинаковым именем
Леонардо
1
Это будет работать. Помните, что DBColumnName - это метаданные - вы буквально фильтруете по «CustomerID = 1 AND DBColumnName = 'FirstName'». Конечно, это не работает, если у вас есть несколько строк FirstName для данного CustomerID, но если вы правильно создаете свои таблицы, и CustomerID, и DBColumnName являются частью вашего первичного ключа ...
утра,
7
Некоторый код / ​​насмешки в качестве примера были бы отличными и сделали бы этот ответ совершенно полным.
DavidScherer
168

Да, но почему !!??

   Select CustomerID,
     Min(Case DBColumnName When 'FirstName' Then Data End) FirstName,
     Min(Case DBColumnName When 'MiddleName' Then Data End) MiddleName,
     Min(Case DBColumnName When 'LastName' Then Data End) LastName,
     Min(Case DBColumnName When 'Date' Then Data End) Date
   From table
   Group By CustomerId
Чарльз Бретана
источник
2
^^ Это сработало для меня. PIVOT неэффективен для нечисловых значений.
Dienekes
6
Это отличная альтернатива. Я использовал Pivotв своем запросе, затем переключился на это и посмотрел на план выполнения для одновременного запуска обоих. Этот подход стоил 8%, а метод Pivot - 92%!
mafue
2
@CharlesBretana, ты молодец! Ты спас мою душу! ) Это лучшее решение. Благодарность!
Chaki_Black 03
3
Мне очень нравится это решение, также оно гарантирует, что столбцы содержат правильные данные, а не сводные данные, спасибо!
Tenerezza
2
Это отлично работает! Но как предотвратить -Warning: Null value is eliminated by an aggregate or other SET operation
Джон Миллс
26
WITH pivot_data AS
(
SELECT customerid, -- Grouping Column
dbcolumnname, -- Spreading Column
data -- Aggregate Column
FROM pivot2 
)
SELECT customerid, [firstname], [middlename], [lastname]
FROM pivot_data
PIVOT (max(data) FOR dbcolumnname IN ([firstname],[middlename],[lastname])) AS p;
Вишванатх Далви
источник
3
Это должен быть принятый ответ, поскольку он показывает правильное использование команды TSQL Pivot.
Ubercoder 02
1
Стоит отметить, что в этом запросе pivot2 - это имя таблицы, в которой находятся исходные данные. Кроме того, использование CTE здесь излишне - SELECTоператор под CTE мог просто указать имя исходной таблицы.
STLDev
@STLDev На самом деле STLDev работает не так. Нам не известны все столбцы в таблице pivot2. На самом деле могут быть другие столбцы, которые OP не указал в таблице. Поэтому, если вы не ограничиваете столбцы - используя CTE или запрос производной таблицы, - тогда ВСЕ столбцы в таблице используются в группировке. Другими словами, PIVOT возвращает что-то, но не то, что мы ожидаем. Эта концепция подробно рассматривается на сертификационном экзамене 70-761.
Зорколот 01
2
Стоит отметить, что PIVOT автоматически группирует столбцы, которые не используются в самом PIVOT. Итак, в этом примере [data] и [dbcolumnname] находятся в PIVOT, поэтому все будет сгруппировано по [CustomerId]
Sal
10

Хорошо, извините за плохой вопрос. gbn направил меня на верный путь. Это то, что я искал в ответе.

SELECT [FirstName], [MiddleName], [LastName], [Date] 
FROM #temp 
PIVOT
(   MIN([Data]) 
    FOR [DBColumnName] IN ([FirstName], [MiddleName], [LastName], [Date]) 
)AS p

Затем мне пришлось использовать оператор while и построить приведенный выше оператор как varchar и использовать dynmaic sql.

Используя что-то вроде этого

SET @fullsql = @fullsql + 'SELECT ' + REPLACE(REPLACE(@fulltext,'(',''),')','')
SET @fullsql = @fullsql + 'FROM #temp '
SET @fullsql = @fullsql + 'PIVOT'
SET @fullsql = @fullsql + '('
SET @fullsql = @fullsql + ' MIN([Data])'
SET @fullsql = @fullsql + ' FOR [DBColumnName] IN '+@fulltext
SET @fullsql = @fullsql + ')'
SET @fullsql = @fullsql + 'AS p'

EXEC (@fullsql)

Наличие a для создания @fulltext с помощью цикла while и выбора отдельных имен столбцов из таблицы. Спасибо за ответы.

ctrlShiftBryan
источник
9
SELECT
main.CustomerID,
f.Data AS FirstName,
m.Data AS MiddleName,
l.Data AS LastName,
d.Data AS Date
FROM table main
INNER JOIN table f on f.CustomerID = main.CustomerID
INNER JOIN table m on m.CustomerID = main.CustomerID
INNER JOIN table l on l.CustomerID = main.CustomerID
INNER JOIN table d on d.CustomerID = main.CustomerID
WHERE f.DBColumnName = 'FirstName' 
AND m.DBColumnName = 'MiddleName' 
AND l.DBColumnName = 'LastName' 
AND d.DBColumnName = 'Date' 

Изменить: я написал это без редактора и не запускал SQL. Надеюсь, вы поняли идею.

Шахкалпеш
источник
8

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

sql параметризованный запрос cte

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

Белявский
источник
1

Попробуй это:

SELECT CUSTOMER_ID, MAX(FIRSTNAME) AS FIRSTNAME, MAX(LASTNAME) AS LASTNAME ...

FROM
(

SELECT CUSTOMER_ID, 
       CASE WHEN DBCOLUMNNAME='FirstName' then DATA ELSE NULL END AS FIRSTNAME,
       CASE WHEN DBCOLUMNNAME='LastName' then DATA ELSE NULL END AS LASTNAME,
        ... and so on ...
GROUP BY CUSTOMER_ID

) TEMP

GROUP BY CUSTOMER_ID
пользователь3538033
источник
1

Это должно работать:

select * from (select [CustomerID]  ,[Demographic] ,[Data]
from [dbo].[pivot]
) as Ter

pivot (max(Data) for  Demographic in (FirstName, MiddleName, LastName, [Date]))as bro
Рэнди Боама
источник
1

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

--суммировать значения в таблицу tmp

declare @STR varchar(1000)
SELECT  @STr =  COALESCE(@STr +', ', '') 
+ QUOTENAME(DateRange) 
from (select distinct DateRange, ID from ##pivot)d order by ID

--- увидеть созданные поля

print @STr

exec('  .... pivot code ...
pivot (avg(SalesAmt) for DateRange IN (' + @Str +')) AS P
order by Decile')
user7237698
источник