Related
I have a simple table with just a DATETIME filed.
My question is, how can I get the value related to the end-of-year of previous year, with a window-function query?
I've tried with this query but the result is the end-of-year of the current year:
SELECT datefield, max(datefield) OVER (PARTITION BY YEAR(datefiled)) FROM foo
I am using SQL Server 2012.
Many thanks to all.
If you want to filter records then you need to use Where clause. You need something like this not window function.
SELECT TOP 1 WITH ties *
FROM foo
WHERE datefield <= Datefromparts(Year(Getdate()) - 1, 12, 31)
ORDER BY datefield DESC
or
SELECT *
FROM foo
WHERE datefield = (SELECT Max(datefield) AS last_date_prev_year
FROM foo
WHERE datefield <= Datefromparts(Year(Getdate()) - 1, 12, 31))
I don't think you need to use a windowed function. A simple filter combined with the max function will return the end of the previous year.
-- Where clause removes records from current and future years.
SELECT
MAX(datefield)
FROM
foo
WHERE
YEAR(datefield) < YEAR(GETDATE())
;
Although simple, this approach has a small problem. Using the year function, on datefield in the where clause, makes the query non-sargable. If performance is an issue; you could fix by using DateFromParts as demonstrated in #Prdp's fine answer.
EDIT
This version of the query uses a windowed function, as requested by the OP.
-- Max of previous year, using a windowed function.
SELECT
MAX(datefield) AS LastYearEnd
FROM
(
-- Rank records based on year.
-- Current year is 1, last year is 2, etc.
SELECT
datefield,
DENSE_RANK() OVER (ORDER BY YEAR(datefield) DESC) AS rn
FROM
foo
) AS dr
WHERE
rn = 2
;
The above only returns one record. If you want see the last day of the previous year, next to every record in your table:
-- Returns last day of previous year, relative to dateField.
SELECT
datefield,
DATEADD(YEAR, -1, MAX(datefield) OVER (PARTITION BY YEAR(datefield)))
FROM
foo
;
I have an SSRS report which when I try to calculate the previous value the first line always gives incorrect value since there is no previous number for it to calculate.
=CDec(format(DateDiff("s",previous(Fields!SyncDate.Value),Fields!Date.Value)/60,"0.00"))
The value of the first line comes out 1059831848.62
Is there a way to tell it to skip first line?
Because also i need to sum it all to get total which counts the first line and gives a huge total.
My query consists of the following:
SELECT ToppingCount, DriverName, COUNT(Pizza) AS Count, Date, SyncDate, BranchName, Branch
FROM System
WHERE (Date BETWEEN #datefrom AND #DateTo) AND (Branch IN (#branch)) AND (SystemSource = 'newsys') AND (SystemSource <> 'oldsys')
GROUP BY Pizza, ToppingCount, DriverName, Date, SyncDate, BranchName, Branch
ORDER BY Branch, DriverName, Date
Thanks
You could add a Row_Number column to your query like so..
SELECT ToppingCount,
DriverName,
COUNT(Pizza) AS Count,
Date,
SyncDate,
BranchName,
Branch,
ROW_NUMBER() OVER (ORDER BY Branch,DriverName,Date) Rn
FROM System
WHERE (Date BETWEEN #datefrom AND #DateTo)
AND (Branch IN (#branch))
AND (SystemSource = 'newsys')
AND (SystemSource <> 'oldsys')
GROUP BY Pizza,
ToppingCount,
DriverName,
Date,
SyncDate,
BranchName,
Branch
ORDER BY Branch,
DriverName,
Date
Then just check to see if you are on Rn = 1 before you do your calculation.
=iif(Fields!Rn.Value = 1, "", CDec(format(DateDiff("s",previous(Fields!SyncDate.Value),Fields!Date.Value)/60,"0.00")))
or
=iif(Fields!Rn.Value = 1, 0, CDec(format(DateDiff("s",previous(Fields!SyncDate.Value),Fields!Date.Value)/60,"0.00")))
if you need to reset the Row_Number for groupings, you can add Partition By to reset the number to 1
You can check the value of the previous sync date field and output a 0 or blank if required.
=CDec(iif(IsNothing(previous(Fields!SyncDate.Value)), 0.0, format(DateDiff("s",previous(Fields!SyncDate.Value),Fields!Date.Value)/60,"0.00")))
Without row_number you can use an outer apply to get the last sync date in the query and pre-calculate the 'sync delta':
SELECT
p.ToppingCount,
p.DriverName,
p.Date,
p.SyncDate,
CASE
WHEN i.SynchDate IS NULL THEN 0.0
ELSE DATEDIFF(SS, l.SyncDate, p.Date) / 60.0
END AS SyncTimeDelta,
p.BranchName,
p.Branch,
COUNT(p.Pizza) AS Count
FROM System p
OUTER APPLY
(
-- Join clause depends on how you're grouping
-- in the report.
SELECT TOP 1
i.SyncDate
FROM System i
WHERE
i.SystemSource = 'newsys'
AND i.Branch = p.Branch
AND i.DriverName = p.DriverName
AND i.Date < p.Date
ORDER BY
i.SyncDate DESC
) l
WHERE
(p.Date BETWEEN #datefrom AND #DateTo) AND
(p.Branch IN (#branch)) AND
(p.SystemSource = 'newsys') AND
(p.SystemSource <> 'oldsys') -- This will never be evaluated
GROUP BY
p.Pizza, p.ToppingCount, p.DriverName, p.Date, p.SyncDate, p.BranchName, p.Branch, l.SyncDate
ORDER BY
p.Branch, p.DriverName, p.Date
This way you can just sum the sync delta in the report. I don't know enough about the query but I've made the assumption that:
You want the time between the last sync date before the current [date] column as per your formula for the given branch and driver.
I apologize if there are syntax issues, I'm typing this up without reference.
Im working on a query where if the records in a table are from the current year, i need only to select records whose day and month are equal or greater than the current date. Otherwise (if the year is greater) I need to select all dates.
I was thinking of using the iif function or conditions on the where clause but im not close at at all.
For example
SELECT IFF(YEAR(FECHAPAGO) = YEAR(DATE()), MONTO WHERE MONTH(FECHAPAGO) >= MONTH(DATE), MONTO) FROM CUPONES
or
SELECT MONTO FROM CUPONES WHERE IF YEAR(FECHAPAGO) = YEAR(DATE()) ...
I apologize if im not being clear enough but im having trouble putting it to words. If any clarification is needed Ill be happy to rephrase my question.
Thanks in advance
You can do it with straight logic;
select monto from cupones where (year(fechapago) = year(date() and (month(fechapago) > month(date()) or (month(fechapago) == month(date() and day(fechapago) > day(date()))( or (year(fechapago()) > year(date()))
I have a table TEST with a DATETIME field, like this:
ID NAME DATE
1 TESTING 2014-03-19 20:05:20.000
What I need a query returning this row and every row with date 03/19/2014, no matter what the time is. I tried using
select * from test where date = '03/19/2014';
But it returns no rows. The only way to make it work that I found is to also provide the time portion of the date:
select * from test where date = '03/19/2014 20:03:02.000';
use range, or DateDiff function
select * from test
where date between '03/19/2014' and '03/19/2014 23:59:59'
or
select * from test
where datediff(day, date, '03/19/2014') = 0
Other options are:
If you have control over the database schema, and you don't need the
time data, take it out.
or, if you must keep it, add a computed column attribute that has the time portion of the date value stripped off...
Alter table Test
Add DateOnly As
DateAdd(day, datediff(day, 0, date), 0)
or, in more recent versions of SQL Server...
Alter table Test
Add DateOnly As
Cast(DateAdd(day, datediff(day, 0, date), 0) as Date)
then, you can write your query as simply:
select * from test
where DateOnly = '03/19/2014'
Simple answer;
select * from test where cast ([date] as date) = '03/19/2014';
I am using MySQL 5.6 and there is a DATE function to extract only the date part from date time. So the simple solution to the question is -
select * from test where DATE(date) = '2014-03-19';
http://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html
This works for me for MS SQL server:
select * from test
where
year(date) = 2015
and month(date) = 10
and day(date)= 28 ;
select * from test
where date between '03/19/2014' and '03/19/2014 23:59:59'
This is a realy bad answer. For two reasons.
1.
What happens with times like 23.59.59.700 etc.
There are times larger than 23:59:59 and the next day.
2.
The behaviour depends on the datatype.
The query behaves differently for datetime/date/datetime2 types.
Testing with 23:59:59.999 makes it even worse because depending on the datetype you get different roundings.
select convert (varchar(40),convert(date , '2014-03-19 23:59:59.999'))
select convert (varchar(40),convert(datetime , '2014-03-19 23:59:59.999'))
select convert (varchar(40),convert(datetime2 , '2014-03-19 23:59:59.999'))
-- For date the value is 'chopped'.
-- For datetime the value is rounded up to the next date. (Nearest value).
-- For datetime2 the value is precise.
use this
select * from TableName where DateTimeField > date() and DateTimeField < date() + 1
Try this
select * from test where Convert(varchar, date,111)= '03/19/2014'
you can try this
select * from test where DATEADD(dd, 0, DATEDIFF(dd, 0, date)) = '03/19/2014';
There is a problem with dates and languages and the way to avoid it is asking for dates with this format YYYYMMDD.
This way below should be the fastest according to the link below. I checked in SQL Server 2012 and I agree with the link.
select * from test where date >= '20141903' AND date < DATEADD(DAY, 1, '20141903');
Bad habits to kick : mis-handling date / range queries
You can use this approach which truncates the time part:
select * from test
where convert(datetime,'03/19/2014',102) = DATEADD(dd, DATEDIFF(dd, 0, date), 0)
-- Reverse the date format
-- this false:
select * from test where date = '28/10/2015'
-- this true:
select * from test where date = '2015/10/28'
Simply use this in your WHERE clause.
The "SubmitDate" portion below is the column name, so insert your own.
This will return only the "Year" portion of the results, omitting the mins etc.
Where datepart(year, SubmitDate) = '2017'
select *, cast ([col1] as date) <name of the column> from test where date = 'mm/dd/yyyy'
"col1" is name of the column with date and time
<name of the column> here you can change name as desired
select *
from invoice
where TRUNC(created_date) <=TRUNC(to_date('04-MAR-18 15:00:00','dd-mon-yy hh24:mi:ss'));
Test this query.
SELECT *,DATE(chat_reg_date) AS is_date,TIME(chat_reg_time) AS is_time FROM chat WHERE chat_inbox_key='$chat_key'
ORDER BY is_date DESC, is_time DESC
select * from invoice where TRANS_DATE_D>= to_date ('20170831115959','YYYYMMDDHH24MISS')
and TRANS_DATE_D<= to_date ('20171031115959','YYYYMMDDHH24MISS');
SELECT * FROM test where DATEPART(year,[TIMESTAMP]) = '2018' and DATEPART(day,[TIMESTAMP]) = '16' and DATEPART(month,[TIMESTAMP]) = '11'
use trunc(column).
select * from test t where trunc(t.date) = TO_DATE('2018/06/08', 'YYYY/MM/DD')
I am trying to extract the difference between two SQL DateTime values in seconds, with decimal places for some performance monitoring.
I have a table, "Pagelog" which has a "created" and "end" datetime. In the past I have been able to do the following:
SELECT DATEDIFF(ms, pagelog_created, pagelog_end)/1000.00 as pl_duration FROM pagelog
However I have started getting the following error:
Msg 535, Level 16, State 0, Line 1
The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.
I have seen numerous responses to this error stating that I should use a less precise unit of measurement. But this hardly helps when I need to distinguish between 2.1 seconds and 2.9 seconds, because DATEDIFF(s,..,..) will return INT results and lose the accuracy I need.
I originally thought that this had been caused by a few values in my table having a huge range but running this:
SELECT DATEDIFF(s, pagelog_created, pagelog_end) FROM pagelog
ORDER BY DATEDIFF(s, pagelog_created, pagelog_end) DESC
Returns a max value of 30837, which is 8.5 hours or 30,837,000 milliseconds, well within the range of a SQL INT as far as I know?
Any help would be much appreciated, as far as I can tell I have two options:
Somehow fix the problem with the data, finding the culprit values
Find a different way of calculating the difference between the values
Thanks!
The StackOverflow magic seems to have worked, despite spending hours on this problem last week, I re-read my question and have now solved this. I thought I'd update with the answer to help anyone else who has this problem.
The problem here was not that there was a large range, there was a negative range. Which obviously results in a negative overflow. It would have been helpful if the SQL Server error was a little more descriptive but it's not technically wrong.
So in my case, this was returning values:
SELECT * FROM pagelog
WHERE pagelog_created > pagelog_end
Either remove the values, or omit them from the initial result set!
Thanks to Ivan G and Andriy M for your responses too
You can try to avoid overflow like this:
DECLARE #dt1 DATETIME = '2013-01-01 00:00:00.000'
DECLARE #dt2 DATETIME = '2013-06-01 23:59:59.997'
SELECT DATEDIFF(DAY, CAST(#dt1 AS DATE), CAST(#dt2 AS DATE)) * 24 * 60 * 60
SELECT DATEDIFF(ms, CAST(#dt1 AS TIME), CAST(#dt2 AS TIME))/1000.0
SELECT DATEDIFF(DAY, CAST(#dt1 AS DATE), CAST(#dt2 AS DATE)) * 24 * 60 * 60
+ DATEDIFF(ms, CAST(#dt1 AS TIME), CAST(#dt2 AS TIME))/1000.0
First it gets number of seconds in whole days from the DATE portion of the DATETIME and then it adds number of seconds from the TIME portion, after that, it just adds them.
There won't be error because DATEDIFF for minimum and maximum time in TIME data type cannot produce overflow.
You could of course do something like this:
SELECT
DATEDIFF(ms, DATEADD(s, x.sec, pagelog_created), pagelog_end) * 0.001
+ x.sec AS pl_duration
FROM pagelog
CROSS APPLY (
SELECT DATEDIFF(s, pagelog_created, pagelog_end)
) x (sec)
;
As you can see, first, the difference in seconds between pagelog_created and pagelog_end is taken, then the seconds are added back to pagelog_created and the difference in milliseconds between that value and pagelog_end is calculated and added to the seconds.
However, since, as per your investigation, the table doesn't seem to have rows that could cause the overflow, I'd also double check whether that particular fragment was the source of the error.
with cte as(
select rownum = row_number() over(partition by T.TR_ID order by T.[date]),
T.* from [dbo].[TR_Events] T
)
select cte.[date],nex.[date],convert(varchar(10),datediff(s, cte.[date], nex.[date])/3600)+':'+
convert(varchar(10),datediff(s, cte.[date], nex.[date])%3600/60)+':'+
convert(varchar(10),(datediff(s,cte.[date], nex.[date])%60))
from cte
left join cte prev on prev.rownum = cte.rownum - 1
left join cte nex on nex.rownum = cte.rownum + 1