Number Range diff - sql-server

I have a question, I have a sample data shown below: my question is I want to calculate the difference between second row starting number (150) to first row ending number (100).
Can any one please help me? This answer will reduce some manual work what I'm doing daily.
Thanks

Sample data
create table MyTable
(
Model nvarchar(5),
StartingNo int,
EndingNo int
);
insert into MyTable (Model, StartingNo, EndingNo) values
('X', 1, 100),
('X', 150, 200),
('Y', 10, 30),
('Y', 1, 5);
Solution 1
This solution uses an outer apply to fetch the previous row.
select t1.*,
t3.EndingNo as PreviousEndingNo,
t1.StartingNo - t3.EndingNo as Diff
from MyTable t1
outer apply ( select top 1 t2.EndingNo -- first row...
from MyTable t2
where t2.Model = t1.Model -- ... with same model
and t2.StartingNo < t1.StartingNo -- ... that comes before current row
order by t2.StartingNo desc ) t3 -- ... when sorting on start number
order by t1.Model, t1.StartingNo;
Solution 2
This solution uses the lag function to fetch the previous row.
select t1.*,
lag(t1.EndingNo) over(partition by t1.Model order by t1.StartingNo) as PreviousEndingNo,
t1.StartingNo - lag(t1.EndingNo) over(partition by t1.Model order by t1.StartingNo) as Diff
from MyTable t1
order by t1.Model, t1.StartingNo;
Result
Model StartingNo EndingNo PreviousEndingNo Diff
----- ----------- ----------- ---------------- -----------
X 1 100 NULL NULL
X 150 200 100 50
Y 1 5 NULL NULL
Y 10 30 5 5
Fiddle to see everything in action.

Related

SQL Server - recursively calculated column

