SQL Server - add/cumulative for last 3 days [closed] - sql-server

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Table 1
Amount
10
20
25
40
50
60
70
80
90
100
110
120
130
Write an sql query to get output as
07/11/2018 10
07/12/2018 20
07/13/2018 25 55
07/14/2018 40 85
07/15/2018 50 115
07/16/2018 60 150
07/17/2018 70 180
07/18/2018 80 210
07/19/2018 90 240
07/20/2018 100 270
07/21/2018 110 300
07/22/2018 120 330
07/23/2018 130 360
So I want to add the last 3 days amount values and get the sum.

The LAG window function is what you need. It allows you to access the value in a column from a previous row. The format is LAG([Column], [Row Offset], [Default]), so this example adds the value from the current row to the value 2 rows back and the value 1 row back.
DECLARE #t TABLE (dt DATE, c INT)
INSERT INTO #t VALUES
('07/11/2018',10),
('07/12/2018',20),
('07/13/2018',25),
('07/14/2018',40),
('07/15/2018',50),
('07/16/2018',60),
('07/17/2018',70),
('07/18/2018',80),
('07/19/2018',90),
('07/20/2018',100),
('07/21/2018',110),
('07/22/2018',120),
('07/23/2018',130)
SELECT dt, c,
c + LAG(c, 2) OVER (ORDER BY dt) + LAG(c, 1) OVER (ORDER BY dt)
FROM #t
ORDER BY dt
Returns:
dt c
2018-07-11 10 NULL
2018-07-12 20 NULL
2018-07-13 25 55
2018-07-14 40 85
2018-07-15 50 115
2018-07-16 60 150
2018-07-17 70 180
2018-07-18 80 210
2018-07-19 90 240
2018-07-20 100 270
2018-07-21 110 300
2018-07-22 120 330
2018-07-23 130 360

I'm not going to give you a full answer here, as you've not responded to my comments. Thus I'm going to give you a partial answer, so that you can work out how to do this yourself.
When working with SUM, you also have access to the OVER clause. In 2012+ (which I assume you're using, as 2008 is effectively out of support now, and anything before is completely unsupported) you have access to the ROWS BETWEEN clause in OVER.
For example:
WITH N AS (
SELECT *
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) V(N)),
Tally AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1
CROSS JOIN N N2)
SELECT I,
SUM(I) OVER (ORDER BY I
ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING) AS SomeSum
FROM Tally
ORDER BY I;
This example calculates the SUM of the current row, and the 3 following rows afterwards. So, for I = 1, that's SUM(1,2,3,4) = 10.
This can all be found in the documentation: SUM (Transact-SQL). SELECT - OVER Clause (Transact-SQL)

Related

How to get > 240 days data from today's date in SQL Server, where particular unit key of some records are within and outside 240 days

How to get > 240 days (or 8 month) data from today's date in SQL Server, where particular unit_key of some records are within and outside 240 days.So eliminate those records.Data available in this table from 2019 and searching records start from 01.01.2020 (allocation_date)
FL_DATA TABLE
fl_key
fl_no
allocation_date
unit_key
unit_name
1
FL/3352
02/20/2021
54
A Pradhan
5
FL/3374
07/14/2020
54
A Pradhan
8
FL/3469
08/16/2019
54
A Pradhan
11
FL/3578
06/22/2019
54
A Pradhan
15
FL/3670
06/15/2020
60
D Raj
22
FL/3692
04/22/2020
60
D Raj
9
FL/3542
07/20/2020
64
K Parihar
33
FL/3599
05/27/2020
64
K Parihar
46
FL/3645
11/13/2019
64
K Parihar
48
FL/3640
02/22/2021
22
R Raja
52
FL/3724
12/28/2020
22
R Raja
12
FL/3342
03/20/2020
22
R Raja
13
FL/3355
12/20/2019
22
R Raja
Output Table:
fl_no
unit_key
unit_name
FL/3670
60
D Raj
FL/3692
60
D Raj
FL/3542
64
K Parihar
FL/3599
64
K Parihar
My probable query is
SELECT fl_no,unit_key,unit_name
from FL_DATA
where DATEDIFF(day, allocation_date, getdate()) > 240
and allocation_date>='01/01/2020'
order by unit_key
But in where clause if I write >240 or <240, in that case data is showing only within or without the DAY boundary mentioned in the query. But here I am talking about some unit_key belongs within 240 and outside 240 days, that data should be eliminated by the query because I want only those unit_key who have not registered with us for more than 240 days. Rest others unit_key should be in query output.
In my table, data is available from the year 2018 and my requirement of data is from year 2020 to till date that’s why I have mentioned "allocation_date>='01/01/2020'”
I hope I got my point through to you and expecting the exact query format.
There you go:
SELECT T.fl_no,
T.unit_key,
T.unit_name,
DATEDIFF(DAY, T.allocation_date, GETDATE()) AS days_past
FROM FL_DATA AS T
WHERE T.allocation_date >= '2020-01-01'
AND NOT EXISTS
(
SELECT 1
FROM FL_DATA AS Recent
WHERE Recent.allocation_date >= DATEADD(DAY, -240, GETDATE())
AND Recent.unit_key = T.unit_key
)
Please let me know if it solves it, or if some adjustments are needed
In case you want to include the days in separate column, I suggest using OUTER APPLY instead:
SELECT T.fl_no,
T.unit_key,
T.unit_name,
unit_key_agg.days_past
FROM FL_DATA AS T
OUTER APPLY
(
SELECT DATEDIFF(
DAY,
MAX(Recent.allocation_date),
GETDATE()
) AS days_past
FROM FL_DATA AS Recent
WHERE Recent.unit_key = T.unit_key
) AS unit_key_agg
WHERE T.allocation_date >= '2020-01-01'
AND unit_key_agg.days_past > 240
This query would give you this result:
fl_no
unit_key
unit_name
days_past
FL/3670
60
D Raj
375
FL/3692
60
D Raj
375
FL/3542
64
K Parihar
340
FL/3599
64
K Parihar
340

