Сводная таблица с пользовательскими значениями

#sql-server #sql-server-2012

#sql-сервер #sql-server-2012

Вопрос:

у меня здесь есть таблица, указывающая на СКРИПКУ

  ---- ------ ------ ----- ----- 
| id | year | sell | buy | own |
 ---- ------ ------ ----- ----- 
| 1  | 2016 | 9    | 2   | 10  |
| 1  | 2017 | 9    |     | 10  |
| 1  | 2018 |      | 2   | 10  |
| 2  | 2016 | 7    | 2   | 11  |
| 2  | 2017 | 2    |     |     |
| 2  | 2018 |      |     | 18  |
 ---- ------ ------ ----- ----- 
  
 create table test(id varchar(20), year varchar(20), 
sell varchar(20), buy varchar(20), 
own varchar(20));

insert into test values('1', '2016','9','2','10' )
insert into test values('1', '2017','9',NULL,'10' )
insert into test values('1', '2018',NULL,'2','10' )
insert into test values('2', '2016','7','2','11' )
insert into test values('2', '2017','2',NULL,'17' )
insert into test values('2', '2018','5','2','18' )
  

Я пытаюсь PIVOT , но вместо того, чтобы агрегировать значения, я хотел сохранить некоторые буквы, если они не равны нулю (S-Sell, B-Buy, O-Own). Если есть значения для всех столбцов за определенный год, то мне нужен S_B_O этот год. Если есть значения только для продажи и покупки, то S_B и т.д., Поэтому ожидаемый результат

  ---- ------- ------ ------ 
| ID | 2016  | 2017 | 2018 |
 ---- ------- ------ ------ 
| 1  | S_B_O | S_O  | B_O  |
 ---- ------- ------ ------ 
| 2  | S_B_O | S    | O    |
 ---- ------- ------ ------ 
  

Самое близкое, что у меня есть, — это использование условного объединения ( MAX и concat ) вместо PIVOT , но это также дает null, если таковой имеется NULL . Пожалуйста, предложите решение.

 select ID, 
MAX(CASE WHEN Year = '2016' AND sell is not null THEN 'S_' END   
CASE WHEN Year = '2016' AND buy is not null THEN 'B_' END  
CASE WHEN Year = '2016' AND own is not null THEN 'O' END)
AS [2016],
MAX(CASE WHEN Year = '2017' AND sell is not null THEN 'S_' END   
CASE WHEN Year = '2017' AND buy is not null THEN 'B_' END  
CASE WHEN Year = '2017' AND own is not null THEN 'O' END)
AS [2017]
/* ......for all year */
from test
group by id 
  

СКРИПКА ЗДЕСЬ

Комментарии:

1. {Some String} NULL = NULL Используйте CONCAT , который неявно изменяет NULL значения на '' .

2. Конечно, @Larnu, я попробую это. Есть ли лучший способ сделать это? потому что иногда я не могу предсказать количество лет. Я должен жестко кодировать ее для каждого года

3. Вам придется использовать динамический свод. Это совсем другой вопрос, и в Stack Overflow есть буквально 100 примеров.

4. Но как я могу использовать эти буквы (S_B_O) в динамическом своде? Не могли бы вы перенаправить меня на какую-нибудь справочную страницу? @Larnu

5. Google — Site:stackoverflow.com Динамический сводный SQL-сервер ; буквально тысячи результатов.

Ответ №1:

Вы можете использовать функцию CONCAT, которая будет автоматически обрабатывать нули.

 select ID, 
CONCAT(MAX(CASE WHEN Year = '2016' AND sell is not null THEN 'S_' END) , 
MAX(CASE WHEN Year = '2016' AND buy is not null THEN 'B_' END) ,
MAX(CASE WHEN Year = '2016' AND buy is not null THEN 'O' END))
AS [2016],
CONCAT(MAX(CASE WHEN Year = '2017' AND sell is not null THEN 'S_' END) , 
MAX(CASE WHEN Year = '2017' AND buy is not null THEN 'B_' END) ,
MAX(CASE WHEN Year = '2017' AND buy is not null THEN 'O' END))
AS [2017]
from test
group by id
  
 
 ---- ------- ------ 
| ID | 2016  | 2017 |
 ---- ------- ------ 
|  1 | S_B_O | S_   |
|  2 | S_B_O | S_   |
 ---- ------- ------ 

  

ОБНОВИТЕ динамический запрос. Как сказал @Larnu, вы должны были задать это как отдельный вопрос. Вы не должны изменять требование.

 DECLARE @lst_Years NVARCHAR(MAX) , @query NVARCHAR(MAX)
SET @lst_Years = STUFF((SELECT distinct ','   QUOTENAME([Year]) 
            FROM test 
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

SET @query = 'SELECT * FROM
(
select ID, [Year],
CONCAT(MAX(CASE WHEN  sell is not null THEN ''S_'' END) , 
MAX(CASE WHEN  buy is not null THEN ''B_'' END) ,
MAX(CASE WHEN buy is not null THEN ''O'' END))
AS [Value]
from test
group by id, [year]) as t
pivot
(
max(value) FOR YEAR IN ('   @lst_Years   ')
) as pvt'

EXEC(@query)
  
 
 ---- ------- ------ ------- 
| ID | 2016  | 2017 | 2018  |
 ---- ------- ------ ------- 
|  1 | S_B_O | S_   | B_O   |
|  2 | S_B_O | S_   | S_B_O |
 ---- ------- ------ ------- 

  

Комментарии:

1. Спасибо. Кажется, все в порядке. Могу ли я как-то сделать ее динамической? так что я не хочу жестко кодировать каждый год.

2. Это должен быть отдельный вопрос, @Avinash , однако быстрый поиск даст вам 100 примеров.

3. @Avinash, КАК упоминал Ларну, это должен быть отдельный вопрос. в любом случае, я добавил динамический запрос также к ответу.

4. @VenkataramanR. Не могли бы вы, пожалуйста, помочь мне удалить лишние _ в конце, S_ S если это возможно? Я перепробовал так много способов, и ничего не работает. Кажется, это легко в последней версии, но ничего не найти в 2012 версии

5. @Avinash, пожалуйста, задайте отдельный вопрос. ее изменение приведет к путанице для будущих читателей этого вопроса.