17

長い質問で申し訳ありませんが、これには、シナリオをテストするために使用したすべての SQL が含まれており、うまくいけば、私が何をしているのかが明確になります。

SQL Server 2005 で PIVOT テーブルを生成するために動的 SQL を構築しています。

以下は、これを行うコードです。生データを表示するさまざまな選択で、GROUP BY を使用した値と PIVOT の値を必要に応じて表示します。

BEGIN TRAN
--Create the table
CREATE TABLE #PivotTest
(
    ColumnA nvarchar(500),
    ColumnB nvarchar(500),
    ColumnC int
)

--Populate the data
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'X', 1)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'Y', 2)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'Z', 3)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'X', 4)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'Y', 5)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('B', 'Z', 6)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('B', 'X', 7)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('B', 'Y', 8)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('B', 'Z', 9)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('C', 'X', 10)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('C', 'Y', 11)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('C', 'Z', 12)

--The data
SELECT * FROM #PivotTest

--Group BY
SELECT
    ColumnA,
    ColumnB,
    SUM(ColumnC)
FROM
    #PivotTest
GROUP BY
    ColumnA,
    ColumnB

--Manual PIVOT
SELECT
    *
FROM
    (
        SELECT
            ColumnA,
            ColumnB,
            ColumnC
        FROM
            #PivotTest
    ) DATA
    PIVOT
    (
        SUM(DATA.ColumnC)
    FOR
        ColumnB
        IN
        (
            [X],[Y],[Z]
        )
    ) PVT

--Dynamic PIVOT
DECLARE @columns nvarchar(max)

SELECT
    @columns = 
    STUFF
    (
        (
            SELECT DISTINCT
                ', [' + ColumnB + ']'
            FROM
                #PivotTest
            FOR XML PATH('')
        ), 1, 1, ''
    )

EXEC
('
    SELECT
        *
    FROM
        (
            SELECT
                ColumnA,
                ColumnB,
                ColumnC
            FROM
                #PivotTest
        ) DATA
        PIVOT
        (
            SUM(DATA.ColumnC)
        FOR
            ColumnB
            IN
            (
                ' + @columns + '
            )
        ) PVT
')

--The data again
SELECT * FROM #PivotTest

ROLLBACK

動的 SQL を生成するときはいつでも、SQL インジェクション攻撃を常に認識しています。したがって、他の INSERT ステートメントに次の行を追加しました。

INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'FOO])) PVT; DROP TABLE #PivotTest;SELECT ((GETDATE()--', 1)

ここで SQL を実行すると、見よ、EXEC 部分が #PivotTest テーブルをドロップするため、最後の SELECT が失敗します。

私の質問は、SQL インジェクション攻撃の危険を冒さずに動的な PIVOT を実行する方法を知っている人はいますか?

4

3 に答える 3

16

私たちはあなたの例に似た多くの仕事をしました。ピボットされるデータを完全かつ完全に制御できるため、SQL インジェクションについては心配していません。悪意のあるコードが ETL を介してデータ ウェアハウスに侵入することはまったくありません。

いくつかの考えとアドバイス:

  • nvarcahr(500) 列でピボットする必要がありますか? 私たちのものは varchar(25) または数値であり、そこから有害なコードをこっそり持ち込むのはかなり難しいでしょう.
  • データチェックはどうですか?これらの文字列の 1 つに "]" 文字が含まれていた場合、それはハッキングの試みか、いずれにせよ爆発するデータのいずれかです。
  • セキュリティはどの程度堅牢ですか? マロリーが自分のハッキングを (直接またはアプリケーションを介して) データベースに忍び込ませないように、システムはロックされていますか?

はぁ。関数 QUOTENAME() を思い出すには、すべてを書く必要がありました。簡単なテストでは、次のようにコードに追加するとうまくいくことが示されているようです (削除された一時テーブルではなく、エラーが発生します)。

SELECT
        @columns = 
        STUFF
        (
                (
                        SELECT DISTINCT
                                ', [' + quotename(ColumnB, ']') + ']'
                        FROM
                                #PivotTest
                        FOR XML PATH('')
                ), 1, 1, ''
        )

ほとんどの場合、値を[ブラケット]する必要があるため、これはピボット(およびアンピボット)の状況で機能するはずです。

于 2009-09-17T15:49:09.833 に答える
0

少しリファクタリング...

CREATE PROCEDURE ExecutePivot (
    @TableName sysname,
    @GroupingColumnName sysname,
    @AggregateExpression VARCHAR(256),
    @SelectExpression VARCHAR(256),
    @TotalColumnName VARCHAR(256) = 'Total',
    @DefaultNullValue VARCHAR(256) = NULL,
    @IsExec BIT = 1)
AS
BEGIN
    DECLARE @DistinctGroupedColumnsQuery VARCHAR(MAX);
    SELECT @DistinctGroupedColumnsQuery = CONCAT('SELECT DISTINCT ',@GroupingColumnName,' FROM ',@TableName,';');
    DECLARE @DistinctGroupedColumnsResult TABLE ( [row] VARCHAR(MAX) );
    INSERT INTO @DistinctGroupedColumnsResult EXEC(@DistinctGroupedColumnsQuery);

    DECLARE @GroupedColumns VARCHAR(MAX);
    SELECT @GroupedColumns = STUFF ( ( SELECT DISTINCT CONCAT(', ',QUOTENAME([row])) FROM @DistinctGroupedColumnsResult FOR XML PATH('') ), 1, 1, '' );

    DECLARE @GroupedColumnsNullReplaced VARCHAR(MAX);
    IF(@DefaultNullValue IS NOT NULL)
        SELECT @GroupedColumnsNullReplaced = STUFF ( ( SELECT DISTINCT CONCAT(', ISNULL(',QUOTENAME([row]),',',@DefaultNullValue,') AS ',QUOTENAME([row])) FROM @DistinctGroupedColumnsResult FOR XML PATH('') ), 1, 1, '' );
    ELSE
        SELECT @GroupedColumnsNullReplaced=@GroupedColumns;

    DECLARE @ResultExpr VARCHAR(MAX) = CONCAT('
        ; WITH cte AS
        (
            SELECT ',@SelectExpression,', ',@GroupedColumns,'
            FROM ',@TableName,'
            PIVOT ( ',@AggregateExpression,' FOR ',@GroupingColumnName,' IN (',@GroupedColumns,') ) as p
        )
        , cte2 AS
        (
            SELECT ',@SelectExpression,', ',@GroupedColumnsNullReplaced,'
            FROM cte
        )
        SELECT ',@SelectExpression,', ',REPLACE(@GroupedColumns,',','+'),' AS ',@TotalColumnName,', ',@GroupedColumns,'
        FROM cte2;
        ');

    IF(@IsExec = 1) EXEC(@ResultExpr);
    ELSE SELECT @ResultExpr;
END;

使用例:

select schema_id, type_desc, 1 as Item 
    into PivotTest
from sys.objects;

EXEC ExecutePivot 'PivotTest','type_desc','SUM(Item)','schema_id','[Total Items]','0',1;
于 2015-11-02T11:23:12.880 に答える