sql subtract dates - sql

Some of the dates in a column of dates were recorded wrong. I'd like to make a query which subtracts one day from each date IF the days are in a certain date range.
I know I'll have to use DATEADD and UPDATE, but I can't seem to figure it out. Thanks in advance.

This should do it:
UPDATE [SomeTable]
SET [DateColumn] = DATEADD(d, -1, [DateColumn])
WHERE [DateColumn] BETWEEN [Date1] AND [Date2]
Here's the MSDN doc's on the DATEADD function: http://msdn.microsoft.com/en-us/library/ms186819.aspx
When performing updates on data like this, it's always best to run a select statement first with the same criteria to ensure that you're updating the correct records. It also helps reduce the stress level of updating (especially if you're unfamiliar with SQL).
SELECT *, --Depending on what columns you would like to see, the wildcard could be replaced
DATEADD(d, -1, [DateColumn]) AS ProposedDate
FROM [SomeTable]
WHERE [DateColumn] BETWEEN [Date1] AND [Date2]

DECLARE #min_date datetime, #max_date datetime;
UPDATE yourtable
SET date_column = DATEADD(day, -1, date_column)
WHERE id IN (
SELECT id
FROM yourtable
WHERE date_column BETWEEN #min_date AND #max_date
);
You'll have to set appropriate values for #min_date and #max_date.

UPDATE MyTable
SET DateField = DATEADD(Day, -1, DateField)
WHERE Datefield BETWEEN '1/1/2011' AND '2/1/2011'

use UPDATE
set the data = ( do the date math )
WHERE the date is in the range you want
and the key is the row you want.

Related

SQL - Correct dates not being presented in result set with date ranges in WHERE clause

Result set without the where clause shows there is data from the 30th September, when using the below where clause the 30th data doesn't come through in the result set (see images below for result sets) - my main aim is to show data from between the first day of the previous month and the last day of the previous month - Any help is much appreciated:
SQL Query
DECLARE #date1 datetime
DECLARE #date2 datetime
SET #date1 = getdate()
SET #date2 = getdate()-15 -- reason for less 15 days is because this will only be run within the first 15 days
-- of the current month, it will enable me to get the last day of the previous
-- month even if I run the SQL in the next month.
SELECT
A.TA_SEQ as 'WO No',
A.TA_DUE_DATEUTC
FROM
F_TASKS A WITH (NOLOCK)
INNER JOIN FINFILE B WITH (NOLOCK) ON A.TA_FKEY_IN_SEQ = B.IN_SEQ
INNER JOIN InstructionSetGroups C WITH (NOLOCK) ON B.InstructionSetGroupId = C.InstructionSetGroupId
WHERE
A.TA_TASK_DESC = 'BREAKDOWN' AND
A.TA_STATUS IN ('ACTIVE', 'ASSIGNED', 'COMPLETE', 'HISTORY') AND
A.TA_DUE_DATE >= DATEADD(DAY, 1, EOMONTH(#date1, -2)) AND
A.TA_DUE_DATE <= EOMONTH(#date2)
ORDER BY
A.TA_DUE_DATE desc
Result Sets
Result set using date ranges in where clause:
Result set without using date ranges in where clause - as you can see there's lots of data from the 30th too which isn't being captured
EOMONTH is terribly ambiguous, because it returns the last day of the month at midnight. I talk about why I don't like this function in Will you use EOMONTH()?
If you want the whole previous month, there are easier and safer ways than assuming the code will run within the first 15 days:
DECLARE #ThisMonth date = DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1);
SELECT ... FROM ...
WHERE TA_DUE_DATE >= DATEADD(MONTH, -1, #ThisMonth)
AND TA_DUE_DATE < #ThisMonth;
I explain why DATEFROMPARTS is best suited for this kind of work in Simplify Date Period Calculations in SQL Server.
With the rest, where do we begin?
getdate()-15 is lazy, unintuitive shorthand that will break in other contexts. Please use DATEADD(DAY, -15, GETDATE()) and see Bad Habits to Kick : Using shorthand with date/time operations.
You can declare/set in a more readable way than an explicit line for each declare and again for each set:
DECLARE #date1 datetime = GETDATE(),
#date2 datetime = DATEADD(DAY, -15, GETDATE());
as 'WO No' - please use AS [Wo No] or AS "Wo No" - string delimiters make those aliases look like, well, strings, and some forms of that syntax are deprecated.
please always specify the schema and never use meaningless aliases like A, B, C.
>= AND <= is the same as BETWEEN. I talk about why BETWEEN is always a terrible idea for date ranges in What do BETWEEN and the devil have in common?
Lots of resources on NOLOCK in I'm using NOLOCK; is that bad?
General date tips in Dating responsibly
I would take a different approach to handling your date values. That whole -15 stuff is bizarre and will cause issues on days towards the end of the month. Instead using a single variable is sufficient if you understand some date math.
DECLARE #date1 date --use the data datatype here because time is important in this case
SET #date1 = getdate()
--this is to show you the values and can be removed.
select BeginningOfLastMonth = dateadd(month, datediff(month, 0, #date1) - 1, 0)
, BeginningOfCurrentMonth = dateadd(month, datediff(month, 0, #date1), 0)
--Your select statement here
where A.TA_DUE_DATE >= dateadd(month, datediff(month, 0, #date1) - 1, 0)
A.TA_DUE_DATE <= dateadd(month, datediff(month, 0, #date1), 0) --ANY datetime value less than this is before the end of last month

Slowly changing dimension by date only

I have a database table using the concept of data warehousing of slowly changing dimension to keep track of old versions.
So, I implemented it with the Log Trigger mechanism.
My table is like this:
CREATE TABLE "T_MyTable" (
"Id" INT NOT NULL DEFAULT NULL,
"Description" NVARCHAR(255) NULL DEFAULT NULL )
and I created an hystory table
CREATE TABLE "T_MyTableHistory" (
"Id" INT NOT NULL DEFAULT NULL,
"Description" NVARCHAR(255) NULL DEFAULT NULL,
StartDate DATETIME,
EndDate DATETIME )
Then, with a trigger like this, I get the history:
CREATE TRIGGER TableTrigger ON T_MyTable FOR DELETE, INSERT, UPDATE AS
DECLARE #NOW DATETIME
SET #NOW = CURRENT_TIMESTAMP
UPDATE T_MyTableHistory
SET EndDate = #now
FROM T_MyTableHistory, DELETED
WHERE T_MyTableHistory.Id = DELETED.Id
AND T_MyTableHistory.EndDate IS NULL
INSERT INTO T_MyTableHistory (Id, Description, StartDate, EndDate)
SELECT Id, Description, #NOW, NULL
FROM INSERTED
And, to query the history table, I use
SELECT Id, Description
FROM T_MyTableHistory
WHERE #DATE >= StartDate
AND (#DATE < EndDate OR EndDate IS NULL)
Now, my question is this: my customer will actually query the history table by date only (i.e. without the time of the day), so I need to get the record version at that date.
I thought about two options:
change the trigger (how?) to record only one "history" record per date.
keep the trigger as-is, recording all the changes in the database (including date and time), but then query the history table to get the latest version of a particular date (how?)
My feeling is that the second option is easier to implement, otherwise the trigger could become complicated (INSERT or UPDATE, depending on the presence of the history record for current date).
I'd need some help in choosing the right direction, and I'd like to have an example of the SQL query needed, in the chosen option.
I agree with your second opinion.
It is good to save date along with time. While filtering data based on date use
CONVERT() function to make sure that DATE only got compared. Also, When client enter a single date, If records have same start and end date
they will not be in your filter so use Date >= StartDate and Date <= EndDate not (>= ,<)
DECLARE #Date AS DATETIME
SET #Date = '2013-07-30'
SELECT TOP 1 Id, Description
FROM T_MyTableHistory
WHERE CONVERT(VARCHAR(20), #DATE, 103)
>= CONVERT(VARCHAR(20), StartDate, 103)
AND (CONVERT(VARCHAR(20), #DATE, 103)
< CONVERT(VARCHAR(20), EndDate, 103) OR EndDate IS NULL)
ORDER BY StartDate DESC
At the end, I came up with this query:
SELECT Id, Description
FROM T_MyTableHistory
WHERE ( DateAdd(day, datediff(day,0, #MyDate), 0) >= StartDate ) AND
(( DateAdd(day, datediff(day,0, #MyDate), 0) < EndDate ) OR ( EndDate IS NULL ))
This should be faster than varchar<->datetime conversion, and it should also be locale-independent.
By the way, this query should not need the TOP 1 and the ORDER BY clauses, since the function
DateAdd(day, datediff(day,0, #MyDate)
automatically returns the selected date, with "midnight" time (e.g. 20141215 00:00:00), so records with the same date are automatically cut out of the results.
References:
How to return the date part only from a SQL Server datetime datatype
Best approach to remove time part of datetime in SQL Server

Given Date parameter is considered as Date Time parameter

I'm writing a stored procedure in sql!
I have to get records in the particular date.
I am using this query:
Declare #FromDate datetime
set #FromDate = '06/02/2014'
select * from Table where Date = #FromDate
Actually, in the Database there are 10 records in that date, but it is showing only two records because the #FromDate is taking like this 06/02/2014 00:00:00.000
If I write the query like this it means it works correctly!
select * from Table
where Date between '2014-08-28 00:00:00.000' and '2014-08-28 23:59:59.999'
How to solve this? I need to get all the records in that particular date.
Please help me !
If #FromDate is of data type datetime and Table.Date is also of data type datetime then:
Declare #FromDate datetime = '2014-06-02';
Select Table.Date
From Table
Where Table.Date >= #FromDate And Date < DateAdd(day, 1, Table.Date)
Above, we create an inclusive lower boundary (anything equal to or later than 2014-06-02) and an exclusive upper boundary (anything earlier than 2014-06-03), but with a variable defined just once. So, effectively the query checks 2014-06-02 <= FromDate < 2014-06-03.
If you convert DateTime to Nvarchar your issue would be solved.
Try this query:
Declare #Date datetime='2014-08-28 00:00:00.000'
select * from Table
where CONVERT(nvarchar(20),Date,105) = CONVERT(nvarchar(20),#Date,105)

How to get date difference between two dates in same year with one date is from an input date not from the year

Well this is my case: I have an input date X (dd-mm-yyyy), and I want to count the number of days between it with the year part is changed into current year and today's date in SQL. I t comes with the following condition, after the year is changed temporarily: (Here's my current idea of the logic)
- If date X is earlier than today, then difference = datediff(X,now), with the X year is current year
- If date X is later than today, then difference = datediff(X,now), with the X year is one year before
Sample case:
1st case: The input date is 6-6-1990. Today (automatically generated) is 22-8-2011. Then the difference will be = datediff(6-6-2011,22-08-2011)
2nd case: The input date is 10-10-1990. Today (automatically generated) is 22-8-2011. Then the difference will be = datediff(10-10-2010,22-08-2011)
Any idea how to do this in SQL (in SQL Server)? Or is there any other more simple alternatives for this problem? I'd also like this to be done in the query and not using a stored procedure or function
Sorry if there's already a similar question, I just don't know the exact keyword for this problem :( if there's a question like this previously, feel free to direct me there.
Thanks in advance
Here is the implementation (if I understood the logic you need correctly):
USE YourDbName
GO
CREATE FUNCTION YearPartDiff (#date datetime)
RETURNS int
AS
BEGIN
DECLARE #dateCurrentYear datetime
SET #dateCurrentYear = DATEADD(year, YEAR(GETDATE()) - YEAR(#date), #date)
DECLARE #result int
IF #dateCurrentYear < GETDATE()
SET #result = ABS(DATEDIFF(day, #dateCurrentYear, GETDATE()))
ELSE
SET #result = ABS(DATEDIFF(day, DATEADD(year, -1, #dateCurrentYear), GETDATE()))
RETURN(#result)
END
GO
And the example of usage:
USE YourDbName
GO
DECLARE #someDate datetime
SET #someDate = '2011-06-06'
SELECT dbo.YearPartDiff(#someDate) /*returns 77*/
SET #someDate = '2010-10-10'
SELECT dbo.YearPartDiff(#someDate) /*returns 316*/
Basically, #Andrei's solution, but in a single statement:
SELECT
DayDiff = DATEDIFF(
DAY,
DATEADD(YEAR, CASE WHEN LastOcc > GETDATE() THEN -1 ELSE 0 END, LastOcc),
GETDATE()
)
FROM (
SELECT LastOcc = DATEADD(YEAR, YEAR(GETDATE()) - YEAR(#InputDate), #InputDate)
) s
This seems to do the job
SELECT DATEDIFF(DAY, CONVERT(DATETIME, N'2011-06-06'), CONVERT(DATETIME, N'2011-08-22'))
So the basic syntax is
SELECT DATEDIFF(DAY, CONVERT(DATETIME, N'yyyy-mm-dd'), CONVERT(DATETIME, N'yyyy-mm-dd '))
Alternatively, you can use GETDATE() instead of the string for today's date
I have used "SELECT DATEDIFF( D, "+myDate+", GETDATE())" in my code, on SQL Server 2005. It works for me. The value myDate of course would be the DateTime input value.
you should try this query:
create table #T (inp_date datetime)
insert #T values ('06-06-1990')
insert #T values ('08-22-1990')
insert #T values ('10-10-1990')
--select * from #T
select inp_date, GETDATE(),
CASE
WHEN DATEADD(yy,DATEDIFF(yy,inp_date,GETDATE()),inp_date) <= GETDATE()
THEN DATEDIFF(dd,DATEADD(yy,DATEDIFF(yy,inp_date,GETDATE()),inp_date),GETDATE())
ELSE DATEDIFF(dd,DATEADD(yy,DATEDIFF(yy,inp_date,GETDATE())-1,inp_date),GETDATE())
END
from #T

Create list of dates, a month apart, starting from current date

I'm looking for a simple select query (not using a table) to just return a list of dates, 1 month apart. The output should looke something like this, (assuming GetDate() = '2011-07-05 11:59:000' and I wanted between NOW() and NOW()+4 months
Date
2011-07-05 11:59:000
2011-08-05 11:59:000
2011-09-05 11:59:000
2011-10-05 11:59:000
2011-11-05 11:59:000
The part that's killing me is calculating the next year, for example if i run this query in Nov, the months should be listed as 11, 12, 1, 2. Thanks!
You can use recursive CTE and need not string UNIONs together if the requirement is not fixed as below:
;with MonthlyCalendar as (
select cast(getdate() as datetime) as dt
union all
select dateadd(mm, 1, dt)
from MonthlyCalendar
)
select top 5 dt as [Date] from MonthlyCalendar
option (maxrecursion 0)
When it comes to performance and you have the need for only 4 months above UNION is far superior than recursive option.
#JNK's answer, just reworked to give you each date in a row:
SELECT GETDATE() 'Date'
UNION
SELECT DATEADD(month, 1, GETDATE()) 'Date'
UNION
SELECT DATEADD(month, 2, GETDATE()) 'Date'
UNION
SELECT DATEADD(month, 3, GETDATE()) 'Date'
UNION
SELECT DATEADD(month, 4, GETDATE()) 'Date'
Had to do something like this just this morning!
I prefer to handle these small (one off) situations by looping through the data and building the list based on the current (or target) date:
if object_id('tempdb..#dates') is not null drop table #dates
select dateadd(MINUTE, -1, CONVERT(VARCHAR(10), dateadd(DD, 1, getdate()), 111)) result into #dates
declare #current datetime
select #current = result from #dates
while not exists (select * from #dates where result = dateadd(month, 4, #current))
begin
insert into #dates
select dateadd(month, 1, max(result)) from #dates
end
select * from #dates order by result
SELECT GETDATE(),
DATEADD(month, 1, GETDATE()),
DATEADD(month, 2, GETDATE()),
DATEADD(month, 3, GETDATE()),
DATEADD(month, 4, GETDATE())
DATEADD takes care of all that year consideration logic for you, and leap years and such too.
Obviously this returns a list of columns. See Ryan's answer for the row solution!
try this :
DECLARE #intFlag INT
declare #LastLimit as int
set #LastLimit = 4
SET #intFlag = 0
WHILE (#intFlag <#LastLimit)
BEGIN
select DATEADD(month, #intFlag, GETDATE())
SET #intFlag = #intFlag + 1
END
You can use a dynamic script to build a calendar set.
A good example can be found here:
http://blog.namwarrizvi.com/?p=139
In that example you would just replaced the DATEADD and DATEDIFF to use months instead of days.
There is a generic elegant solution on the problem here: Get usernames logged in on day by day basis from database
Of course, it will require adjustments, but the principle is great.
In SQL Oracle, you can easily create a list of dates using CONNECT BY.
For example, if you want all the months between '2000-12-31' and today:
select add_months(date '2000-12-31',level) dates
from dual
connect by level <= months_between(sysdate, date '2000-12-31');
The function used to obtain the number of months, here months_between, can change between different SQL versions (e.g. in SQL Server it should be datediff()).
It's often useful to keep a table of incrementing values, as large as you need it to be:
create table sequence ( value int not null primary key clustered )
insert sequence values(0)
insert sequence values(1)
insert sequence values(2)
insert sequence values(3)
. . .
insert sequence values(n)
With such a table, producing a list of any size is trivial. This will give you 36 date/time values a month apart, starting with the current date/time.
select top 36
dtValue = dateadd( month , sequence.value , date(current_timestamp) )
from dbo.sequence
order by sequence.value