1

をターゲットとするさまざまなT-SQL述語を含む SourceTableテーブル変数があります。@TQueriesSourceTable

期待される結果はSELECT、の述語で指定されたIDのリストを返すステートメントを動的に生成すること@TQueriesです。動的に生成された各SELECTステートメントも特定の順序で実行する必要があり、最終的な値のセットは一意であり、順序を保持する必要があります。

幸い、取得する必要のある値の数と、生成する必要のある動的クエリの数には制限があります。Idリストには最大で10個のIDを含める必要があり、7個を超えるクエリは想定されていません。

以下は、実際のデータ/データベースではなく、このセットアップのサンプルです。

-- Set up some test data, this is quick and dirty just to provide some data to test against
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SourceTable]') AND type in (N'U'))
BEGIN
    -- Create a numbers table, sorta
    SELECT TOP 20 
        IDENTITY(INT,1,1) AS Id,
        ABS(CHECKSUM(NewId())) % 100 AS [SomeValue]
    INTO [SourceTable]
    FROM sysobjects a
END


DECLARE @TQueries TABLE (
    [Ordinal] INT,
    [WherePredicate] NVARCHAR(MAX),
    [OrderByPredicate] NVARCHAR(MAX)
);

-- Simulate SELECTs with different order by that get different data due to varying WHERE clauses and ORDER conditions
INSERT INTO @TQueries VALUES ( 1, N'[Id] IN (6,11,13,7,10,3,15)',  '[SomeValue] ASC' ) -- Sort Asc
INSERT INTO @TQueries VALUES ( 2, N'[Id] IN (9,15,14,20,17)', '[SomeValue] DESC' ) -- Sort Desc
INSERT INTO @TQueries VALUES ( 3, N'[Id] IN (20,10,1,16,11,19,9,15,17,6,2,3,13)', 'NEWID()' ) -- Sort Random

私の主な問題は、CURSORの使用を回避すること、または行を1つずつ繰り返すことです。この基準を満たす集合演算に最も近いのは、テーブル変数を使用して各クエリまたは大規模なCTEの結果を格納することです。

提案やコメントは大歓迎です。

4

2 に答える 2

2

これは、すべてのクエリを実行し、結果を返すための単一のステートメントを作成するソリューションです。

@TQueriesこれは、テーブルを反復処理するときの回答と同様のアプローチを使用します。つまり{...}、列の値が移動する場所にトークンを使用@TQueryし、ネストされたREPLACE()呼び出しで値をそこに配置します。

それ以外はランキング機能に大きく依存していて、本当に悪用していないのかわかりません。これまでの方法よりも良いか悪いかを判断する前に、この方法をテストする必要があります。

DECLARE @QueryTemplate nvarchar(max), @FinalSQL nvarchar(max);

SET @QueryTemplate =
N'SELECT
  [Id],
  QueryRank = {Ordinal},
  RowRank = ROW_NUMBER() OVER (ORDER BY {OrderByPredicate})
FROM [dbo].[SourceTable]
WHERE {WherePredicate}
';

SET @FinalSQL =
N'WITH AllData AS (
' +
SUBSTRING(
  (
    SELECT
      'UNION ALL ' +
      REPLACE(REPLACE(REPLACE(@QueryTemplate,
        '{Ordinal}'         , [Ordinal]         ),
        '{OrderByPredicate}', [OrderByPredicate]),
        '{WherePredicate}'  , [WherePredicate]  )
    FROM @TQueries
    ORDER BY [Ordinal]
    FOR XML PATH (''), TYPE
  ).value('.', 'nvarchar(max)'),
  11,                      -- starting just after the first 'UNION ALL '
  CAST(0x7FFFFFFF AS int)  -- max int; no need to specify the exact length
) +
'),
RankedData AS (
  SELECT
    [Id],
    QueryRank,
    RowRank,
    ValueRank = ROW_NUMBER() OVER (PARTITION BY [Id] ORDER BY QueryRank)
  FROM AllData
)SELECT TOP (@top)
  [Id]
FROM RankedData
WHERE ValueRank = 1
ORDER BY
  QueryRank,
  RowRank
';

PRINT @FinalSQL;
EXECUTE sp_executesql @FinalSQL, N'@top int', 10;

基本的に、すべてのサブクエリは次の補助列を取得します。

  • QueryRank[Ordinal]– ;から派生した定数値(サブクエリの結果セット内) 。

  • RowRank–に基づいて行に割り当てられたランキング[OrderByPredicate]

結果セットはUNIONされ、すべての一意の値のすべてのエントリがValueRankクエリのランク付けに基づいて再度ランク付けされます()。

最終結果セットをプルするとき、重複は(条件によってValueRank = 1)抑制され、元の行の順序を保持するために句で使用されQueryRankます。RowRankORDER BY

前者ではクエリにパラメータを追加できるため、のEXECUTE sp_executesql @query代わりに使用しました。EXECUTE (@query)特に、返す結果の数をパラメーター化しました(の引数TOP)。EXECUTE ()ただし、他のものと同じように、を優先する場合は、その値を動的スクリプトに直接連結することもできますEXECUTE sq_executesql

必要に応じて、SQLFiddleでこのクエリを試すことができます。(注:SQL Fiddleバージョンは、@TQueriesテーブル変数をテーブルに置き換えTQueriesます。)

于 2013-01-12T18:58:49.440 に答える
1

これは、私が元の応答から石畳にまとめ、@AndriyMからのコメントによって改善されたものです。

DECLARE @sql_prefix NVARCHAR(MAX);
SET @sql_prefix = 
N'DECLARE @TResults TABLE (
    [Ordinal] INT IDENTITY(1,1),
    [ContentItemId] INT
);

DECLARE @max INT, @top INT;
SELECT @max = 10;';


DECLARE @sql_insert_template NVARCHAR(MAX), @sql_body NVARCHAR(MAX);
SET @sql_insert_template = 
N'SELECT @top = @max - COUNT(*) FROM @TResults; 
INSERT INTO @TResults
SELECT TOP (@top) [Id] 
FROM [dbo].[SourceTable] 
WHERE 
    {WherePredicate} 
    AND NOT EXISTS (
        SELECT 1 
        FROM @TResults AS [tr] 
        WHERE [tr].[ContentItemId] = [SourceTable].[Id]
    )
ORDER BY {OrderByPredicate};';

    WITH Query ([Ordinal],[SqlCommand]) AS (
        SELECT 
            [Ordinal],
            REPLACE(REPLACE(@sql_insert_template, '{WherePredicate}', [WherePredicate]), '{OrderByPredicate}', [OrderByPredicate])
        FROM @TQueries
    )
    SELECT 
        @sql_body = @sql_prefix + (
            SELECT [SqlCommand]
            FROM Query
            ORDER BY [Ordinal] ASC
            FOR XML PATH(''),TYPE).value('.', 'varchar(max)') + CHAR(13)+CHAR(10)
            +N' SELECT * FROM @TResults ORDER BY [Ordinal]';

    EXEC(@sql_body);

基本的な考え方は、テーブル変数を使用して各クエリの結果を保持することです。SQLのテンプレートを作成し、に格納されている内容に基づいてテンプレートの値を置き換えます@TQueries

スクリプト全体が完成したら、EXECで実行します。

于 2013-01-10T18:30:34.697 に答える