Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Advice on SQL-Server Query

I'm looking to improve this query I wrote for a small web application in ASP.NET 4.0 using SQL-Server 2005. This application will allow the user to search by Product ID and have it return the following information:

  • Highest Purchase Price + Most Recent Date of purchase @ this price
  • Lowest Purchase Price + Most Recent Date of purchase @ this price
  • Most Recent Purchase Price + Date
  • Average Purchase Price (optional, i thought this might improve the usefulness of the app)

Here is the structure of the Products table (I'm only including relevant columns, this is a DB already in production and these are non-pk columns)

  • product_id (nvarchar(20))
  • price (decimal(19,2))
  • pDate (datetime)

Before I put down the query I have so far I just want to say that I can get this information easily through multiple queries, so if this is the best practice then disregard improving the query, but I was aiming to minimize the number of queries needed to get all needed information.

What I have so far: (Note: There are rows with price = 0 so I ignored those in the bottom select looking for the MIN price)

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

Now this is returning 2 rows:

  • first = the lowest price + date
  • second row = high price + date

What I would like ideally is to have a query return 1 row with all the needed information stated above if possible, as it would simplify displaying the information in ASP for me. And like I said earlier, if multiple queries is the be approach then no need to re-write a complex query here.

Edit

Here is some sample data

Sample Data

Desired query results: (ignore the format as I typed this in excel)

enter image description here

Here is the query I will be using thanks to 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
like image 975
D.Galvez Avatar asked Aug 14 '12 17:08

D.Galvez


People also ask

What is SQL Server query optimization?

SQL Query optimization is defined as the iterative process of enhancing the performance of a query in terms of execution time, the number of disk accesses, and many more cost measuring criteria. Data is an integral part of any application.


1 Answers

I think you can do a couple of joins back to the table ...

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

This second bit of code should produce desired results....

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

Yep - left out an 'and' condition:

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
like image 61
Ken Benson Avatar answered Oct 17 '22 10:10

Ken Benson