Finding 2nd to last date with SQL - sql

I'm trying to find the 2nd to last date using the below query to get all unique dates.
SELECT DISTINCT CAST(Entrydatetime AS DATE)
FROM [table_name]
This returns
2017-11-15
2017-11-16
2017-11-17
2017-11-20
The [table_name] contains many rows, many that are all on the same dates listed above. (200 records with '2017-11-20' and 180 with '2017-11-17' etc..)
So is there a way to just grab the 2nd to last item given from the above query?
If this were an array I could do something like '$array[-2]'
So in this instance it would pull 2017-11-17.
Any help would be appreciated

You can use ROW_NUMBER to rank your dates:
select dt
from
(
select distinct
cast(entrydatetime as date) as dt,
row_number() over (order by cast(entrydatetime as date) desc) as rn
from table_name
) ranked
where rn = 2;

If using SQL Server, you can use the following query:
with cte as
(
select DISTINCT Convert(Date,Entrydatetime) ,
ROW_NUMBER() over (order by Convert(Date,Entrydatetime) DESC) as rn
from [table name]
)
select * from cte where rn=2
Or you can do it another way
SELECT MAX(Convert(Date,Entrydatetime))
FROM [table name]
WHERE Convert(Date,Entrydatetime) < (SELECT MAX(Convert(Date,Entrydatetime) ) FROM [table name])

Here's an example using a CTE. I think the DISTINCT EntryDate has to be taken- cast to date to remove time- then the rownumber applied. Notice I am using two different times for 2017-11-20, 12:00:00 and 13:00:00. The ORDER BY cannot do this correctly if you have DISTINCT in the select... this requires a subquery.
DECLARE #tablename TABLE (Entrydatetime datetime)
INSERT INTO #tablename VALUES ('2017-11-15 12:00:00')
,('2017-11-16 12:00:00')
,('2017-11-17 12:00:00')
,('2017-11-20 12:00:00')
,('2017-11-20 13:00:00')
;
WITH cte AS
(
SELECT ROW_NUMBER() OVER(ORDER BY EntryDate DESC) [theOrder]
,dT.EntryDate
FROM (
SELECT DISTINCT CAST(Entrydatetime as date) AS EntryDate
FROM #tablename
) AS dT
)
SELECT cte.EntryDate FROM cte WHERE theOrder = 2
Gives output:
EntryDate
2017-11-17

Related

Perform pivot operation on the table but facing problem as it requires multiple aggregate function to segregate dates into two columns

I have this table named hotel which contains EmpId and their corresponding [check-in/out] column in datetime format.
Now, I want the data in table to look like this.
I have tried following simple query and it's working correctly:
select EmpId, min([Check-in/out]) as Checkin, max([Check-in/out]) as Checkout
from [dbo].[hotel]
group by EmpId;
But i want to do it using pivot operator in sql server. I have tried the following query but it's incorrect:
select EmpId, Min([Check-in/out]) AS Checkin, Max([Check-in/out]) AS Checkout
FROM
(select EmpId, [Check-in/out] from [dbo].[hotel]) AS SourceTable
PIVOT
(
min([Check-in/out])
FOR [Check-in/out] IN(Checkin)
)AS PivotTable1
PIVOT
(
max([Check-in/out])
FOR [Check-in/out] IN(Checkout)
)AS PivotTable2;
Here again my comment as answer with an example:
I suggest using cte / subqueries in order to approach this problem... first of all, select EmpID, time and rownumber (partition by empid and order by time) - this gives you all checkins with odd rownumber and all checkouts with even rownumber. Next - basing on this query - select empid, time and CASE WHEN rownumber%2=0 THEN 'CHECKOUT' ELSE 'CHECKIN' END AS CheckInOut... this result again can be used in your pivot statement
Example:
WITH cte AS(
SELECT *, ROW_NUMBER() OVER (PARTITION BY EmpID ORDER BY CheckInOut) rn
FROM T1
),
cteInOut AS(
SELECT EmpID, CheckInOut, CASE WHEN rn%2 = 0 THEN N'CheckOut' ELSE N'CheckIn' END AS CheckInOutState
FROM cte
),
cteInOuntSrt AS(
SELECT EmpID, CheckInOut, CheckInOutState, ROW_NUMBER() OVER (PARTITION BY EmpID, CheckInOutState ORDER BY CheckInOut) rn1
FROM cteInOut
)
select EmpID, rn1 AS CheckIndIdx, CheckIn, CheckOut
from cteInOuntSrt
pivot
(
min(CheckInOut)
for CheckInOutState in ([CheckIn], [CheckOut])
) piv
ORDER BY 1, 2;
fiddle: http://sqlfiddle.com/#!18/af6f3/1/1
If the values line up, you can use lead() and row_number():
select c.empid, checkin, checkout
from (select c.*, [Check-in/out] as checkin,
lead([Check-in/out]) over (partition by empid order by [Check-in/out]) as checkout,
row_number() over (partition by empid order by [Check-in/out]) as seqnum
from [Check-in/out] c
) c
where seqnum % 2 = 1;
This does the following calculations:
Calculate the next date/time value.
Enumerate the rows, starting the count from 1.
Taking the odd-numbered rows. These are the check ins.

