6

SQL-Server 2005 を使用して、ASP.NET 4.0 の小さな Web アプリケーション用に作成したこのクエリを改善したいと考えています。このアプリケーションにより、ユーザーは製品 ID で検索し、次の情報を返すことができます。

  • 最高購入価格 + 最新の購入日 @ この価格
  • 最低購入価格 + 最新の購入日 @ この価格
  • 最近の購入価格 + 日付
  • 平均購入価格 (オプション、これによりアプリの有用性が向上するのではないかと考えました)

Products テーブルの構造は次のとおりです (関連する列のみを含めています。これは既に運用されている DB であり、これらは非 pk 列です)

  • product_id (nvarchar(20))
  • 価格 (10 進数 (19,2))
  • pDate (日時)

これまでのクエリを書き留める前に、複数のクエリを使用してこの情報を簡単に取得できると言いたいだけなので、これがベスト プラクティスである場合は、クエリの改善を無視しますが、必要なクエリの数を最小限に抑えることを目指していました。必要なすべての情報を取得します。

私がこれまでに持っているもの: (注: 価格 = 0 の行があるため、MIN 価格を探して一番下の行を無視しました)

SELECT price, MAX(pDate)
FROM Products
WHERE product_id = @product_id AND
     (price = (SELECT MAX(price)
               FROM Products
               WHERE product_id =@product_id) OR
      price = (SELECT MIN(price)
               FROM Products
               WHERE product_id = @product_id AND price > 0))
GROUP BY price

これで 2 行が返されます。

  • 最初 = 最低価格 + 日付
  • 2 行目 = 高値 + 日付

理想的には、ASP での情報の表示が簡単になるため、可能であれば上記の必要な情報をすべて含む 1 行をクエリで返すようにすることです。先に述べたように、複数のクエリがアプローチである場合、ここで複雑なクエリを書き直す必要はありません。

編集

ここにいくつかのサンプルデータがあります

サンプルデータ

必要なクエリ結果: (Excel で入力したので形式は無視してください)

ここに画像の説明を入力

Ken Benson のおかげで使用するクエリは次のとおりです。

SELECT TOP 1 prod.product_id,
   minp.price AS minprice, minp.pDate as minlastdate,
   maxp.price AS maxprice, maxp.pDate as maxlastdate,
   ag.price AS averageprice
FROM products AS prod
LEFT JOIN (SELECT lmd.product_id,max(lmd.pDate) as pDate,mn.price FROM products as lmd INNER JOIN 
           (SELECT product_id, min(price) AS price from products WHERE price > 0 group by product_id) as mn ON lmd.product_id=mn.product_id AND lmd.price=mn.price
                  group by lmd.product_id,mn.price ) AS minp ON minp.product_id=prod.product_id
LEFT JOIN (SELECT lxd.product_id,max(lxd.pDate) as pDate,mx.price FROM products as lxd INNER JOIN 
           (SELECT product_id, max(price) AS price from products group by product_id) as mx ON lxd.product_id=mx.product_id AND lxd.price=mx.price
              group by lxd.product_id,mx.price ) AS maxp ON maxp.product_id=prod.product_id
LEFT JOIN (SELECT product_id,avg(price) as price FROM products WHERE price > 0 GROUP BY product_id) AS ag ON ag.product_id=prod.product_id
WHERE prod.product_id=@product_id
4

4 に答える 4

1

テーブルへの結合をいくつか行うことができると思います...

Select product_id, min.price, min.pDate, max.price, max.pDate
FROM products as p
LEFT JOIN (Select Min(price), pDate, product_id FROM products GROUP BY product_id) 
   as min on min.product_id=p.product_id
LEFT JOIN (Select max(price), pDate, product_id FROM products GROUP BY product_id) 
   as max on max.product_id=p.product_id
Where p.product_id = @product_id

このコードの 2 番目のビットは、望ましい結果を生成する必要があります....

SELECT prod.product_id,
   minp.price AS minprice, minp.pDate as minlastdate,
   maxp.price AS maxprice, maxp.pDate as maxlastdate,
   ag.price AS averageprice
FROM products AS prod
 LEFT JOIN (SELECT lmd.product_id,max(lmd.pDate) as pDate,mn.price FROM products as lmd INNER JOIN 
           (SELECT product_id, min(price) AS price from products group by product_id) as mn ON lmd.product_id=mn.product_id
                  group by lmd.product_id,mn.price ) AS minp ON minp.product_id=prod.product_id
 LEFT JOIN (SELECT lxd.product_id,max(lxd.pDate) as pDate,mx.price FROM products as lxd INNER JOIN 
           (SELECT product_id, max(price) AS price from products group by product_id) as mx ON lxd.product_id=mx.product_id
                  group by lxd.product_id,mx.price ) AS maxp ON maxp.product_id=prod.product_id
 LEFT JOIN (SELECT product_id,avg(price) as price FROM products GROUP BY product_id) AS ag ON ag.product_id=prod.product_id
WHERE prod.product_id=1
LIMIT 1

はい - 「and」条件を省略しました:

SELECT TOP 1
 prod.product_id,
   minp.price AS minprice, minp.pDate as minlastdate,
   maxp.price AS maxprice, maxp.pDate as maxlastdate,
   ag.price AS averageprice
FROM products AS prod
 LEFT JOIN (SELECT lmd.product_id,max(lmd.pDate) as pDate,mn.price FROM products as lmd INNER JOIN 
           (SELECT product_id, min(price) AS price from products group by product_id) as mn ON lmd.product_id=mn.product_id **AND lmd.price=mn.price**
                  group by lmd.product_id,mn.price ) AS minp ON minp.product_id=prod.product_id
 LEFT JOIN (SELECT lxd.product_id,max(lxd.pDate) as pDate,mx.price FROM products as lxd INNER JOIN 
           (SELECT product_id, max(price) AS price from products group by product_id) as mx ON lxd.product_id=mx.product_id AND **lxd.price=mx.price**
                  group by lxd.product_id,mx.price ) AS maxp ON maxp.product_id=prod.product_id
 LEFT JOIN (SELECT product_id,avg(price) as price FROM products GROUP BY product_id) AS ag ON ag.product_id=prod.product_id
