从查询构建的EXEC动态SQL

时间:2016-03-01 17:23:16

标签: tsql sql-server-2012

我已经做了一些搜索,但还是没有达到我的需要。

我受限于阻止我使用NVARCHAR(MAX)或VARCHAR(MAX)的编码标准,这是我的痛点。

我需要创建一个最有可能超过8000个字符的动态SQL语句。发生这种情况的原因是因为要求使用大量的CASE WHEN语句。我的方法是将每个CASE WHEN作为记录插入表中。然后我在动态SQL语句中使用XML PATH('')来创建另一个动态SQL语句。

以下是我正在使用的代码示例:

CREATE TABLE #TempTest (
ID INT
,Data VARCHAR(50)
,Data2 VARCHAR(50)
,Flag INT
)

CREATE TABLE #TempCase (
ID INT
,CaseS VARCHAR(50)
)

INSERT INTO #TempCase (
ID
,CaseS
)
VALUES
(1,'CASE WHEN Flag = 1 THEN Data ELSE Data2 END')
,(2,'CASE WHEN Flag = 2 THEN ID ELSE 0 END')

INSERT INTO #TempTest (
ID
,Data
,Data2
,Flag
)
VALUES
(1,'Hobo','Jim',1)
,(2,'Hobo Again','Jane',2)

EXEC('SELECT TOP 1 ''SELECT '' + STUFF((SELECT N'', '' + CaseS
    FROM #TempCase TC
    ORDER BY TC.ID
    FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''nvarchar(max)''), 1, 2, N'''') + '' FROM #TempCase''
FROM #TempCase TC
GROUP BY TC.ID') 

此EXEC声明的结果是 -

SELECT CASE WHEN Flag = 1 THEN Data ELSE Data2 END, CASE WHEN Flag = 2 THEN ID ELSE 0 END FROM #TempCase

查询的结果是我必须运行的动态SQL。我试图将当前的EXEC语句嵌套到另一个EXEC语句中,但这只创建了两个具有相同结果的数据集。按照大多数人处理这种方式的方式进行搜索是通过将初始EXEC结果设置为VARCHAR(MAX)并将其分配给第二个EXEC语句,由于我公司的编码标准,我无法做到这一点。

任何建议都将不胜感激。

2 个答案:

答案 0 :(得分:0)

假设您确实有权创建临时表,并且您已经具有将各种CASE语句放在表中的逻辑(以及一些其他假设,这些假设可能有效也可能无效,但仍暗示但在此处缺少明确的描述) ,下面的方法怎么样:

CREATE TABLE #TempTest
  (
       ID    INT,
       Data  VARCHAR(50),
       Data2 VARCHAR(50),
       Flag  INT
  )

CREATE TABLE #TempCase
  (
       ID    INT,
       CaseS VARCHAR(50),
       CaseSDataType VARCHAR(20),
       HasProcessed BIT DEFAULT(0)
  )

INSERT INTO #TempCase
            (ID,
             CaseS,
             CaseSDataType)
     VALUES (1,
             'CASE WHEN Flag = 1 THEN Data ELSE Data2 END',
             'VARCHAR(50)'),
            (2,
             'CASE WHEN Flag = 2 THEN ID ELSE 0 END',
             'VARCHAR(50)')

INSERT INTO #TempTest
            (ID,
             Data,
             Data2,
             Flag)
     VALUES (1,
             'Hobo',
             'Jim',
             1),
            (2,
             'Hobo Again',
             'Jane',
             2) 

CREATE TABLE #Computed ( ID INT )
INSERT INTO #Computed ( ID ) SELECT ID FROM #TempTest

DECLARE @caseID INT
DECLARE @caseSQL VARCHAR(100)
DECLARE @colSQL VARCHAR(100)
DECLARE @updateSQL VARCHAR(255)
WHILE EXISTS( SELECT * FROM #TempCase WHERE HasProcessed = 0 )
BEGIN
    SELECT TOP 1 
        @caseID = ID, 
        @caseSQL = REPLACE(REPLACE('ALTER TABLE #TempTest ADD CaseStmt{id} AS ({stmt})', '{id}', CAST(ID AS VARCHAR)), '{stmt}', CaseS),
        @colSQL = REPLACE(REPLACE('ALTER TABLE #Computed ADD CaseStmt{id} {datatype}', '{id}', CAST(ID AS VARCHAR)), '{datatype}', CaseSDataType),
        @updateSQL = REPLACE('UPDATE t2 SET t2.CaseStmt{id} = t1.CaseStmt{id} FROM #TempTest t1 INNER JOIN #Computed t2 ON t1.ID = t2.ID', '{id}', ID)
    FROM #TempCase 
    WHERE HasProcessed = 0

    -- Add the columns
    EXEC(@caseSQL)
    EXEC(@colSQL)

    -- Update the column
    EXEC(@updateSQL)

    -- Next
    UPDATE #TempCase
       SET HasProcessed = 1
    WHERE ID = @caseID
END

-- Return your results (all of #Computed)
SELECT * FROM #Computed

实际上,#Computed的结构就像各种CASE语句在一个SELECT语句中被链接在一起一样。

我们可以通过简单地将CASE语句直接戳到UPDATE sql来快捷创建计算列,但这可能需要对CASE语句进行一些操作以确保表前缀正确。

答案 1 :(得分:0)

以您向SQL Injection公开的方式构建动态SQL!

无论如何,您可以将第一个EXEC的结果插入临时表并执行它:

CREATE TABLE #final(result VARCHAR(8000));

INSERT INTO #final(result)
EXEC(
'SELECT TOP 1 ''SELECT '' + STUFF((SELECT N'', '' + CaseS
    FROM #TempCase TC
    ORDER BY TC.ID
    FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''nvarchar(max)''), 1, 2, N'''') 
        + '' FROM #TempTest''   
FROM #TempCase TC
GROUP BY TC.ID');

DECLARE @sql VARCHAR(8000);

SELECT @sql = result
FROM #final;

EXEC(@sql);

LiveDemo

输出:

╔══════╦══════╗
║ col1 ║ col2 ║
╠══════╬══════╣
║ Hobo ║    0 ║
║ Jane ║    2 ║
╚══════╩══════╝