2011-10-19 23 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 –