Calculating the running total

I want to calculate the running total using a Stored Procedure. The base Table is ~10.000 rows and is as follows:
nWordNr nBitNr tmTotals
------------------------
5 14 86404
5 14 146
2 3 438
10 2 3319
5 12 225
2 3 58
.... .... .....
.... .... .....
I want this to be GROUPED BY nWordNr, NBitNr and have the total tmTotals. To do this is started of with the following:
SELECT TOP 10
[nWordNr] as W,
[nBitNr] as B,
SUM([tmTotals]) as total,
COUNT(*) as Amount
FROM Messages_History
GROUP BY nWordNr, nBitNr
ORDER BY total desc
This results in:
W B total Amount
-----------------------
2 3 3578775 745
3 3 3557975 395
5 4 2305229 72
5 3 2183050 33
5 12 2022401 825
5 14 1673295 652
48 12 1658862 302
4 3 1606454 215
48 13 1541729 192
5 9 1463256 761
Now I want to calculate the running total on the column total like this:
W B total Amount running
-------------------------------
2 3 3578775 745 3578775
3 3 3557975 395 7136750
5 4 2305229 72 9441979
5 3 2183050 33 11625029
5 12 2022401 825 etc.
5 14 1673295 652 etc.
48 12 1658862 302 etc.
4 3 1606454 215 etc.
48 13 1541729 192 etc.
5 9 1463256 761 etc.
so what I found was:
COUNT([tmTotals]) over (ORDER BY [nWordNr], [nBitNr]) as Running
But here I get the error that is discussed in this question: Column invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause and I just can't figure out how to solve it in this case
it should be SUM ( SUM ( tmTotals) ) OVER ( ... )
SELECT TOP 10
[nWordNr] as W,
[nBitNr] as B,
SUM([tmTotals]) as total,
COUNT(*) as Amount,
SUM(SUM([tmTotals])) OVER (ORDER BY [nWordNr], [nBitNr]) as Running
FROM Messages_History
GROUP BY nWordNr, nBitNr
ORDER BY total desc
EDIT :
Looking at your expected result, the Running should be
SUM(SUM([tmTotals])) OVER (ORDER BY SUM([tmTotals]) DESC) as Running
if the above is a bit difficult to grasp, then you can use a CTE or derived table and perform the running total on the outer query
; with CTE as
(
SELECT
[nWordNr] as W,
[nBitNr] as B,
SUM([tmTotals]) as total,
COUNT(*) as Amount
FROM Messages_History
GROUP BY nWordNr, nBitNr
)
SELECT TOP 10 *,
SUM(total) OVER (ORDER BY total desc) as Running
FROM CTE
ORDER BY total desc

Time Complexity of SQL Cursor

