3

次のような非正規化テーブルがあるとします。

CREATE TABLE Persons
(
    Id           int identity primary key,
    FirstName    nvarchar(100),
    CountryName  nvarchar(100)
)

INSERT INTO Persons
VALUES ('Mark',    'Germany'),
       ('Chris',   'France'),
       ('Grace',   'Italy'),
       ('Antonio', 'Italy'),
       ('Francis', 'France'),
       ('Amanda',  'Italy');

各人の名前と、その国の一意の ID を返すクエリを作成する必要があります。ID は必ずしも連続している必要はありません。さらに重要なことは、それらは任意の順序である必要はありません。これを達成する最も効率的な方法は何ですか?

最も簡単な解決策は次のようDENSE_RANKです。

SELECT FirstName, 
       CountryName, 
       DENSE_RANK() OVER (ORDER BY CountryName) AS CountryId
FROM Persons

-- FirstName  CountryName  CountryId
-- Chris      France       1
-- Francis    France       1
-- Mark       Germany      2
-- Amanda     Italy        3
-- Grace      Italy        3
-- Antonio    Italy        3

ただし、これは私のコラムでソートを引き起こしCountryName、無駄なパフォーマンスを消費します。私はこの代替手段を思いつきました。これはROW_NUMBER、そのソートを抑制するためのよく知られたトリックを使用します。

SELECT P.FirstName, 
       P.CountryName,
       C.CountryId
FROM Persons P
    JOIN (
        SELECT CountryName, 
               ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS CountryId
        FROM Persons
        GROUP BY CountryName
    ) C
    ON C.CountryName = P.CountryName

-- FirstName  CountryName  CountryId
-- Mark       Germany      2
-- Chris      France       1
-- Grace      Italy        3
-- Antonio    Italy        3
-- Francis    France       1
-- Amanda     Italy        3

2 番目のクエリが一般的に (私の不自然なデータ セットだけでなく) パフォーマンスが向上すると仮定するのは正しいですか? どちらの方法でも違いを生む可能性のある要因はありますか (のインデックスなどCountryName)? よりエレガントな表現方法はありますか?

4

3 に答える 3

1

2 番目のクエリは、ハッシュ一致集計を使用して内部クエリを作成し、ハッシュ一致結合を使用して ID を実際のレコードにマップするため、おそらくソートを回避するでしょう。

これは実際にはソートされませんが、元のテーブルを 2 回スキャンする必要があります。

2 番目のクエリが一般的に (私の不自然なデータ セットだけでなく) パフォーマンスが向上すると仮定するのは正しいですか?

必ずしも。でクラスター化インデックスを作成した場合CountryName、並べ替えは問題にならず、すべてが 1 回のパスで実行されます。

よりエレガントな表現方法はありますか?

「正しい」計画は、ハッシュとハッシュ検索を一度に行うことです。

読み取られた各レコードは、ハッシュ テーブルと照合する必要があります。一致すると、保存された ID が返されます。失敗すると、新しい国がハッシュ テーブルに追加され、新しい ID が割り当てられ、新しく割り当てられた ID が返されます。

しかし、SQL Server にそのようなプランを 1 回のクエリで使用させる方法が思い浮かびません。

アップデート:

レコードが多く、国が少なく、最も重要なことに に非クラスター化インデックスがCountryNameある場合は、緩やかなスキャンをエミュレートして国のリストを作成できます。

DECLARE  @country TABLE
         (
         id INT NOT NULL IDENTITY PRIMARY KEY,
         countryName VARCHAR(MAX)
         )
;

WITH    country AS
        (
        SELECT  TOP 1
                countryName
        FROM    persons
        ORDER BY
                countryName
        UNION ALL
        SELECT  (
                SELECT  countryName
                FROM    (
                        SELECT  countryName,
                                ROW_NUMBER() OVER (ORDER BY countryName) rn
                        FROM    persons
                        WHERE   countryName > country.countryName
                        ) q
                WHERE   rn = 1
                )
        FROM    country
        WHERE   countryName IS NOT NULL
        )
INSERT
INTO    @country (countryName)
SELECT  countryName
FROM    country
WHERE   countryName IS NOT NULL
OPTION  (MAXRECURSION 0)

SELECT  p.firstName, c.id
FROM    persons p
JOIN    @country c
ON      c.countryName = p.countryName
于 2014-06-19T21:02:27.770 に答える
-1

group by use は、バックグラウンドでソート演算子も使用します (グループは、C# の Icomparable のような「ソートと比較」に基づいています)

于 2014-06-19T21:03:30.453 に答える