SQL operand data type datetime / varchar is invalid for sum operator - sql

I am trying to subtract one column from another column, and then sum the result. However, whatever I change I keep getting one of the two errors as mentioned in the title.
From all other questions posted, I derived that my statement should be something like:
SELECT SUM(EndTime-BeginTime) AS TotalTime FROM TimeRegister
WHERE OrderNumber = 00000 AND Activity = 11111;
As per suggestion in another topic, I changed the statement to:
SELECT SUM(CONVERT(varchar(EndTime-BeginTime), 108)) AS TotalTime FROM TimeRegister
WHERE OrderNumber = 00000 AND Activity = 11111;
However, I still receive the error.
The times are stored as follows:
1-1-1900 7:30:00
Thanks for any suggestion...
Edit 1: Oh, I am using a Microsoft Query through ODBC for SQL in Excel, kind of like:
Excel vlookup incorporating SQL table
Edit 2: I prefer to have the output again in the HH:MM format.
The thing is, when I copy the complete database to excel, everything works just fine. I can subtract the columns and sum up. I just dont want to that manually every day....
I just checked the SQL database, and the column is of Date/Time type.

First of all, trying to solve date or numeric issues by converting to text never solves anything, it adds additional problems like failed conversions or unexpected results.
SQL Server doesn't have a time interval type. If you subtract two datetime values you get back another datetime value that represents the difference as the offset since 1900-01-01. The following query :
select getdate() - dateadd(d,-1,getdate())
Will return :
1900-01-02 00:00:00.000
Which is 1 full day after 1900-01-01.
datetime values can't be summed though. SQL Server does have a time type but that just represents the time of day. Even if today - yesterday worked, converting that to time would return 00:00.
A quick solution is to use DATEDIFF to calculate the difference between two dates in whatever unit is required - hourss, minutes, seconds etc. Be aware though that DATEDIFF returns the number of interval boundaries crossed. DATEDIFF(DAY,...) between 11pm yesterday and 1am today will return 1, because 1 day boundary was crossed.
You can use
SELECT sum(datediff(minute,EndTime,BeginTime)) AS TotalMinutes
FROM TimeRegister
WHERE OrderNumber = 00000 AND Activity = 11111;
To calculate the difference in minutes and format it as a time interval on the client.
Another option is to cast the datetime to a float before summing, then back to datetime:
SELECT cast( sum(cast(EndTime - BeginTime as datetime)) as datetime) AS TotalOffset
FROM TimeRegister
WHERE OrderNumber = 00000 AND Activity = 11111;
A 2-day duration would appear as :
1900-01-03 00:00:00.000
This works because datetime can be cast to a float whose integral part represents the offset from 1900-01-01 and the fractional part the time.
A client written in a language like C# that does support intervals could subtract 1900-01-01 from this to get back the actual duration, eg :
TimeSpan duration = sumResult - new DateTime(1900,1,1);
Another option would be to avoid the final cast and just use the resulting float value as the number of days.
Displaying in Excel
The last option could be the easiest way to display the duration in Excel! Excel dates are actually stored as floats. What makes them appear as dates or times is the cell's formatting.
You can set a cells format to [h]:mm:ss to display any number as a duration. The [] is important - without it Excel will only display the time part of the "date".
If you enter 2 in a cell, h:mm:ss will show it as 0:00:00 while [h]:mm:ss will display 48:00:00, 48 hours.
Despite its strangeness,
SELECT sum(cast(EndTime - BeginTime as datetime)) AS TotalDuration
FROM TimeRegister
WHERE OrderNumber = 00000 AND Activity = 11111;
May be the best option to display a duration sum in Excel