SQL Earliest hour for every day

I have table like this
And I want to have only earliest time from column time for each day from column date. Rest of table has to be unaffected.
So result would be that for example that I have only time 9:25 for 2018-07-13 and rest of rows with later times for 2018-07-13 are deleted
To delete you can use a CTE with ROW_NUMBER window function
;WITH cteDups
AS(
SELECT *, RN=ROW_NUMBER()OVER (PARTITION BY M.Date ORDER BY M.Time ASC)
FROM dbo.yourtable M
)
--SELECT *
DELETE
FROM cteDups D WHERE D.RN > 1
You can use a window function to return all rows
select
*,
min([time]) over (partition by [date] order by [time])
from YourTable
Or just the aggregate to remove them
select *
from YourTable
inner join
(select whatever, min(FullDate) dt
from yourtable
group by whatever) x on x.whatever = YourTable.whatever and x.dt = YourTable.FullDate
If the whatever column doesn't matter, and you only want the date and time:
Select
[date],
min([time])
from YourTable
group by [Date]
The simplest way to keep certain records and remove the rest would be by using a CTE with a windowing function to rank (or add rownumbers). Check this out:
;WITH EarliestHourEveryDay AS (
SELECT
whatever
,FullDate
,[date]
,[time]
,rn = ROW_NUMBER() OVER (PARTITION BY [date] ORDER BY [time])
FROM TableName
)
SELECT *
FROM EarliestHourEveryDay
WHERE rn = 1
/*
DELETE FROM EarliestHourEveryDay
WHERE rn > 1
*/
I have commented out the delete statement so that you can test this first. Run the CTE as-is, and if the result set contains the exact rows which you want, remove the SELECT statement from the CTE and uncomment the DELETE statement and you'll be good to go.
Group by day and select the MIN time.
Use the MIN function and a GROUP BY clause.
Something like:
SELECT date, MIN(time) AS EarliestTime
FROM MyTable
GROUP BY date
ORDER BY date ASC
Here is an example of this working: SQL Fiddle

Filter the table with latest date having duplicate OrderId

I have following table:
I need to filter out the rows for which start date is latest corresponding to its order id .With reference to given table row no 2 and 3 should be the output.
As row 1 and row 2 has same order id and order date but start date is later than first row. And same goes with row number 3 and 4 hence I need to take out row no 3 . I am trying to write the query in SQL server. Any help is appreciated.Please let me know if you need more details.Apologies for poor English
You can do this easily with a ROW_NUMBER() windowed function:
;With Cte As
(
Select *,
Row_Number() Over (Partition By OrderId Order By StartDate Desc) RN
From YourTable
)
Select *
From Cte
Where RN = 1
But I question the StartDate datatype. It looks like these are being stored as VARCHAR. If that is the case, you need to CONVERT the value to a DATETIME:
;With Cte As
(
Select *,
Row_Number() Over (Partition By OrderId
Order By Convert(DateTime, StartDate) Desc) RN
From YourTable
)
Select *
From Cte
Where RN = 1
Another way using a derived table.
select
t.*
from
YourTable t
inner join
(select OrderId, max(StartDate) dt
from YourTable
group by OrderId) t2 on t2.dt = t.StartDate and t2.OrderId = t.OrderId

