有关SQL-Server查询的建议

时间:2012-08-14 17:14:45

标签: asp.net sql sql-server-2005

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

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

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

  • product_id(nvarchar(20))
  • 价格(小数(19,2))
  • pDate(datetime)

在我放下查询之前,我到目前为止我只想说我可以通过多个查询轻松获取此信息,因此如果这是最佳实践,则忽略改进查询,但我的目标是最小化数量获取所有必需信息所需的查询。

到目前为止我所拥有的:(注意:有些行的价格= 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行:

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

我最理想的是让查询返回1行,如果可能的话,上面包含上述所有必需信息,因为它可以简化在ASP中显示ASP中的信息。就像我之前说的那样,如果有多个查询是方法,则无需在此处重新编写复杂查询。

修改

以下是一些示例数据

Sample Data

所需的查询结果:(忽略我在excel中输入的格式)

enter image description here

以下是我将使用的查询,感谢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 个答案:

答案 0 :(得分: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

答案 1 :(得分: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

序号表示具有您关注的特定属性的行(高价,低价,最近)。条件max然后只选择那些行中的信息。

答案 2 :(得分: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

答案 3 :(得分:0)

好了,因为已经有三次尝试回答,而且没有一种方法可以按照你想要的方式工作,我会告诉你我将如何做 - 这假设你可以使用存储过程并假设产品table不是很大,以至于多个单独的查询会成为一个问题:

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中。你也可以通过使用其他答案中的一些代码来减少一些查询,但为了清楚起见,我已经这样做了。