You seem to want datediff():
SELECT SUM(DATEDIFF(second, BeginTime, EndTime) AS TotalTime
FROM TimeRegister
WHERE OrderNumber = 00000 AND Activity = 11111;

How about the following:
WITH totalTime AS (
SELECT SUM(DATEDIFF(second, EndTime, BeginTime)) as TimeInSecond
FROM TimeRegister
WHERE OrderNumber = 00000 AND Activity = 11111;
)
SELECT RIGHT('0' + CAST(TimeinSecond / 3600 AS VARCHAR),2) + ':' +
RIGHT('0' + CAST((TimeinSecond / 60) % 60 AS VARCHAR),2) + ':' +
RIGHT('0' + CAST(TimeinSecond % 60 AS VARCHAR),2) AS TimeElapsed
FROM totalTime;
In this solution, the time is converted into seconds, before being totalled, and then converted to HH:MM:SS.

Related

SQL Creating a table for song duration

I am trying to create a table in SQL which is about Music, it should contain songDuration. Which means I gotta hold minutes:seconds information in the table. But i have no idea what to use for the type. I am using SQL server.
Edit: I want to use the database for an ASP.NET Core web application. I was using a ready-to-use SQL database like northwnd. Now, I am trying to create one. So, I will not see the timing with SELECT function in SQL query. So, I need to use something that makes it mm:ss otomaticly. Is there is a type that I can decleare like that?
create table musics(
songDuration type,
...)
Why just don't you use int?
So you could calculate duration in the way you like.
E.g. minutes,hours, etc.
There's a datatype time which would be the logical choice, that stores it in format HH:mm:ss with an optional amount of fractional seconds determined by the size you declare the field (e.g. time(3) holds it to three decimal places)
If your source data is already in this notation it makes it getting it in the table easy and simple sorting/filtering operates as you expect. The downside to doing this is if you want to do certain operations such as SUM or AVG (because as a_horse_with_no_name pointed out in their comment) time technically represents a point in time not a duration and you'd have to do some workaround like so:
SELECT totalduration = SUM(DATEDIFF(SECOND, '0:00:00', duration))
FROM dbo.table
Alternatively you could store the number of (say) seconds in the duration using an int, but if you don't already have the information in that format you'd have to do some (light) conversion when inserting the data, and then back if you want to display in mm:ss
e.g:
SELECT CONVERT(varchar, DATEADD(SECOND, durationinseconds, 0), 8) FROM dbo.table
which would convert it back to hh:mm:ss format.
I do this by using an int column, and store the seconds there.
In your client you can calculate from the seconds howmany days, hours, minutes and seconds it is and display it like you want.
To display it in sql you can use this for example
declare #seconds int = 350
select right('00' + convert(varchar, datepart(hour, dateadd(s, #seconds, 0)) + ((#seconds / 86400) * 24)), 2)
+ ':' + right('00' + convert(varchar, datepart(minute, dateadd(s, #seconds, 0))), 2)
+ ':' + right('00' + convert(varchar, datepart(second, dateadd(s, #seconds, 0))), 2)
this will display
00:05:50
which is 0 hours, 5 minutes and 50 seconds
IF the value of seconds is larger than a day, this will be no problem. The number of hours will simply be greater
a value of 350000 will display 97:13:20

TIme - SQL (Minutes and Seconds)

I created a TIME table. This table has two columns: one for minutes and another one for seconds. I made their datatype as a Decimal.
Is there a way to create a derivative column where minutes and seconds are in this format mm:ss from my two columns?
IF NOT, How do I insert data into my minute column if its not a DECIMAL type? What type should it be?
Thank you!
Note I am using SQL server
Your comments make it sound like you're trying to do arithmetic on intervals (or durations).
SQL Server's time data type "Defines a time of a day. The time is without time zone awareness and is based on a 24-hour clock." You can't add two time values; 2 o'clock + 3 o'clock is literally nonsense. In SQL Server 2012 . . .
select cast('2:00' as time) + cast('3:00' as time)
Operand data type time is invalid for add operator.
Other dbms might return a nonsensical number.
Standard SQL includes a data type called interval, which supports the arithmetic and formatting you'd expect. So 2 o'clock + 3 hours would return 5:00:00 (5 o'clock). In the absence of support for the interval data type, store the most granular unit (seconds, for you) as an integer, and format it yourself for display. I might use a view, myself.
declare #val as integer;
-- 10:01:12, 10 hours, 1 minute, 12 seconds, in seconds.
set #val = (10 * 60 * 60) + (1 * 60) + 12;
-- Leading zeroes for minutes and seconds.
select #val as total_sec,
concat(#val / (60 * 60), ':', format((#val / 60) % 60, 'D2'), ':', format(#val % 60, 'D2')) as total_time
total_sec total_time
--
36072 10:01:12
As #mclaassen pointed out, I'd be curious why you aren't using the built in time data type.
That said, if you really want to build a time table by hand, then you can have a calculated column. Let's call it timeString.
alter table [time]
add timeString as (left('0' + cast([minutes] as varchar(10)), 2) + ':' + left('0' + cast([seconds] as varchar(10)), 2))
See https://msdn.microsoft.com/en-us/library/ms188300.aspx for documentation on calculated columns in SQL Server.

SQL Server : Comparing Time

I am trying to compare time in my SQL query. However, when I run the query, I get zero result but I can see that in the table, there are records that should appear.
The query is as such:
SELECT *
FROM dbo.Alarms
WHERE StartDate <= '26/08/2015'
AND StartTime <= CONVERT(varchar(5), GETDATE(), 108)
The StartDate is stored in the database as YYYY-MM-DD and it seems to work fine when I query only with the date.
However, when I add the StartTime is when things don't work. StartTime stores the value in the 24 hour clock format.
What am not doing right?
Thanks
Use a correct datetime format:
SELECT *
FROM dbo.Alarms
WHERE StartDate <= '2015-08-26' AND StartTime <= cast(GETDATE() as date)
Don't compare date/time values as strings. The data types are built into the language. Use them.
I have not explicitly used this scenario but comparing dates can be a problem depending on how the fields are compared.
eg: '28/07/2015' is not less than your startdate as 28 > 26.
You could try comparing dates reformatted into a YYYYMMDD format.
Cheers.

Error message in SQL Server: Conversion failed when converting date and/or time from character string

I need to calculate the total time duration in terms of Hours, Minutes, Seconds given some data with logintime and logouttime.
For example the result must be something like "01:44:19". We're using SQL Server 2008
Any idea of how I could do this?
For information, the columns in the table are int_PK_Id,'dtm_Date', dtm_Login_Time, dtm_Logout_Time, dtm_Duration etc.
column Name datatype
------------ ---------------------
dtm_Date date
dtm_Login_Time time(7)
dtm_Logout_Time time(7)
dtm_Duration time(7)
And I wrote this statement:
update PS_Time_Sheet
set dtm_Duration = convert(varchar(5), DateDiff(s,((convert(datetime,[dtm_Date] ) + convert(datetime,dtm_Login_Time))),getdate())/3600)
+':'+ convert(varchar(5),DateDiff(s,((convert(datetime,[dtm_Date] ) + convert(datetime,dtm_Login_Time))),getdate())%3600/60)
+':'+convert (varchar(5), DateDiff(s,((convert(datetime,[dtm_Date]) + convert(datetime,dtm_Login_Time))),getdate())%60)
where int_PK_Id =2
Assuming 'dtm' means DateTime, you can just minus one from the other, eg
select logOutDate - logInDate
If your duration column just holds this, I'd suggest either changing it into a computed column on the table, or use a view which performs the above. There is no need to store such a derivative, and can lead to problems as you have to ensure it is correct on changes, etc.

convert Excel Date Serial Number to Regular Date

I got a column called DateOfBirth in my csv file with Excel Date Serial Number Date
Example:
36464
37104
35412
When i formatted cells in excel these are converted as
36464 => 1/11/1999
37104 => 1/08/2001
35412 => 13/12/1996
I need to do this transformation in SSIS or in SQL. How can this be achieved?
In SQL:
select dateadd(d,36464,'1899-12-30')
-- or thanks to rcdmk
select CAST(36464 - 2 as SmallDateTime)
In SSIS, see here
http://msdn.microsoft.com/en-us/library/ms141719.aspx
The marked answer is not working fine, please change the date to "1899-12-30" instead of "1899-12-31".
select dateadd(d,36464,'1899-12-30')
You can cast it to a SQL SMALLDATETIME:
CAST(36464 - 2 as SMALLDATETIME)
MS SQL Server counts its dates from 01/01/1900 and Excel from 12/30/1899 = 2 days less.
tldr:
select cast(#Input - 2e as datetime)
Explanation:
Excel stores datetimes as a floating point number that represents elapsed time since the beginning of the 20th century, and SQL Server can readily cast between floats and datetimes in the same manner. The difference between Excel and SQL server's conversion of this number to datetimes is 2 days (as of 1900-03-01, that is). Using a literal of 2e for this difference informs SQL Server to implicitly convert other datatypes to floats for very input-friendly and simple queries:
select
cast('43861.875433912' - 2e as datetime) as ExcelToSql, -- even varchar works!
cast(cast('2020-01-31 21:00:37.490' as datetime) + 2e as float) as SqlToExcel
-- Results:
-- ExcelToSql SqlToExcel
-- 2020-01-31 21:00:37.490 43861.875433912
this actually worked for me
dateadd(mi,CONVERT(numeric(17,5),41869.166666666664)*1440,'1899-12-30')
(minus 1 more day in the date)
referring to the negative commented post
SSIS Solution
The DT_DATE data type is implemented using an 8-byte floating-point number. Days are represented by whole number increments, starting with 30 December 1899, and midnight as time zero. Hour values are expressed as the absolute value of the fractional part of the number. However, a floating point value cannot represent all real values; therefore, there are limits on the range of dates that can be presented in DT_DATE. Read more
From the description above you can see that you can convert these values implicitly when mapping them to a DT_DATE Column after converting it to a 8-byte floating-point number DT_R8.
Use a derived column transformation to convert this column to 8-byte floating-point number:
(DT_R8)[dateColumn]
Then map it to a DT_DATE column
Or cast it twice:
(DT_DATE)(DT_R8)[dateColumn]
You can check my full answer here:
Is there a better way to parse [Integer].[Integer] style dates in SSIS?
Found this topic helpful so much so created a quick SQL UDF for it.
CREATE FUNCTION dbo.ConvertExcelSerialDateToSQL
(
#serial INT
)
RETURNS DATETIME
AS
BEGIN
DECLARE #dt AS DATETIME
SELECT #dt =
CASE
WHEN #serial is not null THEN CAST(#serial - 2 AS DATETIME)
ELSE NULL
END
RETURN #dt
END
GO
I had to take this to the next level because my Excel dates also had times, so I had values like this:
42039.46406 --> 02/04/2015 11:08 AM
42002.37709 --> 12/29/2014 09:03 AM
42032.61869 --> 01/28/2015 02:50 PM
(also, to complicate it a little more, my numeric value with decimal was saved as an NVARCHAR)
The SQL I used to make this conversion is:
SELECT DATEADD(SECOND, (
CONVERT(FLOAT, t.ColumnName) -
FLOOR(CONVERT(FLOAT, t.ColumnName))
) * 86400,
DATEADD(DAY, CONVERT(FLOAT, t.ColumnName), '1899-12-30')
)
In postgresql, you can use the following syntax:
SELECT ((DATE('1899-12-30') + INTERVAL '1 day' * FLOOR(38242.7711805556)) + (INTERVAL '1 sec' * (38242.7711805556 - FLOOR(38242.7711805556)) * 3600 * 24)) as date
In this case, 38242.7711805556 represents 2004-09-12 18:30:30 in excel format
In addition of #Nick.McDermaid answer I would like to post this solution, which convert not only the day but also the hours, minutes and seconds:
SELECT DATEADD(s, (42948.123 - FLOOR(42948.123))*3600*24, dateadd(d, FLOOR(42948.123),'1899-12-30'))
For example
42948.123 to 2017-08-01 02:57:07.000
42818.7166666667 to 2017-03-24 17:12:00.000
You can do this if you just need to display the date in a view:
CAST will be faster than CONVERT if you have a large amount of data, also remember to subtract (2) from the excel date:
CAST(CAST(CAST([Column_With_Date]-2 AS INT)AS smalldatetime) AS DATE)
If you need to update the column to show a date you can either update through a join (self join if necessary) or simply try the following:
You may not need to cast the excel date as INT but since the table I was working with was a varchar I had to do that manipulation first. I also did not want the "time" element so I needed to remove that element with the final cast as "date."
UPDATE [Table_with_Date]
SET [Column_With_Excel_Date] = CAST(CAST(CAST([Column_With_Excel_Date]-2 AS INT)AS smalldatetime) AS DATE)
If you are unsure of what you would like to do with this test and re-test! Make a copy of your table if you need. You can always create a view!
Google BigQuery solution
Standard SQL
Select Date, DATETIME_ADD(DATETIME(xy, xm, xd, 0, 0, 0), INTERVAL xonlyseconds SECOND) xaxsa
from (
Select Date, EXTRACT(YEAR FROM xonlydate) xy, EXTRACT(MONTH FROM xonlydate) xm, EXTRACT(DAY FROM xonlydate) xd, xonlyseconds
From (
Select Date
, DATE_ADD(DATE '1899-12-30', INTERVAL cast(FLOOR(cast(Date as FLOAT64)) as INT64) DAY ) xonlydate
, cast(FLOOR( ( cast(Date as FLOAT64) - cast(FLOOR( cast(Date as FLOAT64)) as INT64) ) * 86400 ) as INT64) xonlyseconds
FROM (Select '43168.682974537034' Date) -- 09.03.2018 16:23:28
) xx1
)
For those looking how to do this in excel (outside of formatting to a date field) you can do this by using the Text function https://exceljet.net/excel-functions/excel-text-function
i.e.
A1 = 132134
=Text(A1,"MM-DD-YYYY") will result in a date
This worked for me because sometimes the field was a numeric to get the time portion.
Command:
dateadd(mi,CONVERT(numeric(17,5),41869.166666666664)*1440,'1899-12-31')