web-dev-qa-db-ja.com

SQLで関数に列を渡すにはどうすればよいですか?

私は中央値を次のように計算しています:

DECLARE @TEMP TABLE
(
    ID INT
)

Select 
(
        (
            Select Top 1 ID
            From   
            (
                Select  Top 50 Percent ID
                From    @Temp
                Where   ID Is NOT NULL
                Order By ID
            ) As A
            Order By ID DESC
        ) + 
        (
            Select Top 1 ID
            From   
            (
                Select  Top 50 Percent ID
                From    @Temp
                Where   ID Is NOT NULL
                Order By ID DESC
            ) As A
            Order By ID Asc
        )
) / 2

上記のクエリを使用します。しかし、私の場合、columnsを計算したい人にはMEDIANがたくさんあります。しかし、columnごとに上記のコードブロックを繰り返すのは良くないと思います。したがって、column値を受け入れ、処理して中央値を返す個別の関数を定義しようとしています。そのためにtable-value-funtionを定義する必要がありますか、それとも別のoptimized方法がありますか?


この質問は、次の質問に関連しています。

5
Kaishu

単純な中央値またはグループ化された中央値を計算するには、質問で示した方法よりもはるかに効率的な方法があります。

中央値を計算する最も速い方法は何ですか?
グループ化された中央値に対する最善のアプローチ

2012年の総合優勝者はPeter Larssonによる方法です。パターンは次のとおりです。

単純中央値

SELECT
    Median = AVG(1.0 * SQ.YourColumn)
FROM 
(
    SELECT NumRows = COUNT_BIG(*) 
    FROM dbo.YourTable
    WHERE ColumnName IS NOT NULL
) AS C
CROSS APPLY 
(
    SELECT YT.ColumnName
    FROM dbo.YourTable AS YT
    WHERE YT.ColumnName IS NOT NULL
    ORDER BY YT.ColumnName ASC
    OFFSET (C.NumRows - 1) / 2 ROWS
    FETCH NEXT 1 + (1 - C.NumRows % 2) ROWS ONLY
) AS SQ;

グループ化された中央値

SELECT
    SQ2.GroupingColumn,
    SQ2.Median
FROM 
(
    SELECT
        GroupingColumn,
        NumRows = COUNT_BIG(*) 
    FROM dbo.YourTable
    WHERE ColumnName IS NOT NULL
    GROUP BY
        GroupingColumn
) AS C
CROSS APPLY 
(
    SELECT 
        Median = AVG(1.0 * SQ1.YourColumn)
    FROM
    (
        SELECT YT.ColumnName
        FROM dbo.YourTable AS YT
        WHERE 
            YT.GroupingColumn = C.GroupingColumn
            AND YT.ColumnName IS NOT NULL
        ORDER BY 
            YT.ColumnName ASC
            OFFSET (C.NumRows - 1) / 2 ROWS
            FETCH NEXT 1 + (1 - C.NumRows % 2) ROWS ONLY
    ) AS SQ1
) AS SQ2;

上記のOFFSETメソッドのパフォーマンスを最大化するには、 ロックのヒントを追加する (高度なトピック)が必要になる場合があります。もちろん、適切な索引付けも必要です。

コードの再利用

これは動的SQLの実行を許可しないため、T-SQL関数を使用して直接達成することは困難です(列名を渡すことを考えていた場合)。

これを回避する方法はいくつかあります。たとえば、関数を使用して動的SQLテキスト自体を生成し、それを呼び出し元が実行できるようにします。質問には、どのアプローチがあなたに最も適しているかを説明するのに十分な詳細がありません。

11
Paul White 9

中央値を計算する場合、使用しているSQL Serverのバージョン(または他の誰か)によっては、いくつかの方法があります。 Dwain CampsはSimple Talkについて2つの記事を書き、さまざまな人々からいくつかの純粋なT-SQLオプションを収集し、それぞれの例を示し、それらのパフォーマンスを比較しました。

ただし、これらのメソッドを関数にカプセル化して簡単に再利用できるとは思いません。そのためには、SQLCLRを使用して ser-Defined Aggregate (UDA)を作成する必要があります。 Medianを例にしてUDAを作成する方法を示す記事を数年前に書きました:-)

SQL Server 2005 UDTとUDAを最大限に活用する

その記事に関して、覚えておいてください:

  • この記事は、SQL Server 2008が登場する前の2007年に書かれたもので、記事に示された圧縮手法をいくらか不要にする改良(つまり、8000バイトを超えるデータを格納できるようにする)が導入されました。ただし、GZipStreamを削除してMaxByteSize8000から-1に変更するのはかなり簡単です。
  • 記事の下部にUDAをインストールするSQLスクリプトが含まれているため、何もコーディングせずにneedしないでください。中央値集計をダウンロード、実行、使用するだけでよい:-)

以下も参照してください。

5
Solomon Rutzky