Сводки с динамическими столбцами в SQL Server - PullRequest
9 голосов
/ 19 октября 2011

Я работаю над запросом SQL, используя pvots с динамическими столбцами в SQL Server (T-sql).Вместо того, чтобы отправлять мой длинный запрос, я иллюстрирую свою проблему с помощью упрощенной модели.

Я создаю 2 таблицы: Table1 и Table2 и заполняю их несколькими записями следующим образом:

Table1:


Col_ID1 ............... Col_Name

1 ......................... янв-11

2 ......................... февраль-11

3 ......................... Mar-11

Table2:


Col_ID2 ...... Account .....AccountName ...... Сумма

1 ............... 121 ........... Электричество ............ 10000

2 ............... 121 ........... Электричество ............ 20000

3 ............... 121 ........... Электричество ............ 30000

1 ............... 122 ........... Телефон .............. 100

2 ............... 122 ........... Телефон .............. 200

3 ............... 122 ........... Телефон .............. 300

Я создаю сводную таблицу, но хочу, чтобы имена столбцов генерировались параметрически (на основе дат, введенных на экране ввода), и nне жестко закодировано.

Приведенный ниже запрос работает хорошо, но дает только несколько столбцов:

Янв-11 ........... Фев-11 ........... март-11

10 000,00 ...... 20 000,00 ...... 30 000,00

100,00 ............... 200,00 ........... 300,00

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

Account........... AccountName ........... Jan-11 ............ февраль-11 .............. март-11

121 ................. Электричество .................10 000,00 ...... 20 000,00 .......... 30 000,00

122 ................. Телефон ..................... 100,00 ........... 200,00 ............. 300,00

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

Этот запрос является адаптацией следующей статьи, написанной доктором Андрасом в сентябре 2007 года. http://www.simple -talk.com / community / blogs / andras / archive / 2007/09/14/37265.aspx

Кто-то заметил, что код может подвергаться атакам с помощью инъекций, и предложил использовать функцию Quotename вместо объединения квадратных скобок.

Не могли бы вы объяснить, как использовать Quotename в моем запросе.

Большое спасибо,

Леон Лай.
.
.

Вот мой запрос:

------------------------ создать и заполнить таблицу1 --------------------------------

CREATE TABLE Table1
(Col_ID1 INT, 
Col_Name varchar(10))  

INSERT INTO Table1 VALUES (1, 'Jan-11')  
INSERT INTO Table1 VALUES (2, 'Feb-11')  
INSERT INTO Table1 VALUES (3, 'Mar-11') 

------------------------- создать и заполнить таблицу2 ----------------------------------

CREATE TABLE Table2  
(Col_ID2 INT,  
Account varchar(10),  
AccountName varchar(20),  
Amount numeric(18,6))  

INSERT INTO Table2 VALUES (1, 121, 'Electricity', 10000)  
INSERT INTO Table2 VALUES (2, 121, 'Electricity', 20000)  
INSERT INTO Table2 VALUES (3, 121, 'Electricity', 30000)  
INSERT INTO Table2 VALUES (1, 122, 'Telephone', 100)        
INSERT INTO Table2 VALUES (2, 122, 'Telephone', 200)   
INSERT INTO Table2 VALUES (3, 122, 'Telephone', 300)   

---------------------------------- создать заголовки столбцов -------------------

DECLARE @cols NVARCHAR(2000)   
SELECT @cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT   
'],[' + t2.Col_Name   
FROM Table1 AS t2   
ORDER BY '],[' + t2.Col_Name 
FOR XML PATH('')   
), 1, 2, '') + ']'   

------------------------------------- create @query ----------------------

DECLARE @query NVARCHAR(4000)   

SET @query = N'SELECT '+   
@cols +' 

FROM   

-------------------------- подзапрос -----

(SELECT<br> t1.Col_Name,<br> t2.Account,<br> t2.Amount<br> FROM Table1 AS t1<br> JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2<br> ) p

-------------------- pivot -------------------------

PIVOT<br> (<br> Sum ([Amount] )<br> FOR Col_Name IN<br> ( '+<br> @cols +' )<br> ) AS pvt '

---------------------- exec & drop ----------

EXECUTE(@query)
drop table table1
drop table table2

=======================================================

Привет, Филипп,

Большое спасибо за ваш ответ.

Ваш предложенный запрос работает без сбоев и создает ожидаемый экран, но это не совсем то, что я хотел.

Во-первых, спасибо за код: SELECT @cols = isnull (@cols + ',', '') + '[' + Col_Name + ']'

Это проще и заменяет мою строку, содержащую материал и путь XML, очевидно, с тем же эффектом.

Позвольте мне объяснить, что я хочу сделать.

Я хочу разработать запрос в Sap Business 1 (Пакет бухгалтерского учета - или назвать его ERP).Sap использует T-sql в Microsoft Server 2008 и имеет собственный генератор запросов.Сочень немногие исключения, Sap sql похож на T-sql.

Я хочу, чтобы мой запрос содержал список всех доходов и расходов ежемесячно за 12-месячный период.

Однако я не хочу, чтобы заголовки столбцов были жестко закодированы (поскольку это потребовало бы от меня время от времени вносить изменения в свой запрос) следующим образом:

