web-dev-qa-db-ja.com

サブクエリをJOINおよびCROSS APPLYにリファクタリングし、親テーブルのレコードごとに1行のみを取得します

次のクエリがあるとします。

SELECT
  p.ProductName,
  CASE
    WHEN EXISTS(SELECT 1 FROM Product WHERE ProductSuperID = p.ProductSuperID AND HasImage = 1)
    THEN
      1
    ELSE
      0
    END
  AS HasImage,
  (SELECT Sum(StockBalance) FROM Product WHERE ProductSuperID = p.ProductSuperID) AS StockBalance,
  CASE
    WHEN EXISTS(SELECT 1 FROM Product WHERE ProductSuperID = p.ProductSuperID AND Price IS NULL)
    AND EXISTS(SELECT 1 FROM Product WHERE ProductSuperID = p.ProductSuperID AND DiscountPrice IS NULL)
    THEN
      0
    ELSE
      1
    END
    AS HasPrice
FROM ProductSuper p

-- SCHEMA
CREATE TABLE ProductSuper
(
  ProductSuperID int,
  ProductName varchar(255)
)
CREATE TABLE Product
(
  ProdID int,
  ProductSuperID int,
  HasImage bit,
  StockBalance int,
  Price decimal(10,2),
  DiscountPrice decimal(10,2)
)
INSERT INTO ProductSuper
  (ProductSuperID, ProductName)
VALUES
  (1, 'Product 1'),
  (2, 'Product 2')
INSERT INTO Product
  (ProductSuperID, HasImage, StockBalance, Price, DiscountPrice)
VALUES
  (1, 0, 10, 10.00, 9.00),
  (1, 0, 0, 10.00, 9.00),
  (2, 0, 10, 10.00, 9.00),
  (2, 0, 2, 10.00, 9.00),
  (2, 1, 5, 10.00, 9.00)


コードの重複を避けるためにJOINまたはCROSS APPLYを使用するように書き直す方法を学びたいです。 JOINベースのバージョン(およびAPPLYを使用したバージョン)を作成しようとしましたが、ProductSuperテーブルの各行について複数の結果が返されましたが、1行しか必要ありません。

つまり、期待される結果:

+-------------+-----+-----+-----+
|  Product 1  |  0  |  10 |  1  |
+-------------+-----+-----+-----+
|  Product 2  |  1  |  17 |  1  |
+-------------+-----+-----+-----+

(この特定のコードでは、サブクエリが高速であるため、書き換えによるメリットはほとんどないことを認識しています。しかし、これは単なる例にすぎません。)

ありがとう。

2
Marc.2377

私はおそらくこのクエリを以下のように構成します

WITH ProductDetails
     AS (SELECT ProductSuperID,
                HasImage = MAX(CASE WHEN HasImage = 1 THEN 1 ELSE 0 END),
                StockBalance = Sum(StockBalance),
                HasPrice = CASE WHEN COUNT(*) = COUNT(Price) AND COUNT(*) = COUNT(DiscountPrice) THEN 1 ELSE 0 END
         FROM   Product
         GROUP  BY ProductSuperID)
SELECT p.ProductName,
       HasImage = ISNULL(pd.HasImage,0),
       pd.StockBalance,
       HasPrice = ISNULL(pd.HasPrice,0)
FROM   ProductSuper p
       LEFT JOIN ProductDetails pd
         ON p.ProductSuperID= pd.ProductSuperID; 

なので CROSS APPLYこのように書くことができます

SELECT ps.ProductName,
       pd.HasImage,
       pd.StockBalance,
       pd.HasPrice
FROM   ProductSuper ps
       CROSS APPLY (SELECT HasImage = MAX(CASE WHEN HasImage = 1 THEN 1 ELSE 0 END),
                           StockBalance = Sum(StockBalance),
                           HasPrice = CASE WHEN COUNT(*) = COUNT(Price) AND COUNT(*) = COUNT(DiscountPrice) THEN 1 ELSE 0 END
                    FROM   Product p
                    WHERE  p.ProductSuperID= ps.ProductSuperID) pd 
4
Martin Smith