How to write a SQL query that generate values based on data in the table

I have a table that looks like the table below and I need to make a query that shows the previous price of the Stock

Stock_ID   | Stock_Code | Price | From_date  | To_date
-----------+------------+-------+------------+-----------
1          | XYZ        | 71    | 2013-01-05 | 2013-01-06
1          | XYZ        | 72    | 2013-01-07 | 2013-01-08
1          | XYZ        | 74    | 2013-01-09 | 2013-01-10
2          | QWE        | 24    | 2013-01-05 | 2013-01-06
2          | QWE        | 22    | 2013-01-07 | 2013-01-08
2          | QWE        | 30    | 2013-02-09 | 2013-01-10

The query should resulted in something like this:

Stock_ID   | Stock_Code | Price | From_date  | To_date   | Previous_Price
-----------+------------+-----+--------------+---------------------------
1          | XYZ        | 71    | 2013-01-05 | 2013-01-06| null
1          | XYZ        | 72    | 2013-01-07 | 2013-01-08| 71
1          | XYZ        | 74    | 2013-01-09 | 2013-01-10| 72
2          | QWE        | 24    | 2013-01-05 | 2013-01-06| null
2          | QWE        | 22    | 2013-01-07 | 2013-01-08| 24
2          | QWE        | 30    | 2013-02-09 | 2013-01-10| 22

what I tried:

SELECT *, 
(SELECT Price 
WHERE To_date in (SELECT DATEADD(day, -1, From_date) from StockTable))
FROM StockTable

However, the order is incorrect and the null is showed in the bottom. I suspect this is because it's a totally separate query and the second query does not directly uses the date from the SELECT * table.

I included Order By as well as suggested by one of the comments, but it still doesn't work.

Is it possible to do so without creating a new table and use join? What can I do to obtain the result?


Perhaps the window function lag() over() would be a good fit here

Select * 
      ,Prev_Price = lag(Price) over (partition by Stock_ID order by From_date)
 from YourTable

Results

enter image description here


A first principles approach is to simply use an inline query to select the previous record. It not a generally efficient solution but if the dates are contiguous and there are not many records for each Stock_Code then the performance might be acceptable:

SELECT s.Stock_ID, s.Stock_Code, s.Price, s.From_date, s.To_date
       , (SELECT Price 
          FROM StockTable lst
          WHERE lst.Stock_ID = s.Stock_ID 
            AND lst.Stock_Code = s.Stock_Code
            AND lst.To_date = (
                SELECT MAX(To_date)
                FROM StockTable mx
                WHERE mx.Stock_ID = s.Stock_ID 
                AND mx.Stock_Code = s.Stock_Code
                AND mx.To_date < s.To_Date
            )
          ) as Previous_Price
FROM StockTable s

You could however simplify this with a simple LAG window query:

SELECT Stock_ID, Stock_Code, Price, From_date, To_date
       , LAG (Price, 1, 0) 
         OVER (PARTITION BY Stock_ID, Stock_Code
               ORDER BY To_Date)
         as Previous_Price
FROM StockTable s

Try this fiddle for proof: http://sqlfiddle.com/#!18/a4372/2


I'm not sure that is the best solution, but:

create table #temp(
Stock_ID INT,
Stock_Code NChar(5),
Price INT,
From_Date DATE,
To_Date DATE
)

INSERT INTO #temp VALUES
(1, 'XYZ', 71, '2013-01-05', '2013-01-06'),
(1, 'XYZ', 72, '2013-01-07', '2013-01-08'),
(1, 'XYZ', 74, '2013-01-09', '2013-01-10'),
(2, 'QWE', 24, '2013-01-05', '2013-01-06'),
(2, 'QWE', 22, '2013-01-07', '2013-01-08'),
(2, 'QWE', 30, '2013-01-09', '2013-01-10')    
SELECT
        t1.Stock_ID, 
        t1.Stock_Code, 
        t1.Price, 
        t1.From_Date, 
        t1.To_Date,
        (SELECT
            t3.Price [Previouse_Price]
        FROM
            #temp t3
        INNER JOIN
            (SELECT
                MAX(t2.From_Date) [Previouse_Date]
            FROM
                #temp t2 
            WHERE 
                t1.Stock_ID = t2.Stock_ID 
                AND 
                t1.Stock_Code = t2.Stock_Code 
                AND
                t1.From_Date > t2.From_Date
            ) A ON A.Previouse_Date = t3.From_Date
        WHERE
            t1.Stock_ID = t3.Stock_ID 
            AND 
            t1.Stock_Code = t3.Stock_Code)
    FROM #temp t1