SQL Combine Date and Time columns into single DateTime column - sql

I would like to combine two datetime2 columns (called Date and Time respectively) into a single datetime2 column in the same table. The 2 old columns will be removed afterwards.
This query works when testing the combine.
SELECT CAST(DATEADD(day, 0, DATEDIFF(day, 0, Date)) AS DATETIME)
+ CAST(DATEADD(day, 0 - DATEDIFF(day, 0, Time), Time) AS DATETIME)
FROM dbo.Orders
Currently I've tried the following, but I can't figure out how to put the results into an existing column called OrderDate.
SOLVED
UPDATE dbo.Orders
SET
OrderDate = CAST(DATEADD(day, 0, DATEDIFF(day, 0, Date)) AS DATETIME)
+ CAST(DATEADD(day, 0 - DATEDIFF(day, 0, Time), Time) AS DATETIME);

Why the subquery? Just use the expression directly. (Just for the record: Maybe the expression can be optimized/simplyfied as well, I didn't look into this.)
UPDATE dbo.orders
SET orderdate = cast(dateadd(DAY, 0, datediff(DAY, 0, date)) AS datetime)
+ cast(dateadd(DAY, 0 - datediff(DAY, 0, time), time) AS datetime);

Unless all the times in the time column end with milliseconds that have a 0, 3, or 7 in the units position (I would never bank on such a thing), there will be rounding errors when you convert the time to DATETIME.
Here are two of the more extreme examples of what can happen when the units position of time is a "9" during conversion from DATETIME2 to DATETIME.
--========================================================================================
-- Demonstrate the erroneous results of using a conversion to DATETIME.
--========================================================================================
DROP TABLE IF EXISTS #TestTable; --Just to make reruns in SSMS easier.
GO
--===== Create and populate the test table on-the-fly.
SELECT v1.RowNum
,Date = CONVERT(DATETIME2,v1.Date)
,Time = CONVERT(DATETIME2,v1.Time)
,v1.Expected
INTO #TestTable
FROM (VALUES
(1,'2022-12-21','22:59:59.999','2022-12-21 22:59:59.999')
,(2,'2022-12-31','23:59:59.999','2022-12-31 23:59:59.999')
)v1(RowNum,Date,Time,Expected)
;
--===== Show that the currently accepted answer (posted on Mar 2, 2022) is INCORRECT.
WITH cteCombine AS
(
SELECT *
,Combined = cast(dateadd(DAY, 0, datediff(DAY, 0, date)) AS datetime)
+ cast(dateadd(DAY, 0 - datediff(DAY, 0, time), time) AS datetime)
FROM #TestTable
)
SELECT *
,Result = IIF(CONVERT(DATETIME2,Combined) = CONVERT(DATETIME2,Expected)
,'Correct','Incorrect')
FROM cteCombine
ORDER BY RowNum
;
Here are the results from the test code above. The "Combined" column doesn't match the "Expected" column.
Here's one way to overcome the sometimes extreme rounding issues of DATETIME using the same data as the test code above.
--========================================================================================
-- Demonstrate the results of a correct method to avoid the rounding of DATETIME.
--========================================================================================
DROP TABLE IF EXISTS #TestTable; --Just to make reruns in SSMS easier.
GO
--===== Create and populate the test table on-the-fly.
SELECT v1.RowNum
,Date = CONVERT(DATETIME2,v1.Date)
,Time = CONVERT(DATETIME2,v1.Time)
,v1.Expected
INTO #TestTable
FROM (VALUES
(1,'2022-12-21','22:59:59.999','2022-12-21 22:59:59.999')
,(2,'2022-12-31','23:59:59.999','2022-12-31 23:59:59.999')
)v1(RowNum,Date,Time,Expected)
;
--===== Show Correct Method.
WITH cteCombine AS
(
SELECT *
,Combined = CONVERT(DATETIME2
,CONVERT(VARBINARY(6),CONVERT(TIME,Time))
+CONVERT(BINARY(3),CONVERT(DATE,Date)))
FROM #TestTable
)
SELECT *
,Result = IIF(CONVERT(DATETIME2,Combined) = CONVERT(DATETIME2,Expected)
,'Correct','Incorrect')
FROM cteCombine
ORDER BY RowNum
;
And here are the results from that bit of code. The "Combined" column matches the "Expected Column"

Related

Conversion failed when converting the nvarchar value '1000050TPAR' to data type int

I am getting this error when running this specific SQL query.
It used to work before. Not sure if certain values from the ID e.g. 1000050TPAR is causing this to fail.
CREATE TABLE #TempTable
(
[ID] INT,
[CarID] VARCHAR (8),
[Dates] DATE
)
GO
INSERT INTO #TempTable ([ID], [CarID], [Dates])
SELECT
ordnum AS [ID],
wh_id AS [CarID],
DATEADD(DAY, -10, CAST(GETDATE() AS DATE)) AS [Dates]
FROM
ord
WHERE
customer_id = '150'
AND moddte BETWEEN DATEADD(DAY, -10, GETDATE()) AND GETDATE()
SELECT * FROM #TempTable
DROP TABLE #TempTable
GO
The only possible integer conversion in this code is ordnum when loaded into the temporary table, because the corresponding column is for integers. You can find the offending values by using:
select ordnum
from ord
where try_convert(int, ordnum) is null;
The likely solution, though, is to use the same type in the temporary table as in the raw data. Or even to change the code to:
select ordnum as [ID],
wh_id as [CarID],
dateadd(day, -10, cast(getdate() as date)) as [Dates]
into #TempTable
from ord
where customer_id = '150' and
moddte between dateadd(day, -10, getdate()) and getdate();
Then you don't need to declare the types of the columns at all.
The where clause can probably be simplified to moddte >= dateadd(day, -10, getdate()) because modification dates are unlikely to be in the future.

Sql query to select data from the day before at 4:15:01 pm to today at 4:15:00 pm

I'm building a SSIS package where I need to get data from the day before at 4:15:01 pm to today's date at 4:15:00 pm, but so far the only query that I know is how to get the day before. I am not sure how to also add hour, minute, and second to the same query. Can someone please show me how to add the hour, minute, and second to this sql query?
Below is the query I have so far.
SELECT Posted_Date, Total_Payment FROM
Table1
WHERE Posted_Date >= dateadd(day, datediff(day, 1, Getdate()), 0)
and Posted_date < dateadd(day, datediff(day, 0, getdate()), 0)
order by posted_date
Be very careful about precision here - saying you want things from 4:15:01 PM yesterday, means that at some point you could possibly lose data (e.g. 4:15:00.500 PM). Much better to use an open-ended range, and I typically like to calculate that boundary outside of the query:
DECLARE #today DATETIME, #today_at_1615 DATETIME;
SELECT #today = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0),
#today_at_1615 = DATEADD(MINUTE, 16.25*60, #today);
SELECT Posted_Date, Total_Payment
FROM dbo.Table1
WHERE Posted_Date > DATEADD(DAY, -1, #today_at_1615)
AND Posted_Date <= #today_at_1615
ORDER BY Posted_date;
You should also avoid using DATEDIFF in queries like this - there is a cardinality estimation bug that can really affect the performance of your query. I don't believe the bug affects SQL Server 2005, but if you wanted to be ultra-safe you could change it to the slightly more expensive:
SELECT #today = CONVERT(CHAR(8), GETDATE(), 112),
And in either case, you should mark this code with some kind of flag, so that when you do get onto SQL Server 2008 or later, you can update it to use the much more optimal:
SELECT #today = CONVERT(DATE, GETDATE()),
SSIS things
I created an SSIS package with an Execute SQL Task that created my table and populated it with data which was then used by a Data Flow Task
Execute SQL Task
I created an Execute SQL Task, connected to an OLE DB Connection Manager and used the following direct input.
-- This script sets up a table for consumption by the DFT
IF EXISTS
(
SELECT * FROM sys.tables AS T WHERE T.name = 'Table1' AND T.schema_id = SCHEMA_ID('dbo')
)
BEGIN
DROP TABLE dbo.Table1;
END;
CREATE table dbo.Table1
(
Posted_Date datetime NOT NULL
, Total_Payment int NOT NULL
);
INSERT INTO
dbo.Table1
(
Posted_Date
, Total_Payment
)
SELECT
DATEADD(minute, D.rn, DATEADD(d, -1, CURRENT_TIMESTAMP)) AS Posted_Date
, D.rn
FROM
(
-- 2 days worth of data
SELECT TOP (60*24*2)
DI.rn
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
FROM sys.all_columns AS AC
) DI
) D;
Right click on the Execute SQL Task and execute it. This ensures the table is created so that we can work with it in the next step.
Data Flow Task
I created a Data Flow Task and set the DelayValidation property to True since my connection manager is pointed at tempdb. This is likely not needed in the real world.
I added an OLE DB Source component and configured it to use the first query
I then added a Derived Column to allow me to attach a data viewer to the flow and fired the package off. You can observe that the last value streaming through is as expected.
I'd think this would work similarly.
DECLARE #today DATETIME, #today_at_1615 DATETIME;
SELECT #today = CONVERT(DATE, GETDATE()),
#today_at_1615 = DATEADD(MINUTE, 16.25*60, #today);
SELECT Posted_Date, Total_Payment
FROM dbo.Table1
WHERE Posted_Date between DATEADD(DAY, -1, #today_at_1615) AND #today_at_1615
ORDER BY Posted_date
Would've made a comment about using BETWEEN but I don't have the reps for it. Is there a reason for not using the between clause?
Another way of asking the same question: Show me the data that would have posted today if it had been posted 7 hours, 44 minutes, and 59 seconds later.
SELECT Posted_Date, Total_Payment FROM
Table1
WHERE CAST(DATEADD(second,7*60*60+44*60+59,Posted_Date) AS date) = CAST(GETDATE() as date)
This will smoothly handle fencepost issues, fractional seconds, and daylight savings, and it will be very fast with an index on CAST(DATEADD(second,7*60*60+44*60+59,Posted_Date) AS date)

Sql query for selecting entries with today's date

I have the following table in sql server 2008 (Called "act_events"):
As you notice some of the datetimes are formated: "yyyy-mm-dd" and some: "yyyy-dd-mm" for some weird reason I still haven't figured..
I have a query as follows:
SELECT * from act_events
WHERE '2013-07-30'>=(CAST(e_start as DATE)) AND
'2013-07-30'<=(CAST(e_end as DATE))
Where I want to select events only with today's date.
But I can't figure a way to select both formats..
I tries this query:
SELECT * from act_events
WHERE( #date1>=(CAST(e_start as DATE)) AND
#date2<=(CAST(e_end as DATE)) ) OR
( #date3>=(CAST(e_start as DATE)) AND
#date4<=(CAST(e_end as DATE)) )
But it only works for certain dates..
Would appreciate your answers.
Also, if there's a statement that will change all datetime to correct format I would love to hear.
Assuming the dates are indeed of type DateTime, what you could do in this case is to use dateadd and datediff.
Run these two statements:
-- Todays date, date part only
select dateadd(dd, 0, datediff(dd, 0, getdate()))
-- Tomorrows date, date part only
select dateadd(dd, 0, datediff(dd, 0, dateadd(dd, 1, getdate())))
Using those two, you can do this (Including edits, thanks #gvee)
select *
from act_events
where
e_start >= dateadd(dd, 0, datediff(dd, 0, getdate()))
and
e_end < dateadd(dd, 0, datediff(dd, 0, getdate()) + 1)
I should mention that getdate() is a built-in function in SQL Server. You could of course change this to a variable if you are using this in a stored procedure for example.
As a side note, the date in SQL Server is actually a number. The format that comes out when you look at it is for humans and humans are not to be trusted, right?
select *
from t
where
CONVERT(nvarchar(30), GETDATE(), 112)=substring(date_c,1,4)
+substring(date_c,6,2)
+substring(date_c,9,2)
or
CONVERT(nvarchar(30), GETDATE(), 112)=substring(date_c,1,4)
+substring(date_c,9,2)
+substring(date_c,6,2)
SQLFiddle demo

how to get records of previous day using tsql?

I need all the records from last day?
Hi
Select * from table1 where tabledate > getdate() -1
with this query, i need to run is exactly after midnight to get exact result. I need to run it in day time and get all the previous day's records.
In SQL Server 2005, this is generally the fastest way to convert a datetime to a date:
DATEADD(day, DATEDIFF(day, 0, yourDate), 0)
In your case, it's done only once, so the how doesn't really matter much. But it does give the following query.
Select
*
from
table1
where
tabledate >= DATEADD(day, DATEDIFF(day, 0, getDate()) - 1, 0)
AND tabledate < DATEADD(day, DATEDIFF(day, 0, getDate()), 0)
Check this page out. It is a great resource for calculating dates.
http://www.simple-talk.com/sql/learn-sql-server/robyn-pages-sql-server-datetime-workbench/#calculatingdates
Another method is to use DATEDIFF alone:
SELECT * FROM table1
WHERE DATEDIFF(DAY, tabledate, GETDATE()) = 1
A datediff of 1 for day covers any time in the previous day.
DECLARE #d SMALLDATETIME;
SET #d = DATEDIFF(DAY, 0, GETDATE());
SELECT <cols> FROM dbo.table1
WHERE tabledate >= DATEADD(DAY, -1, d)
AND tabledate < #d;
Try this:
your_field = cast(dateadd(D,-1,getdate()) as DATE)

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