query issue - grabbing dates 2 years > current date - sql-server

I have two tables in a database: 'Events' and 'Days'
Events table has these columns:
ID | Parent_ID
---------------
303| 4
304| -1
305| 3
306| -1
Days table has these columns:
Event_ID | Day
------------------------------------
303 | 2010-06-24 00:00:00.000
304 | 2013-08-21 00:00:00.000
305 | 2011-09-23 00:00:00.000
306 | 2011-12-04 00:00:00.000
303 | 2012-12-24 00:00:00.000
304 | 2010-11-06 00:00:00.000
305 | 2012-03-14 00:00:00.000
306 | 2011-06-12 00:00:00.000
305 | 2010-03-19 00:00:00.000
304 | 2009-01-20 00:00:00.000
What I'm trying to do is, in the Events table, find each ID who has a Parent_ID of -1, then find the same ID listed under Event_ID in the Days table, and return the last date entered IF it is greater than two years from the current date.
So for example, the query should grab 304 and 306 ID from the Events table since they both have -1 for the Parent_ID, and should return:
2013-08-21 00:00:00.000
and
2011-12-04 00:00:00.000
since they are the last entries for the two ID's that are greater than two years from the current date.
Not really sure how to do the greater than two years part. What I have tried is:
select max(day) as day
from Events e join Days d on d.Event_ID = e.ID
where e.Parent_ID = -1
this returns just the latest entry out of all the Event_IDs.

Try the following using GROUP BY HAVING and DATEADD
SQL Fiddle Demo
SELECT
Event_ID,
MAX(Day)
FROM Days
WHERE Event_ID IN (SELECT ID FROM Events WHERE Parent_ID = -1)
GROUP BY Event_ID
HAVING MAX(Day) < DATEADD(dd, -730, getdate())

I guess you are looking for the dates that are 2 years older than current date (if not let me know so that I will update the answer)
where e.Parent_ID = -1 and d.Day < dateadd(day,-730,getdate())

Related

Recursive first day of each month for current getdate