Minus the lower row from top row all the way down in sql

I have a query which is stated below which show the total km run of my vehicle. Now I want to make a subtraction. I want this like if I have a sample below:
date totkm
2015-27-01 81199
2015-27-01 81198
2015-27-01 81197
Now, what I want as a result is something like this:
date totkm
2015-27-01 0
2015-27-01 1
2015-27-01 1
As you can see I subtracted the first row and second row then subtracted the second row and the third row and so on and so forth.
Really would like to ask for some help.
Here's my query:
SELECT
ROW_NUMBER() OVER(ORDER BY CONVERT(char(10), TRXDATE, 126) DESC) AS Row,
(CONVERT(char(10), TRXDATE, 126)) AS NEWDATE,
kmrun AS totkm
FROM [SMSGPS].[dbo].tblCUrrent_ROI
WHERE
TRXDATE BETWEEN '2015-01-26 06:00:00' AND '2015-01-26 08:30:00'
AND PLATENO = 'NILO-MOTORCYCLE'
ORDER BY CONVERT(char(10), TRXDATE, 126) DESC
You can use LAG function.
CREATE TABLE #temp(
[Date] DATE,
TotKM VARCHAR(50)
)
INSERT INTO #temp VALUES
('20150127', '81199'),
('20150127', '81198'),
('20150127', '81197');
SELECT
[Date],
TotKM = LAG(CAST(TotKM AS INT), 1, CAST(TotKM AS INT)) OVER(ORDER BY [Date], CAST(TotKM AS INT) DESC) - TotKM
FROM #temp
DROP TABLE #temp
Since you have default order of records in your table, I am not applying ORDER BY on Date column. Instead I use SELECT 0 to get the order for ROW_NUMBER.
I am writing query in SQL Server 2008, that supports in SQL Server 2012 too.
SAMPLE TABLE
CREATE TABLE #tblCUrrent_ROI([DATE] DATE,TOTKM VARCHAR(50))
INSERT INTO #tblCUrrent_ROI
SELECT '2015-01-27',81199
UNION ALL
SELECT '2015-01-27',81198
UNION ALL
SELECT '2015-01-27',81197
QUERY
;WITH CTE AS
(
-- Row number in default order
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0))RNO,*
FROM #tblCUrrent_ROI
)
SELECT C1.RNO,C1.[DATE],ISNULL(CAST(C2.TOTKM AS INT)-CAST(C1.TOTKM AS INT),0) KM
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO=C2.RNO+1
Click here to view result

Get Dates on multiple calls

I have a log table that records dates.
I want to find out the date of the Second and Third call.
I get the first call by using MIN(DateCreated)
I get the last date using MAX(DateCreated). This could also be the second call.
What is the best way to find out the date of the second and third call?
I would just get all 3 rows together in one query
SELECT TOP 3 *
FROM LogTable
ORDER BY LogDate ASC -- DESC if you need the last 3
If you do need them one by one, you can use:
WITH LogByDate AS
(
SELECT LogDate,
ROW_NUMBER() OVER (ORDER BY LogDate) AS 'RowNumber'
FROM LogTable
)
SELECT *
FROM LogByDate
WHERE RowNumber = 2; -- RowNumber=3 for the third line
Try using analytical functions:
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY CallId ORDER BY DateCreated) Corr
FROM dbo.YourTable
)
SELECT *
FROM CTE
WHERE Corr IN (2,3)