views:

2102

answers:

3

I have a set of records in my MS SQL table. With Date as the primary key. But the Dates are only for working days and not the continues days. Eg:

1/3/2000 12:00:00 AM 5209.540000000 5384.660000000 5209.540000000 5375.110000000 1/4/2000 12:00:00 AM 5533.980000000 5533.980000000 5376.430000000 5491.010000000 1/5/2000 12:00:00 AM 5265.090000000 5464.350000000 5184.480000000 5357.000000000 1/6/2000 12:00:00 AM 5424.210000000 5489.860000000 5391.330000000 5421.530000000 1/7/2000 12:00:00 AM 5358.280000000 5463.250000000 5330.580000000 5414.480000000 1/10/2000 12:00:00 AM 5617.590000000 5668.280000000 5459.970000000 5518.390000000 1/11/2000 12:00:00 AM 5513.040000000 5537.690000000 5221.280000000 5296.300000000 1/12/2000 12:00:00 AM 5267.850000000 5494.300000000 5267.850000000 5491.200000000

In this i am trying to introduce a new column to the table and the value to it should be the value of the 3rd cloumn minus the value of 3rd column of the previous working day. Please help me in writing such a query. I am finding it difficult as the dates are not present for the week ends.

+1  A: 

You need to break this down into 2 parts. First is updating your existing data, and second is ensuring all new data has the correct value added.

For the first part, consider using a CURSOR. It will probably take a while to run, but at least you only run it once. Use a CURSOR like a FOR loop; iterate through each row in your data, ignoring the first row (since you haven't specified how to calculate the value for the new column when there is no previous date). Most likely you should sort by date ascending, just in case.

As you iterate through, use variables to store the values from this row. As you loop, copy those variables into previous row versions before you get the new row. For example, you have a variable called 'Col3' and another one called 'lastCol3'. Before you loop to the next row (ie the cursor moves to the next row) you copy the value of col3 into lastCol3 and then you get the new value for col3. Now you have your current and previous value on a per row basis, and can call 'update' to update the new column.

For new data going forward, you need to ensure the new value is provided, or if you want SQL Server to do it, use a stored procedure which selects the most recent row, col3, and uses the value to calculate the new value before inserting into the table.

DarkwingDuck
+1  A: 

There are a few ways of doing this. Here is one.

CREATE TABLE MyTable
(
    MyDate datetime NOT NULL PRIMARY KEY,
    Col2 decimal(14,4) NOT NULL,
    Col3 decimal(14,4) NOT NULL,
    Col4 decimal(14,4) NOT NULL,
    Col5 decimal(14,4) NOT NULL
)
GO

INSERT INTO MyTable
SELECT '1/3/2000 12:00:00 AM', 5209.540000000, 5384.660000000, 5209.540000000, 5375.110000000 
 UNION ALL 
SELECT '1/4/2000 12:00:00 AM', 5533.980000000, 5533.980000000, 5376.430000000, 5491.010000000
 UNION ALL 
SELECT '1/5/2000 12:00:00 AM', 5265.090000000, 5464.350000000, 5184.480000000, 5357.000000000
 UNION ALL 
SELECT '1/6/2000 12:00:00 AM', 5424.210000000, 5489.860000000, 5391.330000000, 5421.530000000 
 UNION ALL 
SELECT '1/7/2000 12:00:00 AM', 5358.280000000, 5463.250000000, 5330.580000000, 5414.480000000 
 UNION ALL 
SELECT '1/10/2000 12:00:00 AM', 5617.590000000, 5668.280000000, 5459.970000000, 5518.390000000 
 UNION ALL 
SELECT '1/11/2000 12:00:00 AM', 5513.040000000, 5537.690000000, 5221.280000000, 5296.300000000 
 UNION ALL 
SELECT '1/12/2000 12:00:00 AM', 5267.850000000, 5494.300000000, 5267.850000000, 5491.200000000
GO

CREATE VIEW MyView 
AS
SELECT T1.*,
    CalculatedColumn = Col3 - 
      (SELECT Col3 FROM MyTable Q2
       WHERE Q2.MyDate = (SELECT MAX(Q1.MyDate) 
                          FROM MyTable Q1 
                          WHERE Q1.MyDate < T1.MyDate)
    )
FROM MyTable T1
GO

SELECT * FROM MyView
GO

Results

MyDate                  Col2      Col3      Col4      Col5      CalculatedColumn
----------------------- --------- --------- --------- --------- ----------------
2000-01-03 00:00:00.000 5209.5400 5384.6600 5209.5400 5375.1100 NULL
2000-01-04 00:00:00.000 5533.9800 5533.9800 5376.4300 5491.0100 149.3200
2000-01-05 00:00:00.000 5265.0900 5464.3500 5184.4800 5357.0000 -69.6300
2000-01-06 00:00:00.000 5424.2100 5489.8600 5391.3300 5421.5300 25.5100
2000-01-07 00:00:00.000 5358.2800 5463.2500 5330.5800 5414.4800 -26.6100
2000-01-10 00:00:00.000 5617.5900 5668.2800 5459.9700 5518.3900 205.0300
2000-01-11 00:00:00.000 5513.0400 5537.6900 5221.2800 5296.3000 -130.5900
2000-01-12 00:00:00.000 5267.8500 5494.3000 5267.8500 5491.2000 -43.3900
beach
You can probably optimise the innermost where clause to restrict which rows in Q1 are examined. You know that the prior row will be at most 2 or 3 days earlier.
Mike Thompson
@Mike It performs a CLUSTERED INDEX SEEK, which is very efficient. SQL Server is generally pretty good at self-optimizing.
beach
+1  A: 

Use a self-join and a case statement that takes advantage of SQLServers's built-in date function datepart(dw,@Date) .

I do feel duty-bound to note that making such a transformation in the first place is probably a bad idea.

SquareCog