我希望能够编写一个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个参数),这有点麻烦。
提前致谢!
答案 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