I am using CURSOR to implement the following in SQL Server, I am only iterating through the table - The time complexity will be O(n) I think (?). But everywhere I read about CURSOR, it says CURSOR is a bad practice. So is there a better way to implement the following ?
Existing Table
month value
1 92
4 20
9 92
New Table
month value
1 92
2 92
3 92
4 20
5 20
6 20
7 20
8 20
9 92
10 92
11 92
12 92
The use of cursor isn't (primarily) bad because it has poor time complexity, but because it is more error-prone and harder to read than a simple query. You are correct that iterating over a table via cursor is O(n).
On to your problem at hand. If you have the months (1..12) stored somewhere, say Months, then you can do it like this:
WITH matchingMonths AS (
SELECT m.month, MAX(mav.month) as matchedMonth
FROM Months m, MonthsAndValues mav
WHERE m.month >= mav.month
GROUP BY m.month
)
SELECT mm.month, mav.value
FROM matchingMonths mm
JOIN MonthsAndValues mav on mav.month = mm.matchedMonth
Without such a table Months, you could generate it on-the-fly:
WITH Months(month) AS (
SELECT 1
UNION ALL
SELECT month + 1 FROM Months WHERE month < 12
),
matchingMonths AS (
SELECT m.month, MAX(mav.month) as matchedMonth
FROM Months m, MonthsAndValues mav
WHERE m.month >= mav.month
GROUP BY m.month
)
SELECT mm.month, mav.value
FROM matchingMonths mm
JOIN MonthsAndValues mav on mav.month = mm.matchedMonth

T-SQL Count of Records in Status for Previous Months

I have a T-SQL Quotes table and need to be able to count how many quotes were in an open status during past months.
The dates I have to work with are an 'Add_Date' timestamp and an 'Update_Date' timestamp. Once a quote is put into a 'Won' or 'Loss' columns with a value of '1' in that column it can no longer be updated. Therefore, the 'Update_Date' effectively becomes the Closed_Status timestamp.
Here's a few example records:
Quote_No Add_Date Update_Date Open_Quote Win Loss
001 01-01-2016 NULL 1 0 0
002 01-01-2016 3-1-2016 0 1 0
003 01-01-2016 4-1-2016 0 0 1
Here's a link to all the data here:
https://drive.google.com/open?id=0B4xdnV0LFZI1T3IxQ2ZKRDhNd1k
I asked this question previously this year and have been using the following code:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
)
select format(dateadd(month, n.n, q.add_date), 'yyyy-MM') as yyyymm,
count(*) as Open_Quote_Count
from quotes q join
n
on (closed_status = 1 and dateadd(month, n.n, q.add_date) <= q.update_date) or
(closed_status = 0 and dateadd(month, n.n, q.add_date) <= getdate())
group by format(dateadd(month, n.n, q.add_date), 'yyyy-MM')
order by yyyymm;
The problem is this code is returning a cumulative value. So January was fine, but then Feb is really Jan + Feb, and March is Jan+Feb+March, etc. etc. It took me a while to discover this and the numbers returned now way, way off and I'm trying to correct them.
From the full data set the results of this code are:
Year-Month Open_Quote_Count
2017-01 153
2017-02 265
2017-03 375
2017-04 446
2017-05 496
2017-06 560
2017-07 609
The desired result would be how many quotes were in an open status during that particular month, not the cumulative :
Year-Month Open_Quote_Count
2017-01 153
2017-02 112
2017-03 110
2017-04 71
Thank you in advance for your help!
Unless I am missing something, LAG() would be a good fit here
Example
Declare #YourTable Table ([Year-Month] varchar(50),[Open_Quote_Count] int)
Insert Into #YourTable Values
('2017-01',153)
,('2017-02',265)
,('2017-03',375)
,('2017-04',446)
,('2017-05',496)
,('2017-06',560)
,('2017-07',609)
Select *
,NewValue = [Open_Quote_Count] - lag([Open_Quote_Count],1,0) over (Order by [Year-Month])
From #YourTable --<< Replace with your initial query
Returns
Year-Month Open_Quote_Count NewValue
2017-01 153 153
2017-02 265 112
2017-03 375 110
2017-04 446 71
2017-05 496 50
2017-06 560 64
2017-07 609 49

SQL- Getting maximum value along with all other columns?

I have a table, which can be seen as a evaluation of two courses in several classroom tests, like this:
student_ID Evaluation Course1 Course2
------------------------------------------------------
1 5 88 93
2 4 70 87
1 5 93 90
2 5 99 91
3 3 65 60
3 4 88 70
I need to get the result of the Evaluation=5 for each student, if any. If that student has more than one Evaluation=5, the query only show any one of them. So for the above example table, the query result will be
student_ID Evaluation Course1 Course2
------------------------------------------------------
1 5 88 93
2 5 99 91
Of course in my real table, the "Courses" number is more than 2.
Thanks for the help.
Since you only want to get only one record for every student_id, you can use ROW_NUMBER() which generates sequential number. The number generated will always starts with 1 which you can use to filter out row for every partition, in this case Student_ID.
SELECT Student_ID, Evaluation, Course1, Course2
FROM
(
SELECT Student_ID, Evaluation, Course1, Course2,
ROW_NUMBER() OVER (PARTITION BY Student_ID
ORDER BY Student_ID) rn
FROM TableName
WHERE Evaluation = 5
) a
WHERE a.rn = 1
SQLFiddle Demo

Resources