14

ユーザーごとに1つの行を選択しようとしています。どの画像を取得してもかまいません。このクエリはMySQLで機能しますが、SQLServerでは機能しません。

SELECT user.id, (images.path + images.name) as 'image_path'
FROM users
JOIN images ON images.user_id = users.id
GROUP BY users.id
4

6 に答える 6

17

MIN/MAXこれまでに集計を使用して投稿されたソリューションは、ROW_NUMBERグループごとに1つを選択する前に、一致するすべての行を検査する必要があるため、(データ分散によっては)最も効率的ではない場合があります。

例としてAdventureWorksサンプルデータベースを使用すると、次のクエリはすべて1つTransactionTypeを選択しReferenceOrderID、それぞれのトランザクション履歴テーブルから次のようになりますProductID

MIN/MAX集計を使用する

SELECT
    p.ProductID,
    MIN(th.TransactionType + STR(th.ReferenceOrderID, 11))
FROM Production.Product AS p
INNER JOIN Production.TransactionHistory AS th ON
    th.ProductID = p.ProductID
GROUP BY
    p.ProductID;

集計クエリプラン

使用するROW_NUMBER

WITH x AS 
(
    SELECT 
        th.ProductID, 
        th.TransactionType, 
        th.ReferenceOrderID,
        rn = ROW_NUMBER() OVER (PARTITION BY th.ProductID ORDER BY (SELECT NULL))
    FROM Production.TransactionHistory AS th
)
SELECT
    p.ProductID,
    x.TransactionType,
    x.ReferenceOrderID
FROM Production.Product AS p
INNER JOIN x ON x.ProductID = p.ProductID
WHERE
    x.rn = 1
OPTION (MAXDOP 1);

行番号プラン

内部のみのANY集計を使用する

SELECT
    q.ProductID, 
    q.TransactionType, 
    q.ReferenceOrderID 
FROM 
(
    SELECT 
        p.ProductID, 
        th.TransactionType, 
        th.ReferenceOrderID,
        rn = ROW_NUMBER() OVER (
            PARTITION BY p.ProductID 
            ORDER BY p.ProductID)
    FROM Production.Product AS p
    JOIN Production.TransactionHistory AS th ON p.ProductID = th.ProductID
) AS q
WHERE
    q.rn = 1;

ANY集計の詳細については、このブログ投稿を参照してください。

任意の骨材

非決定論的で相関サブクエリを使用するTOP

SELECT p.ProductID,
    (
    -- No ORDER BY, so could be any row
    SELECT TOP (1) 
        th.TransactionType + STR( th.ReferenceOrderID, 11)
    FROM Production.TransactionHistory AS th WITH (FORCESEEK) 
    WHERE
        th.ProductID = p.ProductID
    )
FROM Production.Product AS p;

トップ1

で使用CROSS APPLYするTOP (1)

前のクエリは連結を必要とNULLし、トランザクション履歴のない製品のを返します。CROSS APPLYwithを使用するとTOP、両方の問題が解決します。

SELECT
    p.Name, 
    ca.TransactionType,
    ca.ReferenceOrderID
FROM Production.Product AS p
CROSS APPLY
(
    SELECT TOP (1) 
        th.TransactionType,
        th.ReferenceOrderID
    FROM Production.TransactionHistory AS th WITH (FORCESEEK) 
    WHERE 
        th.ProductID = p.ProductID
) AS ca;

クロスアプライプラン

最適なインデックスを作成し、各ユーザーが通常多くの画像を持っているAPPLY場合は、が最も効率的です。

于 2013-03-10T02:48:29.267 に答える
4

ユーザーが複数の画像を持っていて、1つの画像だけが必要な場合、どれが必要ですか?MySQLには、選択を強制することなく、古い任意の値を提供するだけの、お粗末な構文がありますが、SQLServerでは選択できます。1つの方法はMIN

SELECT u.id, MIN(i.path + i.name) AS image_path
FROM dbo.users AS u
INNER JOIN dbo.images AS i
ON u.id = i.user_id
GROUP BY u.id;

MAXの代わりに使用することもできますMIN。また、SQL Serverのバージョンによっては、実際にもっと多くの列が必要かどうかによって、これを少し効率的に行う他の方法がある場合があります(並べ替え/グループ作業の一部を回避します)。たとえば、パスと名前を別々にしたい場合、これはあまりうまくいきません:

SELECT u.id, MIN(i.path), MIN(i.name)
FROM dbo.users AS u
INNER JOIN dbo.images AS i
ON u.id = i.user_id
GROUP BY u.id;

...理論的には2つの異なる行からパスと名前を取得でき、この結果はもはや意味をなさないためです。したがって、代わりにこれを行うことができます:

;WITH x AS 
(
  SELECT user_id, path, name, rn = ROW_NUMBER() OVER 
    (PARTITION BY user_id ORDER BY (SELECT NULL))
  FROM dbo.images
)
SELECT u.id, x.path, x.name
FROM dbo.users AS u
INNER JOIN x
ON u.id = x.user_id
WHERE x.rn = 1;

既存のケースでこのバリエーションを使用することが理にかなっているかどうかは、これら2つのテーブルのインデックス作成方法に大きく依存しますが、このアプローチを試して、計画/パフォーマンスを比較することができます。

;WITH x AS 
(
  SELECT user_id, path + name AS image_path, rn = ROW_NUMBER() OVER 
    (PARTITION BY user_id ORDER BY (SELECT NULL))
  FROM dbo.images
)
SELECT u.id, x.image_path
FROM dbo.users AS u
INNER JOIN x
ON u.id = x.user_id
WHERE x.rn = 1;

SELECT NULL(そして、の狭いインデックスの先頭の列に置き換えてみてくださいdbo.images。)

AS 'alias'PS構文を使用しないでください。この形式は非推奨になり、エイリアスは文字列リテラルのように見えます。また、スキーマプレフィックスを常に使用し、エイリアスを使用して、クエリ全体で完全なテーブル名を繰り返す必要がないようにします。

于 2013-03-09T19:28:46.493 に答える
3

集計関数が必要です。適切な集計関数はアプリケーションに依存します。それはあなたが言うことができる唯一の人であることを意味します。それに対する1つの原始的なハック:

SELECT user.id, max((images.path + images.name)) as 'image_path'
FROM users
JOIN images ON images.user_id = users.id
GROUP BY users.id

MySQLによるGROUPBY句の処理は、広くBADと見なされています。

于 2013-03-09T19:27:24.420 に答える
2

必要に応じて最大または最小を使用します。

SELECT user.id, max(images.path + images.name) as image_path
FROM users
      JOIN images ON images.user_id = users.id
GROUP BY users.id
于 2013-03-09T19:28:15.940 に答える
1

1人のユーザーが複数の画像を使用できる場合、これにより最初の(アルファベット順の)エントリが選択されます

SELECT user.id, min(images.path + images.name) as image_path
FROM users
JOIN images ON images.user_id = users.id
GROUP BY users.id
于 2013-03-09T19:26:40.257 に答える
1

使用GROUP BYする場合は、集計する列と他の集計関数のみを使用できます。

これを実現する1つの方法は次のとおりです。

SELECT user.id, (MAX(images.path) + MAX(images.name)) as 'image_path'
FROM users
JOIN images ON images.user_id = users.id
GROUP BY users.id

あなたが望む可能性が高いですが:

SELECT user.id, MAX(images.path + images.name)) as 'image_path'
FROM users
JOIN images ON images.user_id = users.id
GROUP BY users.id
于 2013-03-09T19:26:45.517 に答える