How to run a 3 month sum of rows in SQL Server - sql-server

I am trying to replicate this behavior in SQL Server 2014. I have total dollars being worked per month, but I want a new column that takes the dollars and sums it by the current and next 2 months and places the total in the current month row. How can I accomplish this with SQL server?
Date Dollars 3 MONTH SUM
1/1/2018 10 37
2/1/2018 12 32
3/1/2018 15 36
4/1/2018 5 36
5/1/2018 16 34
6/1/2018 15 23
7/1/2018 3 27
8/1/2018 5 40
9/1/2018 19 46
10/1/2018 16 27
11/1/2018 11 11
I tried:
SUM(dollars) OVER (ORDER BY date ROWS BETWEEN current row and 2 following) AS [3 month sum]
but the calculation wasn't accurate. Is this the proper way to do it and I am just not doing it correctly or is there a better method?

This seems to work, and look very simple to read
declare #Incomings table([Date] Datetime, Dollars int)
insert into #Incomings
values
('20180101', 10)
,('20180201', 12)
,('20180301', 15)
,('20180401', 5)
,('20180501', 16)
,('20180601', 15)
,('20180701', 3)
,('20180801', 5)
,('20180901', 19)
,('20181001', 16)
,('20181101', 11)
select * , (select SUM(Dollars) from #Incomings where [Date] between i.[Date] and DATEADD(MONTH,2,i.[Date]))
from #Incomings i

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

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

How do I round up a date?

I have a table that contains the contract information for our customers. I need to round up the contract maturity date to a certain day of the month depending on the contract date itself. For example, if the contract date is 01-05-2016 I need to round it up to 01-10-2016. If the contract date is 01-11-2016 I need to round it up to 01-20-2016. And finally, if the contract date is 01-21-2016 I need to round it up to 01-30-2016. These round up dates match our billing cycles and I need all of our contracts to fall within one of these billing cycles. All dates are a DATETIME data type. Any help would be appreciated.
Maybe something like this?
DECLARE #testData TABLE(TestDate DATE);
INSERT INTO #testData VALUES({d'2016-02-05'}),({d'2016-02-12'}),({d'2016-02-21'});
SELECT TestDate
,CASE WHEN DAY(TestDate) BEtWEEN 1 AND 10 THEN 1
WHEN DAY(TestDate) BEtWEEN 11 AND 20 THEN 2
ELSE 3 END AS BillingCycle
,CASE WHEN DAY(TestDate) BEtWEEN 1 AND 10 THEN CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0')+'01' AS DATE)
WHEN DAY(TestDate) BEtWEEN 11 AND 20 THEN CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0') +'11' AS DATE)
ELSE CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0')+'28' AS DATE) END AS BillingCycleDate
FROM #testData
The result:
TestDate BillingCycle BillingCycleDate
2016-02-05 1 2016-02-01
2016-02-12 2 2016-02-11
2016-02-21 3 2016-02-28
You would do much better to avoid casting dates from strings. The long way is something like this though I'm sure it could be shortened and obfuscated. You did not specify what to do with dates after the 28th though:
dateadd(
day,
case day(contract_dt)
when 1 then 9
when 2 then 8
...
when 10 then 0
when 11 then 9
when 12 then 8
...
when 20 then 0
when 21 then 7
when 22 then 6
...
when 28 then 0
when 29 then -1
when 30 then -2
when 31 then -3
end,
contract_dt
)
Here is one of the more compact forms I alluded to:
dateadd(day, case
when day(contract_dt) <= 20
then 10 - day(contract_dt) % 10
else 28 - day(contract_dt)
end, contract_dt)
EDIT: I presume based on your acceptance of the other answer that you want to fall back to the 28th for the outliers and so I edited the above accordingly.

Pivot by count in postgres or sql-server

I have 2 sample tables, i need months group on rows and columns
rows group by #tnx table and columns month group by #act
and values filled with count(tnx_no)
create table #tnx(tnx_id int,tnx_date int,tnx_no)
insert into #tnx values(1,200140101,1),(1,200140101,2),(2,20130901,2),(2,20130802,2)
create table #act(act_id int,act_dt int)
insert into #act values(1,20140101),(1,20130809),(2,20140101),(2,20140203)
--i need this output sample output.
/*
-- the values are count(tnx_no)
0-6 monthns 0 1 2 3
months from #tnx 7-12 months 12 34 32 54
13-18 months 12 09 89 07
18+ months 11 100 190 190
0-6 months 7-12 months 13-18 months >18months
--months from #act
/*
The client app is in postgres,and sql server appreciate any help

Add dynamic column in select query - MS SQL

Im using Ms SQL database,
My table looks,
SrNo Emp_ID Date Time
------------------------------------
1 25 03-Sep-12 9:35:35 AM
2 25 03-Sep-12 10:31:32 AM
3 25 03-Sep-12 10:34:13 AM
4 25 03-Sep-12 11:05:08 AM
5 25 03-Sep-12 11:08:39 AM
6 25 04-Sep-12 09.05.40 AM
The expected output is
SrNo Emp_ID Date Time Type
---------------------------------------------
1 25 03-Sep-12 9:35:35 AM IN
2 25 03-Sep-12 10:31:32 AM OUT
3 25 03-Sep-12 10:34:13 AM IN
4 25 03-Sep-12 11:05:08 AM OUT
5 25 03-Sep-12 11:08:39 AM IN
6 25 04-Sep-12 09.05.40 AM IN
For an employee, type "in" and "out" has to be added simultaneously for particular date. For the next date or next /same employee, type has to start with "IN". Can any 1 help me in writing the sql query.
You can achieve this using an additional column
ROW_NUMBER() OVER (PARTITION BY Emp_ID, Date ORDER BY Time) AS RN
then checking modulus 2 on it, if remainder is 1, it's an IN, if it's 0 it's an OUT, based on your requirements.
More specifically
CASE WHEN
ROW_NUMBER() OVER (PARTITION BY Emp_ID, Date ORDER BY Time) % 2 = 1
THEN 'IN'
ELSE 'OUT'
END AS [Type]

Resources