6

我希望改进我为使用 SQL-Server 2005 在 ASP.NET 4.0 中的小型 Web 应用程序编写的此查询。此应用程序将允许用户按产品 ID 搜索并让它返回以下信息:

  • 最高购买价格 + 最近购买日期@此价格
  • 最低购买价格 + 最近购买日期@此价格
  • 最近购买价格 + 日期
  • 平均购买价格(可选,我认为这可能会提高应用程序的实用性)

这是 Products 表的结构(我只包括相关列,这是一个已经在生产中的数据库,这些是非 pk 列)

  • product_id (nvarchar(20))
  • 价格(十进制(19,2))
  • pDate(日期时间)

在我放下查询之前,我只想说我可以通过多个查询轻松获取这些信息,所以如果这是最佳实践,那么请忽略改进查询,但我的目标是尽量减少所需的查询数量获取所有需要的信息。

到目前为止我所拥有的:(注意:有价格 = 0 的行,所以我忽略了底部选择中寻找最低价格的行)

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 行:

  • 第一个 = 最低价 + 日期
  • 第二行 = 高价 + 日期

如果可能的话,我希望有一个查询返回 1 行,其中包含上述所有需要的信息,因为它可以简化在 ASP 中显示信息的过程。就像我之前说的,如果多个查询是 be 方法,那么不需要在这里重新编写一个复杂的查询。

编辑

这是一些示例数据

样本数据

期望的查询结果:(忽略我在 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

这第二段代码应该会产生预期的结果......

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

是的 - 遗漏了一个“和”条件:

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

我会结合排名函数和条件聚合来做到这一点:

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 回答
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

请原谅任何印刷错误,我没有对此进行测试,但是如果您可以使用存储过程,这将起作用。

因此,这与从客户端执行多个查询没有太大区别,但将它们全部放入单个 SP 应该会更好。您还可以通过使用其他答案中的一些代码来减少查询的数量,但为了清楚起见,我将其保留为这种方式。

于 2012-08-14T21:38:59.040 回答