I need to calculate a column based of a seed row where each row's value uses the "previous" row's values. I feel like this should be a recursive query but I can't quite wrap my head around it.
To illustrate:
BOP EOP IN OUT Wk_Num
--------------------------------------
6 4 10 12 1
? ? 2 6 2
? ? 7 5 3
... ... ... ... ...
So the next row's BOP and EOP columns need to be calculated using the seed row. The IN and OUT values are already present in the table.
BOP = (previous row's EOP)
EOP = (Previous row's EOP) + IN - OUT [where IN and OUT are from the current row)
OUTPUT of this example should look like:
BOP EOP IN OUT Wk_num
-------------------------------------
6 4 10 12 1
4 0 2 6 2
0 2 7 5 3
2 6 4 0 4
... ... ... ... ...
You can use a Recursive CTE for this;
WITH RecursiveCTE AS (
-- Base Case
SELECT
BOP,
EOP,
[IN],
[OUT],
[WK_Num]
FROM [someTable]
WHERE BOP IS NOT NULL
UNION ALL
SELECT
r.EOP AS BOP,
r.EOP + r2.[In] - r2.[Out] AS EOP,
r2.[IN],
r2.[OUT],
r2.[WK_Num]
FROM [someTable] r2
INNER JOIN [RecursiveCTE] r
ON r2.[Wk_Num] = r.[Wk_Num] + 1
)
SELECT * FROM RecursiveCTE
Here is a SQL Fiddle: http://sqlfiddle.com/#!18/e041f/1
You basically define the base case as the first row (by saying the row with BOP != null), then join to each following week with the Wk_Num + 1 join, and reference the previous rows values
You can use SUM OVER like this:
DECLARE #TempTable AS TABLE(T_In INT, T_Out INT, T_WeekNum INT)
INSERT INTO #TempTable VALUES (6, 0, 0)
INSERT INTO #TempTable VALUES (10, 12, 1)
INSERT INTO #TempTable VALUES (2, 6, 2)
INSERT INTO #TempTable VALUES (7, 5, 3)
INSERT INTO #TempTable VALUES (4, 0, 4)
SELECT
SUM(T_In - T_Out) OVER(ORDER BY T_WeekNum ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - T_In + T_Out AS T_Bop,
SUM(T_In - T_Out) OVER(ORDER BY T_WeekNum ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS T_Eop,
T_In,
T_Out,
T_WeekNum
FROM #TempTable
The calculation is the same for BOP and EOP but the values from the current row are subtracted from the BOP column to get the value from the last row.

How to calculate running total over specific date or better?

I would like to calculate the what orders can be completed and what dates are missing (diff) after completing as many orders as possible at the moment. Picked in order of FEFO.
When thinking about the problem I think that some kind of a running sum based on both the dates of the stock and the orders would be one way to go. Based on Calculate running total / running balance and other similar threads it seems like a good fit for the problem - but I'm open to other solutions.
Example code
DECLARE #stockTable TABLE (
BATCH_NUM nvarchar(16),
QUANTITY int,
DATE_OUTGO DATE
)
DECLARE #orderTable TABLE (
ORDER_ID int,
QUANTITY int,
DATE_OUTGO DATE
)
INSERT INTO #stockTable (BATCH_NUM, QUANTITY, DATE_OUTGO)
VALUES
('1000', 10, '2017-08-25'),
('1001', 20, '2017-08-26'),
('1002', 10, '2017-08-27')
INSERT INTO #orderTable (ORDER_ID, QUANTITY, DATE_OUTGO)
VALUES
(1, 10, '2017-08-25'),
(1, 12, '2017-08-25'),
(2, 10, '2017-08-26'),
(3, 10, '2017-08-26'),
(4, 16, '2017-08-26')
SELECT
DATE_OUTGO,
SUM(RunningTotal) AS DIFF
FROM (
SELECT
orderTable.DATE_OUTGO AS DATE_OUTGO,
RunningTotal = SUM(stockTable.QUANTITY - orderTable.QUANTITY ) OVER
(ORDER BY stockTable.DATE_OUTGO ROWS UNBOUNDED PRECEDING)
FROM
#orderTable orderTable
INNER JOIN #stockTable stockTable
ON stockTable.DATE_OUTGO >= orderTable.DATE_OUTGO
GROUP BY
orderTable.DATE_OUTGO,
stockTable.DATE_OUTGO,
stockTable.QUANTITY,
orderTable.QUANTITY
) A
GROUP BY DATE_OUTGO
Results
The correct result would look like this.
-------------------------
| OT_DATE_OUTGO | DIFF |
-------------------------
| 2017-08-25 | 0 |
-------------------------
| 2017-08-26 | -18 |
-------------------------
My result currently looks like this.
-------------------------
| OT_DATE_OUTGO | DIFF |
-------------------------
| 2017-08-25 | 80 |
-------------------------
| 2017-08-26 | 106 |
-------------------------
I've taken out complexities like item numbers, different demands simultaneously (using the exact date only and date or better) etc. to simplify the core issue as much as possible.
Edit 1:
Updated rows in both tables and results (correct and with original query).
First answer gave a diff of -12 on 2017-08-25 instead of 0. But 2017-08-26 was correct.
You can use the following query:
;WITH ORDER_RUN AS (
SELECT SUM(SUM(QUANTITY)) OVER (ORDER BY DATE_OUTGO) AS ORDER_RUNTOTAL,
DATE_OUTGO
FROM #orderTable
GROUP BY DATE_OUTGO
), STOCK_RUN AS (
SELECT SUM(SUM(QUANTITY)) OVER (ORDER BY DATE_OUTGO) AS STOCK_RUNTOTAL,
DATE_OUTGO
FROM #stockTable
GROUP BY DATE_OUTGO
)
SELECT ORR.DATE_OUTGO AS OT_DATE_OUTGO,
X.STOCK_RUNTOTAL - ORDER_RUNTOTAL AS DIFF
FROM ORDER_RUN AS ORR
OUTER APPLY (
SELECT TOP 1 STOCK_RUNTOTAL
FROM STOCK_RUN AS SR
WHERE SR.DATE_OUTGO <= ORR.DATE_OUTGO
ORDER BY SR.DATE_OUTGO DESC) AS X
The first CTE calculates the order running total, whereas the second CTE calculates the stock running total. The query uses OUTER APPLY to get the stock running total up to the date the current order has been made.
Edit:
If you want to consume the stock of dates that come in the future with respect to the order date, then simply replace:
WHERE SR.DATE_OUTGO <= ORR.DATE_OUTGO
with
WHERE STOCK_RUNTOTAL <= ORDER_RUNTOTAL
in the OUTER APPLY operation.
Edit 2:
The following improved query should, at last, solve the problem:
;WITH ORDER_RUN AS (
SELECT SUM(SUM(QUANTITY)) OVER (ORDER BY DATE_OUTGO) AS ORDER_RUNTOTAL,
DATE_OUTGO
FROM #orderTable
GROUP BY DATE_OUTGO
), STOCK_RUN AS (
SELECT SUM(SUM(QUANTITY)) OVER (ORDER BY DATE_OUTGO) AS STOCK_RUNTOTAL,
SUM(SUM(QUANTITY)) OVER () AS TOTAL_STOCK,
DATE_OUTGO
FROM #stockTable
GROUP BY DATE_OUTGO
)
SELECT ORR.DATE_OUTGO AS OT_DATE_OUTGO,
CASE
WHEN X.STOCK_RUNTOTAL - ORDER_RUNTOTAL >= 0 THEN 0
ELSE X.STOCK_RUNTOTAL - ORDER_RUNTOTAL
END AS DIFF
FROM ORDER_RUN AS ORR
OUTER APPLY (
SELECT TOP 1 STOCK_RUNTOTAL
FROM STOCK_RUN AS SR
WHERE STOCK_RUNTOTAL >= ORDER_RUNTOTAL -- Stop if stock quantity has exceeded order quantity
OR
STOCK_RUNTOTAL = TOTAL_STOCK -- Stop if the end of stock has been reached
ORDER BY SR.DATE_OUTGO) AS X

Accessing prior rows and divide its value by current row

I have the rows below, and i want to access prior row and divide its value by current row. For every row, i need to calculate the Vi value, this Vi value is equal to Vi-1/Vi which means that:
Given the table
Table T
id value out
1 100
2 200
3 10
4 50
I want to generate these values
V1 = 100
V2= 100/200 = 0.5
V3 = 0.5/10 = 0.05
V4 = 0.05/50 = 0.001
So at the end i want the following output:
id value out
1 100 100
2 200 0.5
3 10 0.05
4 50 0.001
I tried using the aggregate function SUM with OVER(), but i do not know how to solve this problem as i need to divide and not sum the value
SELECT id, value, SUM(value) OVER(ORDER BY id ROWS BETWEEN
1 PRECEDING AND 1 PRECEDING ) / value as out
FROM T
Sample data:
CREATE TABLE t(
id INT,
value INT
);
INSERT INTO t VALUES
(1, 100), (2, 200), (3, 10), (4, 50);
Unfortunately, SQL do not have Product, but it should be simple to use cte. The performance should be not bad if id was indexed
DECLARE #T table (id int identity(1,1) primary key, value int)
INSERT #T VALUES (100), (200), (10), (50)
;WITH cte AS
(
SELECT id, value, CAST(value AS decimal(20,4)) AS out FROM #T WHERE id = 1
UNION ALL SELECT T.id, T.value, CAST(cte.out / T.value AS decimal(20,4)) FROM cte INNER JOIN #T T ON cte.id = T.id - 1
)
SELECT * FROM cte

Subtraction for two rows

I have following table named table1 in SQL Server.
id value
1 10
2 100
3 20
4 40
5 50
When i execute query following query it gives me result of 110 which is expected
SELECT SUM(value) from table1 where id in (1,2)
What i want is opposite of SUM means the output should be 90 or -90.
i know this can be done by writing following query
select ((SELECT value from table1 where id in (1)) - (SELECT value from table1 where id in (2)) )
but is there any simplified way to do this(something like SUM function).
Fiddle demo using Sum() with Case:
Declare #SubId int =1
--To get -90 or +90 change the value of #SubId from 1 to 2
Select Sum(Case When Id = #SubId Then value Else -1*Value End) Total
From Table1
Where Id in (1,2);
Depending on whether you want result to be non-negative or non-positive, you can switch MIN and MAX in the following statement:
select max(value) - min(value)
from table1
where id in (1,2)

Tsql group by clause with exceptions

I have a problem with a query.
This is the data (order by Timestamp):
Data
ID Value Timestamp
1 0 2001-1-1
2 0 2002-1-1
3 1 2003-1-1
4 1 2004-1-1
5 0 2005-1-1
6 2 2006-1-1
7 2 2007-1-1
8 2 2008-1-1
I need to extract distinct values and the first occurance of the date. The exception here is that I need to group them only if not interrupted with a new value in that timeframe.
So the data I need is:
ID Value Timestamp
1 0 2001-1-1
3 1 2003-1-1
5 0 2005-1-1
6 2 2006-1-1
I've made this work by a complicated query, but am sure there is an easier way to do it, just cant think of it. Could anyone help?
This is what I started with - probably could work with that. This is a query that should locate when a value is changed.
> SELECT * FROM Data d1 join Data d2 ON d1.Timestamp < d2.Timestamp and
> d1.Value <> d2.Value
It probably could be done with a good use of row_number clause but cant manage it.
Sample data:
declare #T table (ID int, Value int, Timestamp date)
insert into #T(ID, Value, Timestamp) values
(1, 0, '20010101'),
(2, 0, '20020101'),
(3, 1, '20030101'),
(4, 1, '20040101'),
(5, 0, '20050101'),
(6, 2, '20060101'),
(7, 2, '20070101'),
(8, 2, '20080101')
Query:
;With OrderedValues as (
select *,ROW_NUMBER() OVER (ORDER By TimeStamp) as rn --TODO - specific columns better than *
from #T
), Firsts as (
select
ov1.* --TODO - specific columns better than *
from
OrderedValues ov1
left join
OrderedValues ov2
on
ov1.Value = ov2.Value and
ov1.rn = ov2.rn + 1
where
ov2.ID is null
)
select * --TODO - specific columns better than *
from Firsts
I didn't rely on the ID values being sequential and without gaps. If that's the situation, you can omit OrderedValues (using the table and ID in place of OrderedValues and rn). The second query simply finds rows where there isn't an immediate preceding row with the same Value.
Result:
ID Value Timestamp rn
----------- ----------- ---------- --------------------
1 0 2001-01-01 1
3 1 2003-01-01 3
5 0 2005-01-01 5
6 2 2006-01-01 6
You can order by rn if you need the results in this specific order.

Resources