Using DATEDIFF within a SELECT - sql

SELECT DISTINCT
Test,
FIRST_VALUE (CONVERT(DATE, datetime)) OVER (ORDER BY datetime DESC) StartDate,
LAST_VALUE (CONVERT(DATE, datetime)) OVER (ORDER BY datetime DESC) EndDate,
DATEDIFF(MONTH, StartDate, EndDate)
FROM
Tests
Any ideas on how I can include the difference between the 2 dates within the SELECT?
I want to be able to use the values StartDate and EndDate however I'm getting an error saying StartDate and EndDate are invalid columns.

You can't use aliases you created in the SELECT clause elsewhere in the same SELECT clause, so you need to place your FIRST_VALUE() and LAST_VALUE() functions within the DATEDIFF() function:
SELECT DISTINCT
Test,
FIRST_VALUE (CONVERT(DATE, datetime)) OVER (ORDER BY datetime DESC) StartDate,
LAST_VALUE (CONVERT(DATE, datetime)) OVER (ORDER BY datetime DESC) EndDate,
DATEDIFF(MONTH, FIRST_VALUE (CONVERT(DATE, datetime)) OVER (ORDER BY datetime DESC)
, LAST_VALUE (CONVERT(DATE, datetime)) OVER (ORDER BY datetime DESC) )
FROM
Tests
Or you could run everything but the DATEDIFF() in a subquery/cte:
SELECT *,DATEDIFF(MONTH, StartDate, EndDate)
FROM (SELECT DISTINCT
Test,
FIRST_VALUE (CONVERT(DATE, datetime)) OVER (ORDER BY datetime DESC) StartDate,
LAST_VALUE (CONVERT(DATE, datetime)) OVER (ORDER BY datetime DESC) EndDate
FROM
Tests
) AS sub
You also can't use aliases created in the SELECT clause in the WHERE clause, but you can use them in ORDER BY and of course in any outer queries.

