2011-10-19 40 views
9

我正在使用pvots與SQL Server(T-sql)中的動態列進行SQL查詢。 而不是提交我冗長的查詢,我用簡化的模型來說明我的問題。在SQL Server中使用動態列的樞軸

我創建2個表:表1和表2,並用幾個條目填充這些如下:

表1:


Col_ID1 ............ ... COL_NAME

1 .........................月11

2 ........ ................. Feb-11

3 ......................... 3月-11

表2:


Col_ID2 ......賬戶.....賬戶名......金額

1 ............... 121 ......... ..Electricity ............ 10000

2 ............... 121 ...........電。 ........... 20000

3 ............... 121 ...........電力............ 30000

1 ............... 122 ...........電話.............. 100

2。 .............. 122 ...........電話.............. 200

3 ... ............ 122 ...........電話.............. 300

我正在創建一個樞軸,但我想要參數化地生成列名(基於從輸入s鍵入的日期creen),而不是硬編碼。

下面的查詢效果很好,但僅提供了一些列的FOLL:

月11 ...........月11 ........ ...... 3月-11

10,000.00 20,000.00 ...... 30,000.00 ......

100.00 200.00 ............... .... ....... 300.00

我希望查詢也返回描述性列,如下所示:

帳戶...........帳戶名........... 1月11日............ 2月11日.. ............ Mar-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 

------------------------ --subquery -----

(SELECT
t1.Col_Name,
t2.Account,
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 '

---------------------- EXEC &下降----------

EXECUTE(@query) 
drop table table1 
drop table table2 

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

菲利普嗨,

非常感謝您的回覆。

您提出的查詢工作順利,並生成期望的屏幕,但它不完全是我想要的。

首先,感謝的代碼: SELECT @cols = ISNULL(@cols + '', '')+ '[' + COL_NAME + ']'

這是簡單和不替換我線涉及到東西和XML路徑,顯然具有相同的效果。

讓我解釋我想要做什麼。

我想在SAP業務1(會計軟件包 - 或稱之爲ERP)中開發查詢。 Sap在Microsoft Server 2008中使用T-sql,並擁有自己的查詢生成器。 除了極少數例外,Sap sql與T-sql類似。

我希望我的查詢能夠在12個月內逐月列出所有收入和支出。

不過,我不希望被硬編碼我的列標題,(因爲這需要我不時修改我的查詢)如下:

揚11月11,Mar- 11,Apr-11,... Dec-11

相反,我希望列標題是從用戶在輸入屏幕中輸入的日期動態生成的。

正如我所提到的,我在論壇上發佈的查詢是我的真實查詢的過度簡化版本,僅用於說明。真正的查詢包含多個變量和一個輸入屏幕(在Sap b1中稱爲查詢 - 選擇標準框)允許用戶輸入日期。這是將用於動態確定列名稱的日期。

這就是爲什麼我需要這樣複雜的工具@cols,@query,旋轉等

如果我輸入,說'01 .06.11' (2011年6月1日)在輸入畫面中,該日期將被傳遞給sql,它將確定列標題的名稱爲foll:

Jun-11,Jul-11,Aug-11 ..... May-12。

如果我輸入另一個日期,說'01 .09.10' (2010年9月1日),列標題將變爲:

九月10,2010年10月,... 8 - 11

看來你已經硬編碼了我的列標題。

您能否再次查看我的查詢,並提出一些允許以參數方式生成列名而不是硬編碼的內容?

感謝

黎明

回答

11

添加這些列是非常簡單的。最終的查詢是

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添加到子查詢,以及帳戶和帳戶名添加到初始選擇。他們折騰到生成的語句,就大功告成了:

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 
+0

嗨菲利普,我發佈了一個問題到您的回覆謝謝萊昂 –

+0

這是一個可笑的很好的答案! – jTC

+0

使用'quotename(Col_Name)'而不是''['+ Col_Name +']''。 – jnm2

1

添加另一個答案,因爲這編輯了幾乎第二個問題。 (沒有細節和細節,我只能提供一般概要和僞代碼 - 我不知道SAP。)

讓我們從數據透視開始。它需要生成標記爲(可能是月份)的列,在您的示例中,該列爲Table1.Col_Name,varchar(10);這些值將被提取並動態添加到數據透視表中作爲列名稱。如果數據庫中沒有這樣的列,那麼您必須根據用戶輸入的數據構建查詢。我將使用以下假設: - 數據具有dateime列,其中可以找到任何值(年至毫秒) - 用戶指定「開始日期」(是否始終是一個月的第一個月?)並且必須爲此和隨後的11個月生成列,彙總落在每個目標月份內的數據。

第1步,我會設置和填充含12目標列一個臨時表:當你希望它出現

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

標籤被格式化,MonthStart將是本月的絕對開始(比如2011年10月1日00:00:00.000),MonthEnd將是下個月的絕對開始時間(2011年11月1日00:00:00:00) - 這允許您使用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查詢(我不知道還有什麼要調用它),動態構建和執行,你應該很好。

+0

感謝Philip Leon Lai –