13

同じテーブルからいくつかの値を変換(ルックアップ)する必要がある場合があります。私が最初に書いた方法は、サブクエリを使用することでした。

SELECT
    (SELECT id FROM user WHERE user_pk = created_by) AS creator,
    (SELECT id FROM user WHERE user_pk = updated_by) AS updater,
    (SELECT id FROM user WHERE user_pk = owned_by) AS owner,
    [name]
FROM asset

私はこのサブクエリを頻繁に使用しているので(つまり、これらのフィールドを持つテーブルが約50あります)、サブクエリにさらにコードを追加する必要があるかもしれません(たとえば、「AND active = 1」)。 dこれらをユーザー定義関数UDFに入れて、それを使用します。しかし、そのUDFを使用したパフォーマンスはひどいものでした。

CREATE FUNCTION dbo.get_user ( @user_pk INT )
RETURNS INT
AS BEGIN 
    RETURN ( SELECT id
             FROM   ice.dbo.[user]
             WHERE  user_pk = @user_pk )
END

SELECT dbo.get_user(created_by) as creator, [name]
FROM asset

#1のパフォーマンスは1秒未満です。#2のパフォーマンスは約30秒です...

なぜ、またはもっと重要なことに、SQL Server 2008でコーディングできる方法があるので、それほど多くのサブクエリを使用する必要はありませんか?

編集:

これがいつ役立つかについてのもう少しの説明。この単純なクエリ(つまり、ユーザーIDの取得)は、ユーザーのテキストが必要な場合、言語を取得するためにプロファイルに参加し、言語をフェッチする必要があるかどうかを会社に確認する必要があるため、はるかに複雑になります。代わりにそこから編集し、翻訳されたテキストを取得するために翻訳テーブルを使用します。そして、これらのクエリのほとんどでは、パフォーマンスは読みやすさと保守性の二次的な問題です。

4

4 に答える 4

34

UDFはクエリオプティマイザーのブラックボックスであるため、すべての行に対して実行されます。行ごとのカーソルを実行しています。アセットの各行について、別のテーブルでIDを3回検索します。これは、スカラーまたはマルチステートメントUDFを使用する場合に発生します(インラインUDFは、外部クエリに展開される単なるマクロです)

この問題に関する多くの記事の1つは、「スカラー関数、インライン化、およびパフォーマンス:退屈な投稿の面白いタイトル」です。

サブクエリは、行ごとの操作を相互に関連付けて回避するように最適化できます。

あなたが本当に欲しいのはこれです:

SELECT
   uc.id AS creator,
   uu.id AS updater,
   uo.id AS owner,
   a.[name]
FROM
    asset a
    JOIN
    user uc ON uc.user_pk = a.created_by
    JOIN
    user uu ON uu.user_pk = a.updated_by
    JOIN
    user uo ON uo.user_pk = a.owned_by

2019年2月更新

SQL Server 2019は、この問題の修正を開始します。

于 2009-02-04T10:09:14.653 に答える
13

他の投稿者が示唆しているように、結合を使用すると、全体的に最高のパフォーマンスが確実に得られます。

ただし、50 のような類似の結合またはサブクエリを維持するという頭痛の種を避けたいと述べたので、次のようにインライン テーブル値関数を使用してみてください。

CREATE FUNCTION dbo.get_user_inline (@user_pk INT)
RETURNS TABLE AS
RETURN
(
    SELECT TOP 1 id
    FROM ice.dbo.[user]
    WHERE user_pk = @user_pk
        -- AND active = 1
)

元のクエリは次のようになります。

SELECT
    (SELECT TOP 1 id FROM dbo.get_user_inline(created_by)) AS creator,
    (SELECT TOP 1 id FROM dbo.get_user_inline(updated_by)) AS updater,
    (SELECT TOP 1 id FROM dbo.get_user_inline(owned_by)) AS owner,
    [name]
FROM asset

インライン テーブル値関数は、スカラー関数または複数ステートメントのテーブル値関数よりもパフォーマンスが優れている必要があります。

パフォーマンスは元のクエリとほぼ同じになるはずですが、将来の変更は UDF で行うことができるため、保守性が大幅に向上します。

于 2009-02-04T11:54:54.177 に答える
2

同じ結果を得るには (ユーザーが削除されているかアクティブでない場合は NULL)。

 select 
    u1.id as creator,
    u2.id as updater,
    u3.id as owner,
    [a.name]
 FROM asset a
        LEFT JOIN user u1 ON (u1.user_pk = a.created_by AND u1.active=1) 
        LEFT JOIN user u2 ON (u2.user_pk = a.created_by AND u2.active=1) 
        LEFT JOIN user u3 ON (u3.user_pk = a.created_by AND u3.active=1) 
于 2009-02-04T10:40:10.723 に答える
0

何か不足していますか?なぜこれが機能しないのですか?テーブルに既にあるIDのみを選択しています:

select created_by as creator, updated_by as updater, 
owned_by as owner, [name]
from asset

ところで、設計ではname、フィールド名として などのキーワードを避ける必要があります。

于 2009-02-04T14:53:11.353 に答える