As a note, I think your query is equivalent to:
SELECT Test,
CONVERT(DATE, MIN(datetime)) as StartDate,
CONVERT(DATE, MAX(datetime) as EndDate,
DATEDIFF(MONTH, CONVERT(DATE, MIN(datetime)), CONVERT(DATE, MAX(datetime)))
FROM Tests
GROUP BY Test;
The functions MIN() and MAX() are more succinct. In addition, the query is easier to read as a group by rather than as a select distinct.

Related

Lag function for dates in SQL

I am using LAG SQL function to get the difference of 2 consecutive dates in the records. THe issue is, the result it is returning is in DATE -TIME format whereas I want it either to be in Days, or hours. Attached is Output it outcome and the expected result.
Code:
select
*,
(action_date - lag(action_date) over (partition by voucher_no order by action_date)) as diff
from cte3
I would recommend using time for the last column -- although it does give you seconds as well:
SELECT DATEDIFF(day, LAG(Date) OVER (ORDER BY Date), Date ) AS diff_day,
CONVERT(TIME, Date - LAG(Date) OVER (ORDER BY Date)) as time
FROM t;
It is simple enough to convert this to just HH:MM format if you prefer:
SELECT DATEDIFF(day, LAG(Date) OVER (ORDER BY Date), Date ) AS diff_day,
CONVERT(VARCHAR(5), CONVERT(TIME, Date - LAG(Date) OVER (ORDER BY Date))) as time_hhmm
FROM t;
Here is a db<>fiddle.
I prefer this method because it is easier to format the time. In particular, the fiddle includes an example where the time is 00:01 and it is formatted as 00:01 rather than 0:1.
You can use DATEDIFF() function such as
SELECT
DATEDIFF(day, LAG(Date) OVER (ORDER BY Date), Date ) AS diff_day,
CONCAT(
DATEDIFF(minute, LAG(Date) OVER (ORDER BY Date), Date )/60,
':',
DATEDIFF(minute, LAG(Date) OVER (ORDER BY Date), Date )%60
) AS diff_hour_minute, *
FROM t
in order to get the desired difference values.
Demo

how to filter database data in sql server by per hour

if i have the data like the picture above, let's say 1 day data per minute, and i want to filter it to be per hour, so i will have 24 data, because i take the data per hour. how to do that
i have tried some queries, like this one using group by, but the result is not like i want. didn't work for grouping the datas.
SELECT myDatetime FROM datates
WHERE myDatetime >= '2020-03-01 05:30:00'
AND myDatetime < DATEADD(DAY,1,'2020-03-01 07:30:00')
group by myDatetime ,DATEPART(hour,myDatetime )
You can use row_number(). For instance, if you want the first value per hour:
select d.*
from (select d.*,
row_number() over (partition by convert(date, myDatetime), datepart(hour, myDatetime) order by mydatetime) as seqnum
from datates d
) d
where seqnum = 1;
If you want to sum the values, use aggregation:
select dateadd(hour, datepart(hour, myDatetime), convert(datetime, convert(date, myDatetime))),
sum(nilai)
from datates d
group by dateadd(hour, datepart(hour, myDatetime), convert(datetime, convert(date, myDatetime)));

Unable to get MIN and MAX time from a Datetime Column from every backday

I am trying to get MIN and MAX time from a Datetime (InOut) column but output is same for both column.
My query:
SELECT
CONVERT(NVARCHAR(12),MIN(Punch_history.Ecode)) as EmpCode,
CONVERT(NVARCHAR(12),MIN(EmployeeMaster.RecommandedBy)) as EmpID,
convert(date, InOut) as Report_date,
CONVERT(VARCHAR(10),(InOut),108) as InTime,
CONVERT(VARCHAR(10),(InOut),108) as OutTime,
CONVERT(NVARCHAR(12),MIN(TID)) as LOCATION
FROM Punch_history inner join EmployeeMaster on Punch_history.ECode = EmployeeMaster.Ecode
where CAST (InOut as DATE) between CAST(getdate() -1 as DATE ) and CAST(getdate() -1 as DATE ) and
EmployeeMaster.RecommandedBy like 'M0%' group by EmpID,InOut
Try grouping by the converted date instead of inout.
SELECT
CONVERT(NVARCHAR(12),MIN(Punch_history.Ecode)) as EmpCode,
convert(date, InOut) as Report_date,
CONVERT(NVARCHAR(10),min(InOut),108) as InTime,
CONVERT(NVARCHAR(10),max(InOut),108) as OutTime
FROM Punch_history
where CAST (InOut as DATE) between CAST(getdate() -1 as DATE )
and CAST(getdate() -1 as DATE )
group by EmpID, convert(date, InOut)
In addition to the grouping issue your where clause is quite strange. First of all using shortcuts for date math is a no win situation. It will fail with datetime2. And using between two identical values is just strange.
SELECT
CONVERT(NVARCHAR(12), MIN(Punch_history.Ecode)) as EmpCode,
convert(date, MIN(InOut)) as Report_date,
CONVERT(NVARCHAR(10), min(InOut), 108) as InTime,
CONVERT(NVARCHAR(10), max(InOut), 108) as OutTime
FROM Punch_history
where convert(date, InOut) = convert(date, dateadd(day, -1, getdate()))
group by EmpID
The major issue is that you should not group by InOut (datetime), that is why in the result max and min are the same. Actually, this is cancelling the grouping which you want to happen on daily basis.
SELECT
CONVERT(NVARCHAR(12),MIN(Punch_history.Empcode)) as EmpCode,
CONVERT(DATE , Max(InOut)) as Report_date,
CONVERT(NVARCHAR(10),min(InOut),108) as InTime,
CONVERT(NVARCHAR(10),max(InOut),108) as OutTime
FROM Punch_history
where datediff(day, CAST(InOut as DATE ) , CAST(getdate() as DATE ) )=1
group by EmpID
In addition, if empid and empcode are 1-1 (logically they should be)
then you can group directly with empcode, or both of them
avoiding this rather strange Min(empcode).

Summing a column by all transactions in a day

I'm trying to sum up all transactions for each day in my database.
SELECT DISTINCT
SUM(Balance) OVER (partition by Date) AS account_total,
Date
FROM tbl_FundData
ORDER BY Date;
The problem with the output is if a transaction is completed at a different time it becomes its own unique sum instead of rolling into the one day. I'm not sure how to modify the query to fix this.
I'm using SQL Server 2008 (I think)
Seems yo use DateTime as column data type so cast it as DATE :
SELECT DISTINCT SUM(Balance) OVER (partition by CAST([Date] AS DATE)) AS account_total, CAST([Date] AS DATE)
FROM tbl_FundData
ORDER BY CAST([Date] AS DATE);
Also you'd better use Group By in this case as :
SELECT SUM(Balance) AS account_total, CAST([Date] AS DATE)
FROM tbl_FundData
GROUP BY CAST([Date] AS DATE);
SELECT DISTINCT
SUM(Balance) OVER (partition by convert(varchar, Date, 103)) AS account_total,
convert(varchar, Date, 103) Date
FROM tbl_FundData
ORDER BY convert(varchar,Date,103)
SELECT SUM(Balance) account_total
,CAST(FLOOR(CAST(IssueDate AS FLOAT)) AS DATETIME) IssueDate
FROM tbl_FundData
GROUP BY
CAST(FLOOR(CAST(IssueDate AS FLOAT)) AS DATETIME)
ORDER BY
CAST(FLOOR(CAST(IssueDate AS FLOAT)) AS DATETIME)
I guess you have a timestamp as well in your date that's why you get unique values when you sum. Use this:
SELECT sum(balance)
FROM tbl_FundData
GROUP BY convert(date ,date, 106)
106 is a format for date. But you could use whatever.

Is there a better way to group a log on minutes?

I have the following that selects from a log and groups down to minute (excluding seconds and milisec):
SELECT DATEPART(YEAR, [Date]) AS YEAR, DATEPART(MONTH, [Date]) AS MONTH,
DATEPART(DAY, [Date]) AS DAY, DATEPART(HOUR, [Date]) AS HOUR,
DATEPART(MINUTE, [Date]) AS MIN, COUNT(*) AS COUNT
FROM [database].[dbo].[errorlog]
GROUP BY DATEPART(YEAR, [Date]), DATEPART(MONTH, [Date]), DATEPART(DAY, [Date]),
DATEPART(HOUR, [Date]), DATEPART(MINUTE, [Date])
ORDER BY DATEPART(YEAR, [Date]) DESC, DATEPART(MONTH, [Date]) DESC,
DATEPART(DAY, [Date]) DESC, DATEPART(HOUR, [Date]) DESC,
DATEPART(MINUTE, [Date]) DESC;
But as you can see thats a lot of fuzz just for getting a count, so I wonder if there is a better way to group it so I get grouped down to minutes in respect to year, month, day and hour?
This should would work:
select CAST([Date] AS smalldatetime) as time_stamp, count(*) as count
FROM [database].[dbo].[errorlog]
group by CAST([Date] AS smalldatetime)
order by CAST([Date] AS smalldatetime) desc;
Update after comments on this answer:
select dateadd(second,-datepart(ss,[Date]),[Date]) as time_stamp, count(*) as count
FROM [database].[dbo].[errorlog]
group by dateadd(second,-datepart(ss,[Date]),[Date])
order by dateadd(second,-datepart(ss,[Date]),[Date]) desc ;
The first solution rounds up the timestamp to the nearest minute. I realised that this is not exactly what the OP wanted.
So, the second solution just substracts the seconds part from the timestamp and leaves the timestamp with seconds as zero(Assuming [Date] does not have fractional seconds)
DATEADD(minute,DATEDIFF(minute,'20010101',[Date]),'20010101')
Should round all Date column values down to the nearest minute. So:
SELECT DATEADD(minute,DATEDIFF(minute,'20010101',[Date]),'20010101'),
COUNT(*) AS COUNT
FROM [database].[dbo].[errorlog]
GROUP BY DATEADD(minute,DATEDIFF(minute,'20010101',[Date]),'20010101')
ORDER BY DATEADD(minute,DATEDIFF(minute,'20010101',[Date]),'20010101') DESC;
(You could move this expression into a subquery if you want to further reduce the repetition)
You could do something like this to get
declare #now datetime
set #now = GETDATE()
select dateadd(minute, mm, #now) as date, c from (
select DATEDIFF(minute, #now, [Date]) as mm, COUNT(1) as c
from [database].[dbo].[errorlog]
group by DATEDIFF(minute, #now, [Date])
) t