How to pick last inserted DateTime from each month? - sql-server

I have a table PriceDate with two columns PriceId and PriceDate with 3-4 entries per month.
I want to retrieve last inserted PriceDate for each month.
This is my table
PriceDate PriceId
2012-01-07 00:00:00.000 1
2012-01-14 00:00:00.000 2
2012-01-21 00:00:00.000 3
2012-01-28 00:00:00.000 4
2012-02-04 00:00:00.000 5
2012-02-11 00:00:00.000 6
2012-02-18 00:00:00.000 7
2012-02-25 00:00:00.000 8
I need this output
PriceDate DateFormat PriceId
2012-01-28 00:00:00.000 Jan 2012 4
2012-02-25 00:00:00.000 Feb 2012 8

This seems to do the trick:
declare #t table (PriceDate datetime not null, PriceId int not null)
insert into #t(PriceDate,PriceId) values
('2012-01-07T00:00:00.000',1),
('2012-01-14T00:00:00.000',2),
('2012-01-21T00:00:00.000',3),
('2012-01-28T00:00:00.000',4),
('2012-02-04T00:00:00.000',5),
('2012-02-11T00:00:00.000',6),
('2012-02-18T00:00:00.000',7),
('2012-02-25T00:00:00.000',8)
;With Numbered as (
select *,
ROW_NUMBER() OVER (
PARTITION BY DATEADD(month,DATEDIFF(month,0,PriceDate),0)
ORDER BY PriceDate desc) as rn
from #t
)
select PriceDate,
RIGHT(CONVERT(varchar(20),PriceDate,106),8) [Dateformat],
PriceId
from Numbered where rn=1
The DATEADD/DATEDIFF trick is to basically round every date to the start of its respective month.
Result:
PriceDate Dateformat PriceId
----------------------- -------- -----------
2012-01-28 00:00:00.000 Jan 2012 4
2012-02-25 00:00:00.000 Feb 2012 8

Similar to #Damian_The_Unbeliever's but using the YEAR() and Month() functions
;WITH DateOrdered
AS
(
SELECT PriceDate, PriceId,
ROW_NUMBER() OVER (
PARTITION BY YEAR(PriceDate), MONTH(PriceDate)
ORDER BY PriceDate DESC) As Num
from PriceDate
)
SELECT PriceDate, RIGHT(CONVERT(varchar(20),PriceDate,106),8) [Dateformat], PriceId
FROM DateOrdered
WHERE Num = 1

Related

SQL Server - Split a date range by a given date