Using T-SQL, I want a new column that will show me the first day of each month, for the current year of getdate().
After that I need to count the rows on this specific date. Should I do it with CTE or a temp table?
If 2012+, you can use DateFromParts()
To Get a List of Dates
Select D = DateFromParts(Year(GetDate()),N,1)
From (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) N(N)
Returns
D
2017-01-01
2017-02-01
2017-03-01
2017-04-01
2017-05-01
2017-06-01
2017-07-01
2017-08-01
2017-09-01
2017-10-01
2017-11-01
2017-12-01
Edit For Trans Count
To get Transactions (assuming by month). It becomes a small matter of a left join to created Dates
-- This is Just a Sample Table Variable for Demonstration.
-- Remove this and Use your actual Transaction Table
--------------------------------------------------------------
Declare #Transactions table (TransDate date,MoreFields int)
Insert Into #Transactions values
('2017-02-18',6)
,('2017-02-19',9)
,('2017-03-05',5)
Select TransMonth = A.MthBeg
,TransCount = count(B.TransDate)
From (
Select MthBeg = DateFromParts(Year(GetDate()),N,1)
,MthEnd = EOMonth(DateFromParts(Year(GetDate()),N,1))
From (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) N(N)
) A
Left Join #Transactions B on TransDate between MthBeg and MthEnd
Group By A.MthBeg
Returns
TransMonth TransCount
2017-01-01 0
2017-02-01 2
2017-03-01 1
2017-04-01 0
2017-05-01 0
2017-06-01 0
2017-07-01 0
2017-08-01 0
2017-09-01 0
2017-10-01 0
2017-11-01 0
2017-12-01 0
For an adhoc table of months for a given year:
declare #year date = dateadd(year,datediff(year,0,getdate() ),0)
;with Months as (
select
MonthStart=dateadd(month,n,#year)
from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11)) t(n)
)
select MonthStart
from Months
rextester demo: http://rextester.com/POKPM51023
returns:
+------------+
| MonthStart |
+------------+
| 2017-01-01 |
| 2017-02-01 |
| 2017-03-01 |
| 2017-04-01 |
| 2017-05-01 |
| 2017-06-01 |
| 2017-07-01 |
| 2017-08-01 |
| 2017-09-01 |
| 2017-10-01 |
| 2017-11-01 |
| 2017-12-01 |
+------------+
The first part: dateadd(year,datediff(year,0,getdate() ),0) adds the number of years since 1900-01-01 to the date 1900-01-01. So it will return the first date of the year. You can also swap year for other levels of truncation: year, quarter, month, day, hour, minute, second, et cetera.
The second part uses a common table expression and the table value constructor (values (...),(...)) to source numbers 0-11, which are added as months to the start of the year.
Not sure why you require recursive... But for first day of month you can try query like below:
Select Dateadd(day,1,eomonth(Dateadd(month, -1,getdate())))
declare #year date = dateadd(year,datediff(year,0,getdate() ),0)
;WITH months(MonthNumber) AS
(
SELECT 0
UNION ALL
SELECT MonthNumber+1
FROM months
WHERE MonthNumber < 11
)
select dateadd(month,MonthNumber,#year)
from months

Most efficient way to migrate from timestamp to from-to columns

Given a table Orders with columns:
id | revision | insertedAt
1 0 2016-01-01 00:00.000
1 1 2016-01-01 02:00.000
2 0 2016-01-01 02:00.000
Where the id, revision combination is unique.
How can I best migrate to this:
id | revision | applyFrom | applyTo
1 0 2016-01-01 00:00.000 2016-01-01 01:99.999
1 1 2016-01-01 02:00.000 9999-31-12 00:00.000
2 0 2016-01-01 02:00.000 9999-31-12 00:00.000
I've tried iterating over a CURSOR and updating as I go along.
UPDATE orders SET applyFrom = #newApplyFrom, applyTo = #newApplyTo
WHERE id = #id AND revision = #revision;
But with 226 million rows, estimated runtime is somewhere near 60 hours, even hitting the index.
Is there a faster way of achieving the same result? I can add indices as needed. Currently, there is a clustered index on (id, revision).
You can update like below: I am using lead and showing with select
;with cte as (
select *, lead(insertedAt,1,'9999-12-31 00:00.000') over(order by id) migdate from Orders
)
select *, case when insertedAt = migdate then '9999-12-31 00:00.000' else DATEADD(S, -1, migdate) end as applyto from cte
Here is a version including LEAD and a self join. Not sure about the performance on large data sets, but I've included batching just in case.
WITH cte AS (
SELECT
id,
revision,
insertedAt,
applyFrom,
applyTo,
LEAD(insertedAt) OVER (PARTITION BY id ORDER BY id, revision) AS newApplyTo
FROM orders
)
UPDATE TOP (#BatchSize) o SET
applyFrom = o.insertedAt,
applyTo = ISNULL(DATEADD(s, -1, o.newApplyTo), '9999-12-31')
FROM cte o
WHERE
o.applyFrom IS NULL AND
o.applyTo IS NULL;
The dataset I've used (with results) is:
Id revision insertedAt applyFrom applyTo
----------- ----------- --------------------------- --------------------------- ---------------------------
1 0 2016-01-01 00:00:00.0000000 2016-01-01 00:00:00.0000000 2016-01-01 01:59:59.0000000
1 1 2016-01-01 02:00:00.0000000 2016-01-01 02:00:00.0000000 9999-12-31 00:00:00.0000000
2 0 2016-01-01 02:00:00.0000000 2016-01-01 02:00:00.0000000 9999-12-31 00:00:00.0000000
3 0 2016-01-01 00:00:00.0000000 2016-01-01 00:00:00.0000000 2016-10-31 23:59:59.0000000
3 1 2016-11-01 00:00:00.0000000 2016-11-01 00:00:00.0000000 2016-11-30 23:59:59.0000000
3 2 2016-12-01 00:00:00.0000000 2016-12-01 00:00:00.0000000 9999-12-31 00:00:00.0000000

DateDiff of Logtable Dates Which Have The Same Column in SQL Server

Using SQL Server 2012 I need to get the datediff of all dates in a Log table which has the same column, for example:
ID | Version | Status | Date
-----------------------------------------------------
12345 | 1 | new | 2014-05-01 00:00:00.000
12345 | 2 | up | 2014-05-02 00:00:00.000
12345 | 3 | appr | 2014-05-03 00:00:00.000
67890 | 1 | new | 2014-05-04 00:00:00.000
67890 | 2 | up | 2014-05-08 00:00:00.000
67890 | 3 | rej | 2014-05-13 00:00:00.000
I need to get the date diff of all sequential dates (date between 1, 2 and date between 2, 3)
I have tried creating a while but with no luck!
Your help is really appreciated!
This Calculates DateDiff as per your query "date diff of all sequential dates",if not sequential,it will just show same date.Further please don't use Reserved Keywords as Column names
SELECT ID,
[VERSION],
[STATUS],
[DATE],
CASE WHEN LEAD([DATE]) OVER (PARTITION BY ID ORDER BY [VERSION])=DATEADD(DAY,1,[DATE])
THEN CAST(DATEDIFF(DAY,[DATE],LEAD([DATE]) OVER (PARTITION BY ID ORDER BY VERSION)) AS VARCHAR(5))
ELSE [DATE] END AS DATEDIFFF
FROM
#TEMP
Another way with OUTER APPLY (get the previous value) :
SELECT t.*,
DATEDIFF(day,p.[Date],t.[Date]) as dd
FROM YourTable t
OUTER APPLY (
SELECT TOP 1 *
FROM YourTable
WHERE ID = t.ID AND [DATE] < t.[Date] AND [Version] < t.[Version]
ORDER BY [Date] DESC
) as p
Output:
ID Version Status Date dd
12345 1 new 2014-05-01 00:00:00.000 NULL
12345 2 up 2014-05-02 00:00:00.000 1
12345 3 appr 2014-05-03 00:00:00.000 1
67890 1 new 2014-05-04 00:00:00.000 NULL
67890 2 up 2014-05-08 00:00:00.000 4
67890 3 rej 2014-05-13 00:00:00.000 5
Note: If you are using SQL Server 2012 then better use LEAD and LAG functions.

Cleaning up old record to a specific date: How to select the old record?

I posted a question here, which I now need to perform. I edited it a few times to match the current requirement, and now I think i will make it clearer as a final solution for me as well.
My table:
Items | Price | UpdateAt
1 | 2000 | 02/02/2015
2 | 4000 | 06/04/2015
1 | 2500 | 05/25/2015
3 | 2150 | 07/05/2015
4 | 1800 | 07/05/2015
5 | 5540 | 08/16/2015
4 | 1700 | 12/24/2015
5 | 5200 | 12/26/2015
2 | 3900 | 01/01/2016
4 | 2000 | 06/14/2016
As you can see, this is a table that keeps items' price as well as their old price before the last update.
Now I need to find the rows which :
UpdateAt is more than 1 year ago from now
Must have updated price at least once ever since
Aren't the most up-to-date price
Why those conditions? Because I need to perform a cleanup on that table off of those records that older than 1 year, while still maintain the full item list.
So with those conditions, the result from the above table should be :
Items | Price | UpdateAt
1 | 2000 | 02/02/2015
2 | 4000 | 06/04/2015
4 | 1800 | 07/05/2015
The update at 02/02/2015 of item 1 should be selected, while the update no. 2 at 05/25/2015, though still over 1 year old, should not because it is the most up-to-date price for item 1.
Item 3 isn't in the list because it never been updated, hence its price remain the same until now so i don't need to clean it up.
At first i think it wouldn't be so hard, and i think I've already had an answer but as I proceed, it isn't something that easy anymore.
#Tim Biegeleisen provided me with an answer in the last question, but it doesn't select the items which price doesn't change over the year at all, which i'm having to deal with now.
I need a solution to effectively clean up the table - it isn't necessary to follow 3 conditions above if it can produce the same result as I need : Records that needs to be deleted.
try this,
DECLARE #Prices TABLE(Items INT, Price DECIMAL(10,2), UpdateAt DATETIME)
INSERT INTO #Prices
VALUES
(1, 2000, '02/02/2015')
,(2, 4000, '06/04/2015')
,(1, 2500, '05/25/2015')
,(3, 2150, '07/05/2015')
,(4, 1800, '07/05/2015')
,(5, 5540, '08/16/2015')
,(4, 1700, '12/24/2015')
,(5, 5200, '12/26/2015')
,(2, 3900, '01/01/2016')
,(4, 2000, '06/14/2016')
SELECT p.Items, p.Price, p.UpdateAt
FROM #Prices p
LEFT JOIN ( SELECT
p1.Items,
p1.UpdateAt,
ROW_NUMBER() OVER (PARTITION BY p1.Items ORDER BY p1.UpdateAt DESC) AS RowNo
FROM #Prices p1
) AS hp ON hp.Items = p.Items
AND hp.UpdateAt = p.UpdateAt
WHERE hp.RowNo > 1 -- spare one price for each item at any date
AND p.UpdateAt < DATEADD(YEAR, -1, GETDATE()) -- remove only prices older than a year
the result is:
Items Price UpdateAt
----------- --------------------------------------- -----------------------
1 2000.00 2015-02-02 00:00:00.000
2 4000.00 2015-06-04 00:00:00.000
4 1800.00 2015-07-05 00:00:00.000
This query will return the dataset you're looking for:
SELECT t1.Items, t1.Price, t1.UpdateAt
FROM
(
SELECT
t2.Items,
t2.Price,
t2.UpdateAt,
ROW_NUMBER() OVER (PARTITION BY t2.Items ORDER BY t2.UpdateAt DESC) AS rn
FROM [Table] AS t2
) AS t1
WHERE t1.rn > 1
AND t1.UpdateAt < DATEADD(year, -1, GETDATE())

How to remove the duplicate records in select query over clause

I am having Transactions table as follows in SQL SERVER.
UserID TranDate Amount
1 | 2015-04-01 | 0
1 | 2015-05-02 | 5000
1 | 2015-09-07 | 1000
1 | 2015-10-01 | -4000
1 | 2015-10-02 | -700
1 | 2015-10-03 | 252
1 | 2015-10-03 | 260
1 | 2015-10-04 | 1545
1 | 2015-10-05 | 1445
1 | 2015-10-06 | -2000
I want to query this table to get available balance at any particular date. So I used Windowing function for that.
SELECT TransactionDate,
SUM(Amount) OVER (PARTITION BY UserId ORDER BY TransactionDate ROWS
BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM Transactions
But as transactions table is having duplicate entry for date 2015-10-03 it is repeating data for date 2015-10-03. Whenever there is same date I am expecting the last record of that date with available balance summed up.
Current output
TransactionDate AvailableBalance
2015-04-01 | 0
2015-05-02 | 5000
2015-09-07 | 6000
2015-10-01 | 2000
2015-10-02 | 1300
2015-10-03 | 1552
2015-10-03 | 1804
2015-10-04 | 3349
2015-10-05 | 4794
2015-10-06 | 2794
Expected: I want to remove below record from the above result set.
2015-10-03 | 1552
HERE is my sql fiddle
You can SUM before windowed function like:
SqlFiddleDemo
WITH cte AS
(
SELECT TransactionDate, UserId, SUM(Amount) AS Amount
FROM Transactions
GROUP BY TransactionDate, UserId
)
SELECT TransactionDate,
SUM(Amount) OVER (PARTITION BY UserId ORDER BY TransactionDate ROWS
BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS AvailableBalance
FROM cte
Use RANGE instead of ROWS.
SQL Fiddle
SELECT
TransactionDate,
SUM(Amount) OVER (
PARTITION BY UserId
ORDER BY TransactionDate
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS AvailableBalance
FROM Transactions;
This variant produces a different result set than originally requested, but it may be useful in some cases. This variant returns same number of rows as in Transactions table. So, it will return two rows with 2015-10-03, but for both rows AvailableBalance would be 1804.
I just wanted to highlight that there is that option RANGE. If you really need one row per day, then grouping by day at first as in the answer by #lad2025 is the way to go.

Resources