SQL Server存储过程以仅按特定顺序选择活动列

时间:2016-11-06 22:04:52

标签: sql sql-server xml stored-procedures

我希望能够编写一个SQL Server存储过程,该过程将根据参数选择列,选择顺序应该基于另一个参数。

例如,我有一个表tblStaging,它有几列(Name,date,Addr1,Addr2,Addr3)。我想使用此表来使用存储过程创建XML。存储过程只需从该表中选择列并以XML语法显示它们,如:

SELECT
    Name, date, Addr1, Addr2, Addr3 
FROM  
    tblStaging 
FOR XML RAW ('TRAN'), ROOT ('SEPA'), ELEMENTS;

现在,我想通过让用户传递2个参数,即“Active”标志和“Order”来使这个存储过程可配置。活动:1或0,将决定是否在XML架构中选择一个字段。订单:1,2,3,4 ..将确定选择字段的顺序。例如。如果'Name'设置为Active = 1且Order = 2,它将被选中但不是第一个元素,而是XML中的第二个元素。

实现这一目标的最佳方法是什么?不知道我是否应该每列有2个参数并要求用户为每个参数传递一个值,如果表有10列(= 20个参数),这有点麻烦。

提前致谢!

1 个答案:

答案 0 :(得分:1)

我已经为你编写了一份工作代码。您可能希望在生产系统中使用它之前进一步开发它,因为它未经过测试并受SQL注入。

@OrderBy:您也可以使用SQL允许的序数(order by 1 ASC将起作用)

@Columns:您可以使用序号或列名

进一步开发可以为表名添加另一个变量,例如。

CREATE PROCEDURE dbo.Sample_Procedure 
    @Orderby nvarchar(100) = 'Name Asc',
    @Columns nvarchar(100) = '1,2,3,Name' 
AS
DECLARE @SQLString nvarchar(500)
DECLARE @UsedColumns nvarchar(500)
DECLARE @X xml

/*
   Using string manipulation and covert to transform  @Columns to xml, 
   so 1,2,3 becomes: <root><s>1</s><s>2</s><s>2</s></root> and then
   converted to XML so we can select from it as if it was a table. 
   if you have SQL 2016 it's possible to replace it with STRING_SPLIT 
*/
SELECT  @X = CONVERT(xml,' <root> <s>' + REPLACE(@Columns,',','</s> <s>') + '</s>   </root> ')


DECLARE @ColsTab as TABLE (Col nvarchar(100))
/*This part "shreds" the xml above into the variable table @ColsTab. 
  we need it for the `IN' operator later.
*/
INSERT  into @ColsTab (col)
SELECT  T.c.value('.','varchar(20)') FROM @X.nodes('/root/s') T(c)


SET @UsedColumns = STUFF(   (SELECT ',' + COLUMN_NAME 
    FROM    INFORMATION_SCHEMA.COLUMNS 
    WHERE   TABLE_NAME='tblStaging'
        and (COLUMN_NAME in  (Select Col from @ColsTab)
            or ORDINAL_POSITION in (Select iif(IsNumeric(Col)=1,Col,Null) from @ColsTab)
            )
                FOR XML PATH('')), 
            1, 1, '')



SET @SQLString = 'Select '+ @UsedColumns +'
from tblStaging 
order by '+@Orderby+'
FOR XML RAW (''TRAN''), ROOT (''SEPA''), ELEMENTS';

EXECUTE sp_executesql   @SQLString

RETURN 

相同的查询,这次动态查询还设置了输出列的序号:

CREATE PROCEDURE dbo.Sample_Procedure2 
    @Orderby nvarchar(100) = 'Name Asc',
    @Columns nvarchar(100) = '2,3,Name' 
AS
DECLARE @SQLString nvarchar(500)
DECLARE @UsedColumns nvarchar(500)
DECLARE @X xml

/*
   Using string manipulation and covert to transform  @Columns to xml, 
   so 1,2,3 becomes: <root><s>1</s><s>2</s><s>2</s></root> and then
   converted to XML so we can select from it as if it was a table. 
   if you have SQL 2016 it's possible to replace it with STRING_SPLIT 
*/
SELECT  @X = CONVERT(xml,' <root> <s>' + REPLACE(@Columns,',','</s> <s>') + '</s>   </root> ')


DECLARE @ColsTab as TABLE (Col nvarchar(100))
/*This part "shreds" the xml above into the variable table @ColsTab. 
  we need it for the `IN' operator later.
*/
INSERT  into @ColsTab (col)
SELECT  T.c.value('.','varchar(20)') FROM @X.nodes('/root/s') T(c)


/*  This version usues left join to assume the same order of 
    columns as specified in the input field @Columns
    be mindful not to specify the same field twice
*/
SET @UsedColumns = STUFF(   (
    SELECT      ',' + i.COLUMN_NAME 
    FROM        @ColsTab as c
    LEFT JOIN   INFORMATION_SCHEMA.COLUMNS as i
        ON      i.COLUMN_NAME = Col 
            OR  ORDINAL_POSITION = iif(IsNumeric(Col)=1,Col,Null)
    WHERE       TABLE_NAME='tblStaging'
        AND     COLUMN_NAME is not null
                FOR XML PATH('')), 
                1, 1, '')



SET @SQLString = 'Select '+ @UsedColumns +'
from tblStaging 
order by '+@Orderby+'
FOR XML RAW (''TRAN''), ROOT (''SEPA''), ELEMENTS';

EXECUTE sp_executesql   @SQLString

RETURN