I have a table that stores an Id and an effective period indicating when it is active
PortfolioId StartDate EndDate
1 2018-01-01 00:00:00.000 2018-05-31 00:00:00.000
2 2017-01-01 00:00:00.000 2018-05-31 00:00:00.000
I have another table that stores a component related to the above Id and that too has an effective period. Table 2 can have more that on entry for any given entry in table 1.
PortfolioComponentId PortfolioId SplitDate
1 1 2018-02-28 00:00:00.000
2 1 2018-03-31 00:00:00.000
3 2 2017-03-31 00:00:00.000
4 2 2017-09-20 00:00:00.000
5 2 2018-01-15 00:00:00.000
I have a period where I am running the query for
i.e
StartDate : 30-JUN-2017
End date : 15-MAY-2018
I have looking for a result like the below, where data in table 1 is split based on the data from table 2
PortfolioId StartDate EndDate
1 2018-01-01 00:00:00.000 2018-02-28 00:00:00.000
1 2018-03-01 00:00:00.000 2018-03-31 00:00:00.000 - Starts from End date + 1 from the prev row
1 2018-04-01 00:00:00.000 2018-05-15 00:00:00.000
2 2017-06-30 00:00:00.000 2017-09-20 00:00:00.000 - Starts from Seach date [Portfolio component Id 3 ignored as it falls outside of search date range]
2 2017-09-21 00:00:00.000 2018-01-15 00:00:00.000
2 2018-01-16 00:00:00.000 2018-05-15 00:00:00.000 - Ends by seach end date
Data setup - In case it helps
DECLARE #SearchStartDate DATETIME = '30-JUN-2017'
DECLARE #SearchEndDate DATETIME = '15-MAY-2018'
DECLARE #Portfolio TABLE
(
PortfolioId INT PRIMARY KEY IDENTITY(1,1),
StartDate DATETIME,
EndDate DATETIME
)
INSERT INTO #Portfolio
SELECT '01-JAN-2018', '31-MAY-2018'
INSERT INTO #Portfolio
SELECT '01-JAN-2017', '31-MAY-2018'
DECLARE #PortfolioComponents TABLE
(
PortfolioComponentId INT PRIMARY KEY IDENTITY(1,1),
PortfolioId INT,
SplitDate DATETIME
)
INSERT INTO #PortfolioComponents
SELECT 1, '28-FEB-2018'
INSERT INTO #PortfolioComponents
SELECT 1, '31-MAR-2018'
INSERT INTO #PortfolioComponents
SELECT 2, '31-MAR-2017'
INSERT INTO #PortfolioComponents
SELECT 2, '20-SEP-2017'
INSERT INTO #PortfolioComponents
SELECT 2, '15-JAN-2018'
SELECT * from #Portfolio
SELECT * from #PortfolioComponents
I believe I have got a result close to what I want, though not
the most ideal approach
the best approach from a performance perspective
DECLARE #Temp TABLE (BenchmarkDate Datetime, ComponentType int, PortfolioId INT)
INSERT INTO #Temp
SELECT StartDate , 1, PortfolioId FROM #Portfolio
UNION
SELECT EndDate , 2, PortfolioId FROM #Portfolio
INSERT INTO #Temp
SELECT SplitDate , 2, PortfolioId FROM #PortfolioComponents
UNION
SELECT SplitDate + 1 , 1, PortfolioId FROM #PortfolioComponents
DECLARE #Results TABLE
(
Id INT IDENTITY(1,1),
StartDate DATETIME,
EndDate DATETIME,
PortfolioId INT
)
INSERT INTO #Results
SELECT rset1.BenchmarkDate [Startdate],
( SELECT MIN(rset2.BenchmarkDate)
FROM #Temp rset2
WHERE rset2.ComponentType = 2
AND rset2.BenchmarkDate > rset1.BenchmarkDate
AND rset1.PortfolioId = rset2.PortfolioId) [Enddate],
rset1.PortfolioId
FROM #Temp rset1
WHERE rset1.ComponentType = 1
ORDER BY rset1.PortfolioId, rset1.BenchmarkDate
SELECT
CASE WHEN (#SearchStartDate BETWEEN StartDate AND EndDate ) THEN #SearchStartDate ELSE StartDate END StartDate,
CASE WHEN (#SearchEndDate BETWEEN StartDate AND EndDate) THEN #SearchEndDate ELSE EndDate END EndDate,
PortfolioId ,
(
SELECT PortfolioComponentId
FROM #PortfolioComponents pc
WHERE
(pc.PortfolioID = r.PortfolioId AND
(DATEADD(d, 1, pc.SplitDate) = r.StartDate ))
) PortfolioComponentId
FROM #Results r
WHERE
(#SearchStartDate < StartDate AND #SearchStartDate < EndDate AND #SearchEndDate > EndDate)
OR
(#SearchEndDate BETWEEN StartDate AND EndDate)
OR
(#SearchStartDate BETWEEN StartDate AND EndDate )

SQL Server TSQL: Put Subsequent (date) rows into bins

i have following table:
id insertDate
1 2015-01-01
22 2015-01-02
43 2015-01-03
46 2015-01-06
124 2015-01-07
In the end i want to have a possiblity to group the rows that differ exaclty in one day so in that case
id insertDate groups
1 2015-01-01 1
22 2015-01-02 1
43 2015-01-03 1
46 2015-01-06 2
124 2015-01-07 2
How is this possible? I bet it is some fancy window function usage
This is a "gaps and islands" problem.
One approach is below. This works on SQL Server 2005+. I have assumed that insertDate is unique as per your example data.
WITH CTE
AS (SELECT *,
DATEDIFF(DAY, 0, [insertDate]) - ROW_NUMBER() OVER (ORDER BY ID) AS Grp
FROM YourTable)
SELECT [id],
[insertDate],
DENSE_RANK() OVER (ORDER BY Grp) AS Grp
FROM CTE

Count number of items which hadn't been checked so far in the last x months

We have a list of items. Each item may be checked/examined one or more times in the last #FromDate to #ToDate (12 months, for example). How do we count the number of items which were checked in a month that had never been check since #FromDate and display that count monthly.
The result would look like below:
2014-05-01 00:00:00.000 1 May 2014 381
2014-06-01 00:00:00.000 2 June 2014 296
2014-07-01 00:00:00.000 3 July 2014 24
2014-08-01 00:00:00.000 4 August 2014 260
2014-09-01 00:00:00.000 5 September 2014 249
2014-10-01 00:00:00.000 6 October 2014 177
2014-11-01 00:00:00.000 7 November 2014 298
2014-12-01 00:00:00.000 8 December 2014 274
2015-01-01 00:00:00.000 9 January 2015 41
2015-02-01 00:00:00.000 10 February 2015 0
2015-03-01 00:00:00.000 11 March 2015 0
2015-04-01 00:00:00.000 12 April 2015 0
So far we could count the number of items which were checked monthly by using the following query:
;WITH d AS
(
SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, '2014-05-15'), 0)) as d, ROW_NUMBER() OVER (ORDER BY n) as rn
FROM ( SELECT TOP (DATEDIFF(MONTH, '2014-05-15', '2015-04-15') + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
SELECT
d.d,
d.rn,
DATENAME(MONTH, d.d) as [Month],
YEAR(d.d) as [Year],
COUNT(DISTINCT ItemNumber) AS Count
FROM d LEFT OUTER JOIN ItemCheck
ON CheckedTime >= d.d
AND CheckedTime < DATEADD(MONTH, 1, d.d)
GROUP BY d.d, d.rn
ORDER BY d.d;
However, we still can't figure out how to count the number of items which were checked say in July 2014 but had never been checked in May and June 2014.
A simple single query below would be able to display that for a particular month but that doesn't display in monthly.
select count(distinct ItemNumber) from ItemCheck
where CheckTime >= '2014-07-01' AND CheckTime < '2014-08-01' and
ItemNumber NOt in (SELECT ItemNumber FROM ItemCheck as t1 where t1.CheckTime >= '2014-05-01' AND t1.CheckTime < '2014-07-01')
order by ItemNumber
Update: The ItemCheck table looks like below:
CheckID | ItemNumber | CheckTime
1 i1 2014-05-02
2 i4 2014-06-12
3 i5 2014-07-03
4 i1 2014-08-01
5 i1 2014-08-02
6 i2 2014-09-15
7 i3 2014-10-11
Suppose you have a table called months with one column called month.
Then you could do it something along these lines:
DECLARE #date DATE
SET #date = '2014-01-01'
SELECT months.month AS currentMonth, COUNT(ItemNumber) ItemNumber from ItemCheck
CROSS JOIN months
WHERE CheckTime >= dateadd(m, months.month - 1, #date) AND CheckTime < dateadd(m, months.month, #date) and
ItemNumber NOT IN (SELECT ItemNumber FROM ItemCheck AS t1 WHERE t1.CheckTime >= DATEADD(m, months.month - 3, #date) AND t1.CheckTime < dateadd(m, months.month - 1, #date))
GROUP BY month
ORDER BY ItemNumber
You could also do it with a function, but you have to write it (function is called dbo.Split and you could write it this way: select data from dbo.Split(',', '1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12'). This way you could avoid creating another table (in-memory or a real one..)
If you want the code for dbo.Split I can provide it.

T sql order by 2 rows according to different dates

I have table called "customers"
customers table has below columns
CompanyName varchar(100)
ProductName varchar(100)
CreatedDate datetime
Salesprice money
I have below data in customer table
CompanyName (column) - ProductName (column) - CreatedDate (column) - Salesprice (column)
A B 2015-01-02 00:00:00.000 419,10
C D 2014-04-20 00:00:00.000 30,10
A B 2014-01-02 00:00:00.000 60,00
C D 2015-04-20 00:00:00.000 540,00
I want to select data as below. I need to order by CreatedDate from less than more like
CreatedDate 2014
CreatedDate 2015
CreatedDate 2014
CreatedDate 2015
Result must be as below (2014-2015 ordering)
A B 2014-01-02 00:00:00.000 60,00 (This is 2014)
A B 2015-01-02 00:00:00.000 419,10 (This is 2015)
C D 2014-04-20 00:00:00.000 30,10 (This is 2014)
C D 2015-04-20 00:00:00.000 540,00 (This is 2015)
Question:
How can i select customer table
First 2014 date than 2015 date if exists more then 2016 etc.. if CompanyName is same and ProductName is Same like Result in above side ?
What you need to do is add ORDER BY in your query.
SELECT * from customers
ORDER BY CompanyName, ProductName, CreatedDate;
UPDATED:
SELECT CompanyName, ProductName, CreatedDate, ROW_NUMBER() OVER (PARTITION BY YEAR(CreatedDate)
ORDER BY YEAR(CreatedDate)) as rownumber
FROM customers
ORDER by rownumber, YEAR(CreatedDate)

How do i display the end date from StartDate in sql server?

I am using SQL SERVER 2008. I have a table called Cut-Off table. This table only shows the StartDate of cut-off for every month. So i am creating a temp table that will store StartDate, EndDate, Month and Year. But i don't know how to get the EndDate with datetime.
TABLE
----------------------------------------------------------
StartDate EndDate Month Year
----------------------------------------------------------
2014-12-15 00:00:00 NULL 12 2014
2015-01-26 00:00:00 NULL 1 2015
2015-02-26 00:00:00 NULL 2 2015
---------------------------------------------------------
EXPECTED OUTPUT
--------------------------------------------------------------------
StartDate EndDate Month Year
------------------------------------------------------------------
2014-12-15 00:00:00 2015-01-25 23:59:99 12 2014
2015-01-26 00:00:00 2015-02-25 23:59:99 1 2015
2015-02-26 00:00:00 2015-03-25 23:59:99 2 2015
------------------------------------------------------------------
How i get the EndDate??
Was unable to get 99 seconds in '23:59:99'
Try this:
CREATE TABLE #temp(StartDate datetime, EndDate datetime, Month int, Year int)
INSERT #temp values
('2014-12-15 00:00:00', NULL, 12, 2014),
('2015-01-26 00:00:00', NULL, 1, 2015),
('2015-02-26 00:00:00', NULL, 2, 2015)
UPDATE t
SET EndDate = DateAdd(m, DateDiff(m, 0, StartDate), '1900-02-25T23:59:59')
FROM #temp t
SELECT * FROM #temp
Result:
StartDate EndDate Month Year
2014-12-15 00:00:00.000 2015-01-25 23:59:59.000 12 2014
2015-01-26 00:00:00.000 2015-02-25 23:59:59.000 1 2015
2015-02-26 00:00:00.000 2015-03-25 23:59:59.000 2 2015
Edit:
If you want to calculate the EndDate from next StartDate, here is your UPDATE statement:
UPDATE t
SET EndDate = (SELECT top 1 DateAdd(s, -1, StartDate) FROM #temp
WHERE t.StartDate < StartDate
ORDER BY startdate)
FROM #temp t
Result:
StartDate EndDate Month Year
2014-12-15 00:00:00.000 2015-01-25 23:59:59.000 12 2014
2015-01-26 00:00:00.000 2015-02-25 23:59:59.000 1 2015
2015-02-26 00:00:00.000 null 2 2015
SAMPLE TABLE
CREATE TABLE #TEMP(DATES DATETIME,DATES DATETIME,[MONTH] INT,[YEAR] INT)
INSERT INTO #TEMP
SELECT '2014-12-15 00:00:00', NULL, 12, 2014
UNION ALL
SELECT '2015-01-26 00:00:00', NULL, 1, 2015
UNION ALL
SELECT '2015-02-26 00:00:00', NULL, 2, 2015
QUERY
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER(ORDER BY STARTDATE)RNO, STARTDATE
FROM #TEMP
)
,CTE2 AS
(
SELECT C1.STARTDATE,
ISNULL(DATEADD(S,-1,C2.STARTDATE),DATEADD(S,-1,DATEADD(MONTH,1,C1.STARTDATE))) ENDDATE
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
)
UPDATE #TEMP
SET EndDate = CTE2.ENDDATE
FROM #TEMP T1
JOIN CTE2 ON T1.STARTDATE=CTE2.STARTDATE
RESULT
You can also do it with OUTER APPLY
select
c.StartDate
DATEADD(SECOND,-1,e.StartDate) EndDate
DATEPART(MONTH,StartDate) [Month],
DATEPART(YEAR,StartDate) [Year]
from [Cut-Off] c
outer apply (
select TOP 1
e.StartDate
from [Cut-Off] e
where
e.StartDate > c.StartDate
order by
e.StartDate
) e

Resources