I am using SQL Server 2005.
Let's say I have a product table and another price table so that I can track price changes over time. I need a query that retrieves individual products (the easy part), as well as every last product price and date of change.
Product table:
CREATE TABLE [dbo].[Products]( [ID] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](50) NOT NULL, [Price] [money] NOT NULL, CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED ( [ID] ASC ) ) ON [PRIMARY] GO INSERT INTO Products (Name, Price) VALUES ('Hat', 10); INSERT INTO Products (Name, Price) VALUES ('Shirt', 15); INSERT INTO Products (Name, Price) VALUES ('Pants', 20); INSERT INTO Products (Name, Price) VALUES ('Coat', 25); INSERT INTO Products (Name, Price) VALUES ('Shoes', 30);
PriceChanges table:
CREATE TABLE [dbo].[PriceChanges]( [ID] [int] IDENTITY(1,1) NOT NULL, [ProductId] [int] NOT NULL, [Price] [money] NOT NULL, [PriceChanged] [datetime] NOT NULL, CONSTRAINT [PK_PriceChanges] PRIMARY KEY CLUSTERED ( [ID] ASC ) ) ON [PRIMARY] GO INSERT INTO PriceChanges (ProductId, Price, PriceChanged) VALUES (1, 9.65, '1/1/2010'); INSERT INTO PriceChanges (ProductId, Price, PriceChanged) VALUES (1, 10.10, '1/2/2010'); INSERT INTO PriceChanges (ProductId, Price, PriceChanged) VALUES (1, 11.50, '1/3/2010'); INSERT INTO PriceChanges (ProductId, Price, PriceChanged) VALUES (2, 15.50, '1/4/2010'); INSERT INTO PriceChanges (ProductId, Price, PriceChanged) VALUES (2, 15.65, '1/5/2010'); INSERT INTO PriceChanges (ProductId, Price, PriceChanged) VALUES (3, 19.95, '1/6/2010'); INSERT INTO PriceChanges (ProductId, Price, PriceChanged) VALUES (4, 24.95, '1/7/2010');
Here's a query that returns too much:
SELECT p.ID ProductId, p.Name, COALESCE(c.Price, p.Price) Price, c.PriceChanged FROM dbo.Products AS p LEFT JOIN dbo.PriceChanges AS c ON c.ProductId = p.ID
Returns:
1 Hat 9.65 2010-01-01 00:00:00.000 1 Hat 10.10 2010-01-02 00:00:00.000 1 Hat 11.50 2010-01-03 00:00:00.000 2 Shirt 15.50 2010-01-04 00:00:00.000 2 Shirt 15.65 2010-01-05 00:00:00.000 3 Pants 19.95 2010-01-06 00:00:00.000 4 Coat 24.95 2010-01-07 00:00:00.000 5 Shoes 30.00 NULL
I need to return it:
1 Hat 11.50 2010-01-03 00:00:00.000 2 Shirt 15.65 2010-01-05 00:00:00.000 3 Pants 19.95 2010-01-06 00:00:00.000 4 Coat 24.95 2010-01-07 00:00:00.000 5 Shoes 30.00 NULL
This query works, but it got two nested choices, and it will make DBAs around the world cry:
SELECT p.ID ProductId, p.Name, COALESCE((SELECT TOP 1 Price FROM dbo.PriceChanges WHERE ProductId = p.ID ORDER BY PriceChanged DESC), p.Price) Price, (SELECT TOP 1 PriceChanged FROM dbo.PriceChanges WHERE ProductId = p.ID ORDER BY PriceChanged DESC) PriceChanged FROM dbo.Products AS p
What is the best way to do this?