11 января, 11 февраля, 11 марта, 11 апреля, ..... 11 декабря

Скорее, я хочу, чтобы заголовки столбцов создавались динамически из дат, которые пользователь вводит на экране ввода.

Как я уже упоминал, запрос, который я разместил на форуме, является упрощенной версией моего реального запроса и используется только для иллюстрации. Реальный запрос содержит несколько переменных, и экран ввода (называемый «Запрос - поле Критерии выбора» в Sap b1) позволяет пользователю ввести дату. Именно эта дата будет использоваться для динамического определения имен столбцов.

Именно поэтому мне понадобились такие сложные инструменты, как @cols, @query, pivot и т. Д.

Если я введу, скажем, «01 .06.11» (01 июня 2011 г.) на экране ввода, эта дата будет передана в sql, который определит названия заголовков столбцов следующим образом:

11 июня, 11 июля, 11 августа ... 12 мая.

Если я введу другую дату, скажем, «01 .09.10» (01 сентября 2010 г.), заголовки столбцов изменятся на:

сентябрь-10, октябрь-10, .... авг-11

Похоже, вы жестко закодировали заголовки моих столбцов.

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

Спасибо

Леон Лай

Ответы [ 2 ]

11 голосов
/ 19 октября 2011

Добавить эти столбцы очень просто.Окончательным запросом будет

SELECT Account, AccountName, [Feb-11],[Jan-11],[Mar-11]   FROM   
(SELECT
t1.Col_Name,
t2.Account,
t2.AccountName,
t2.Amount
FROM Table1 AS t1
JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2
) p
PIVOT
(
Sum ([Amount] )
FOR Col_Name IN
( [Feb-11],[Jan-11],[Mar-11] )
) AS pvt 

, в котором t2.AccountName добавлено в подзапрос, а Account и AccountName добавлены в начальный SELECT.Бросьте их в оператор сборки, и все готово:

DECLARE @query NVARCHAR(4000)
SET @query = N'SELECT Account, AccountName, ' +    @cols +'   FROM   

(SELECT
t1.Col_Name,
t2.Account,
t2.AccountName,
t2.Amount
FROM Table1 AS t1
JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2
) p

PIVOT
(
Sum ([Amount] )
FOR Col_Name IN
( '+
@cols +' )
) AS pvt ' 

Что касается внедрения SQL-кода, я могу видеть, что это происходит только в том случае, если кто-то каким-то образом внедряет вредоносный код в Table1.Col_Name и если у вас естьчтобы беспокоиться об этом, у вас есть большие проблемы, чем «блокировка» этого динамического запроса.

Также стоит упомянуть, я бы использовал следующее для построения списка столбцов (@Cols), потому что он короче и проще длячитать, но в основном потому, что я не люблю XML.

DECLARE @cols NVARCHAR(2000)    
SELECT @cols = isnull(@cols + ',', '') + '[' + Col_Name + ']'
 FROM Table1
 ORDER BY Col_Name
1 голос
/ 20 октября 2011

Добавление еще одного ответа, так как это редактирование почти второй вопрос.(Без деталей и конкретики, я могу предложить лишь общие контуры и псевдо-код, я не знаю SAP.)

1002 * Давайте начнем с шарниром.Он должен генерировать столбцы, помеченные, предположительно, месяцем, который вы имели в своем примере как Table1.Col_Name, varchar (10);эти значения извлекаются и динамически добавляются в сводный запрос в виде имен столбцов.Если в базе данных такого столбца нет, его необходимо построить для запроса на основе данных, введенных пользователем.Я буду работать со следующими предположениями: - У данных есть столбец даты и времени, где можно найти любое значение (от года до миллисекунды) - Пользователь указывает «дату начала» (всегда ли это первое число месяца?), А вынеобходимо сгенерировать столбцы для этого и последующих 11 месяцев, объединяя данные, которые попадают в каждый целевой месяц.

Шаг 1, я бы настроил и заполнил временную таблицу, содержащую 12 целевых столбцов:

CREATE TABLE #Months
 (
   Col_Name    varchar(10)
  ,MonthStart  datetime
  ,MonthEnd    datetime
)

Метка отформатирована так, как вы хотите, чтобы она отображалась, MonthStart будет абсолютным началом месяца (скажем, 1 октября 2011 г. 00: 00: 00.000), а MonthEnd будет абсолютным началом следующего месяца.(1 ноября 2011 г. 00: 00: 00.000) - это позволяет использовать SELECT … from <table> where DataDate >= MontStart and DataDate < MonthEnd для получения всех данных в течение этого месяца.

Далее, присоедините эту таблицу к вашей таблице данных и агрегируйте, что-то вроде:

SELECT
   mt.Col_Name
  ,sum(dt.RawData)  Amount
 from #Months mt
  inner join MyData dt
   on dt.DataDate >= mt.MonthStart
    and dt.DataDate < mt.MonthEnd  --  Yes, ON clauses don't have to be simple equivalencies!
  inner join <other tables as necessary for Account, AccountName, etc.>

Подключите это как самый внутренний запрос оператора сводки, извлеките / соберите список Col_Names извременную таблицу, использующую не XML-запрос (я не знаю, как еще это назвать), создайте и выполните динамически, и вы должны быть хорошими.

...