WHERE prod.product_id=@product_id
于 2012-08-14T17:54:49.207 に答える
0

答える試みは3回ありましたが、どれも希望どおりに機能しなかったので、その方法を説明します。これは、ストアドプロシージャを使用できることと、製品テーブルがそれほど大きくないことを前提としています。複数の個別のクエリが問題になる:

CREATE PROCEDURE myproc AS

  DECLARE @Price1 money
  DECLARE @Date1 smalldatetime

  DECLARE @Price2 money
  DECLARE @Date2 smalldatetime

  DECLARE @Price3 money
  DECLARE @Date3 smalldatetime

  DECLARE @Price4 money

  SELECT @Price1 = MAX(Price) FROM Products
  SELECT @Date1 = MAX(pDate) FROM Products WHERE Price=@Price1

  SELECT @Price2 = Min(Price) FROM Products WHERE Price >0
  SELECT @Date2 = MAX(pDate) FROM Products WHERE Price=@Price2

  SELECT @Date3 = Max(pDate) FROM Products
  SELECT @Price3 = MAX(Price) FROM Products WHERE pDate=@Date3 --max in case there are more than one purchases with the same date.

  SELECT @Price4 = AVG(Price) FROM Products WHERE Price>0

  SELECT @Price1 As MaxPrice, 
         @Date1 As MaxPriceDate,
         @Price2 As LowPrice, 
         @Date2 As LowPriceDate,
         @Price4 As AveragePrice,
         @Price3 As RecentPrice,
         @Price3 As RecentPriceDate
GO

誤植は許してください。私はこれをテストしませんでしたが、ストアドプロシージャを使用できる場合は、これで問題ありません。

したがって、これはクライアントから複数のクエリを実行することと大差ありませんが、すべてを1つのSPにまとめた方がパフォーマンスが向上するはずです。他の回答のコードの一部を使用してクエリの数を少し減らすこともできますが、わかりやすくするためにこのままにしておきます。

于 2012-08-14T21:38:59.040 に答える
0

ランキング関数と条件付き集計を組み合わせてこれを行います。

select product_id, 
       max(case when seqnum_hi = 1 then price end) as highPrice,
       max(case when seqnum_hi = 1 then pdate end) as highPrice_date
       max(case when seqnum_low = 1 then price end) as lowPrice,
       max(case when seqnum_low = 1 then pdate end) as lowPrice_date,
       max(case when seqnum_rec = 1 then price end) as recentPrice,
       max(case when seqnum_rec = 1 then pdate end) as recentPrice_date,
       avg(price) as avg_price
from (select p.*,
             row_number() over (partition by product_id order by price asc) as seqnum_low,
             row_number() over (partition by product_id order by price desc) as seqnum_hi,
             row_number() over (partition by product_id order by pdate desc) as seqnum_rec
      from price
      where product_id = @product_id
group by product_id

シーケンス番号は、関心のある特定の属性 (高価格、低価格、最新) を持つ行を識別します。条件付き最大は、それらの行から情報を選択するだけです。

于 2012-08-14T17:46:54.263 に答える
0

以下はあなたが望むものを得るはずです。かなり長いですが、読みやすいので、必要な人は誰でも簡単に変更できます。

;WITH CTE_MaxPrice AS
(
  SELECT product_id, MAX(P.price) AS MaxPrice
  FROM Products P
  GROUP BY product_id
  HAVING product_id = @product_id
),
CTE_MinPrice AS
(
  SELECT product_id, MIN(P.price) AS MinPrice
  FROM Products P
  GROUP BY product_id
  HAVING product_id = @product_id 
),
CTE_MaxPriceDate AS
(
  SELECT P.product_id, MAX(P.pDate) AS MaxDate
  FROM Products P
  INNER JOIN CTE_MaxPrice MaxP ON P.product_id = MaxP.product_id 
                               AND P.price = MaxP.MaxPrice
  GROUP BY P.product_id
),
CTE_MinPriceDate AS
(
  SELECT P.product_id, MAX(P.pDate) AS MinDate
  FROM Products P
  INNER JOIN CTE_MinPrice MinP ON P.product_id = MinP.product_id 
                               AND P.price = MinP.MinPrice
  GROUP BY P.product_id
)
SELECT MaxP.MaxPrice, MaxPD.MaxDate, 
       MinP.MinPrice, MinPD.MinDate, 
       RP.price AS RecentPrice, MAX(RP.pDate) AS RecentDate, 
       AVG(AP.price) AS AveragePrice 
FROM Products P
INNER JOIN CTE_MaxPrice MaxP ON P.product_id = MaxP.product_id
INNER JOIN CTE_MinPrice MinP ON P.product_id = MinP.product_id 
                             AND MinP.MinPrice > 0
INNER JOIN CTE_MaxPriceDate MaxPD ON P.product_id = MaxPD.product_id
INNER JOIN CTE_MinPriceDate MinPD ON P.product_id = MinPD.product_id
INNER JOIN Products RP ON P.product_id = RP.product_id
INNER JOIN Products AP ON P.product_id = AP.product_id
GROUP BY MaxP.MaxPrice, MaxPD.MaxDate, 
MinP.MinPrice, MinPD.MinDate, RP.price
HAVING P.product_id = @product_id
于 2012-08-14T18:09:08.467 に答える