I have a data request - SQL Server 2014 (fourth column). The application does not handle decimals. So I have to provide the data without the decimals. For example: If an employee worked 8 hours, I am sending 8.00. The upstream application wants the data as 800. Or if its less than 8 hours then 050 (.50) for half hour.
My calculation for two weeks of work is:
CONVERT(decimal(10, 2), dur) / 60 AS HOURS
My question is: how do I strip that decimal yet maintain the correct hours such that 8.00 becomes 800?
You could simply convert the hours to a string and then replace the decimal with nothing. If necessary, convert the string without the decimal to an integer.
DECLARE #Hours DECIMAL(10, 2)
SET #Hours = 8.50
SELECT
#Hours AS [Hours]
, REPLACE(CONVERT(VARCHAR(13), #Hours), '.', '') AS [Hours as String]
, CONVERT(INT, REPLACE(CONVERT(VARCHAR(13), #Hours), '.', '')) AS [Hours as Integer]
Since your application is expecting leading zeros on values under an hour, you're going to have to pad that out. However, since there can be values over 10 hours, we need to treat those a little differently to account for the extra characters in the end result.
This seems to give the values you're looking for.
Setup:
create table t
(tm decimal(18,5));
insert t values (8.05999),(11.00000),(0.34111);
Query:
select
case
when t.tm > 10
then cast(cast(t.tm*100 as integer) as varchar(5)) --<-Handles longer shifts
else right(concat('000',cast(t.tm*100 as integer)),3) --<-Handles shorter shifts
end as appVal
from t;
Results:
+--------+
| appVal |
+--------+
| 805 |
| 1100 |
| 034 |
+--------+
Replaced the decimal sign as suggested by #isaac and #eric-brandt. Then only populated the number of values the upstream application needed.
LEFT(REPLACE((CONVERT(decimal(10, 2), dur) / 60),'.',''),3) AS HOURS,
Related
I'm working on a legacy database and need to parse info from one database to another, parsing it into the new database is easy enough but first I need to create the query to convert and cast the following in the legacy SQL Server database:
WorkedHours(NVARCHAR(10)) is in text format 07:30
I need to convert and cast this as a decimal ie 7.5
I have searched around for the answer to this but can not find anything that has worked, so thought I would put it out there to see if any of you has any ideas.
Edit - What I should of asked is. What is causing an error converting to an int from a character with a value of 0 when trying to trying to convert and cast a time to a decimal?
DATEDIFF(
MINUTE,
0,
CAST('07:30' AS TIME)
)
/
60.0
Works up to '23:59' only
EDIT:
Based on a comment elsewhere, you have some 'bad' values.
This may find them...
SELECT
*
FROM
yourTable
WHERE
TRY_CONVERT(TIME, worked_hours) IS NULL
And as such, this is a safer version of my expression....
DATEDIFF(
MINUTE,
0,
TRY_CONVERT(TIME, worked_hours)
)
/
60.0
(Returns NULL for values that failed to parse.)
There's no reason to pull out the date/time types. Just do some simple string parsing:
cast(left(right('0' + WorkedHours, 5), 2) as int)
+ cast(right(WorkedHours, 2) as int) / 60.00
This won't have any limitations on 24 hours or anything like that. It just assumes that you've got one or two digits before a colon and two digits after.
This should work in SQL Server and an example-string "1101:56" (1101h & 56 minutes) | in general from 0h to >24h:
-- Take all hours before ":" and all Minutes (2 digits) after ":" and convert it to decimal.
select convert(decimal,left('1101:56',CHARINDEX(':','1101:56')-1)) + ( convert(decimal,right('1101:56',2))/60 );
-- with column-placeholder "time_str_from_table"
select convert(decimal,left(time_str_from_table,CHARINDEX(':',time_str_from_table)-1)) + ( convert(decimal,right(time_str_from_table,2))/60 );
If the source table have NULL-Values, than use "ISNULL" with substitution-value "0.0":
-- with column-placeholder "time_str_from_table"
select isnull( ( convert(decimal,left(time_str_from_table,CHARINDEX(':',time_str_from_table)-1)) + ( convert(decimal,right(time_str_from_table,2))/60) ), 0.0);
I've got a column that shows the date as a decimal such as 101118 for 10-11-18 and 90118 for 09-01-18. I am trying to create a simple report that would give me all reservations yesterday.
So for example
Select playerid, datereservationmade
from dbo.lms
normally there is very simple and I would just do
Select playerid, datereservationmade
from dbo.lms
where datereservationmade >= dateadd(day,datediff(day,1,GETDATE()),0)
AND datereservationmade < dateadd(day,datediff(day,0,GETDATE()),0)
That does not work in this case because the datereservationmade field is a decimal and if its a month 1-9 it leaves off the 0 and makes it a 5 digit decimal then if its 10-12 it is a 6 digit decimal.
Someone please help me figure out how to convert this!
If at all possible, you really should fix your schema so that dates are actually being stored as dates.
If you need to work with the decimal data type, you can use something like the following...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
BEGIN DROP TABLE #TestData; END;
CREATE TABLE #TestData (
decimal_date DECIMAL(6, 0) NOT NULL
);
INSERT #TestData (decimal_date) VALUES (101118), (90118), (101718);
--==============================================
SELECT
td.decimal_date,
dd.date_date
FROM
#TestData td
CROSS APPLY ( VALUES (RIGHT('0' + CONVERT(VARCHAR(6), td.decimal_date), 6)) ) cd (char_date)
CROSS APPLY ( VALUES (CONVERT(DATE, STUFF(STUFF(cd.char_date, 5, 0, '/'), 3, 0, '/'), 1)) ) dd (date_date)
WHERE
dd.date_date = CONVERT(DATE, DATEADD(DAY, -1, GETDATE()));
Convert the decimal to varchar(6) by adding a zero in front and getting the RIGHT 6 characters.
Then convert the string to a date from its parts, which are substrings in your varchar(6). This is made easier in SQL Server 2012 with the DATEFROMPARTS function.
Using the DATEFROMPARTS, as Tab Alleman suggested, you might get something like this:
-- Example of the arithmetic
SELECT 101118 / 10000 AS Month, (101118 % 10000) / 100 AS Day, (101118 % 100) AS Year
-- Using the math in DATEFROMPARTS
SELECT DATEFROMPARTS((101118 % 100) + 2000, 101118 / 10000, (101118 % 10000) / 100 )
However, I'm skeptical that you've provided all the correct information. What happens on January first? Your decimal value won't start with zero (as you stated). Will your day always pad with zero? If not, then 1119 won't produce the same result as 10119. If, however, your day does start with zero, then the equation above should work fine.
In a legacy system (SQL Server 2005) I have a column that stores a 5 digit integer (ie 86340) as time. The legacy application shows 86340 as 23:59:00. I am unsure how how to translate that 5 digit integer into a date-time data type using SQL.
SQL Server 2012+ has TIMEFROMPARTS function:
TIMEFROMPARTS ( hour, minute, seconds, fractions, precision )
Returns a time value for the specified time and with the specified precision.
Which is similiar to Excel's TIME:
TIME(hour, minute, second)
Excel version could handle values over 0-60 range:
Minute Required. A number from 0 to 32767 representing the minute. Any value greater than 59 will be converted to hours and minutes.
And SQL counterpart cannot do that.
It looks like that value is simply number of seconds so you could use:
DECLARE #A1 INT = 86340;
SELECT DATEADD(second, #A1,CAST('00:00:00' AS TIME));
DBFiddle Demo
EDIT:
As SQL Server 2005 does not support TIME data type, you could use DATETIME instead and skip date part in application.
DECLARE #A1 INT = 86340;
SELECT DATEADD(second, #A1,CAST('00:00:00' AS DATETIME));
DBFiddle Demo2
Since the value you have is an integer representation of the seconds since midnight, you have a couple of choices in SQL Server 2005. You can either render the value as a VARCHAR, which is readable, you can render it as DATETIME, which appends the date information, or you can maybe pull in a date from another field to get an meaningful DATETIME for your value.
SELECT CONVERT(VARCHAR(12), DATEADD(SECOND, 86340, 0), 114) AS [InVarchar];
+--------------+
| InVarchar |
+--------------+
| 23:59:00:000 |
+--------------+
SELECT DATEADD(SECOND, 86340, 0) AS [InDatetime];
+-------------------------+
| InDatetime |
+-------------------------+
| 1900-01-01 23:59:00.000 |
+-------------------------+
SELECT DATEADD(SECOND, 86340, CAST('2018-09-05' AS DATETIME)) AS [InDatetimeWithDate];
+-------------------------+
| InDatetimeWithDate |
+-------------------------+
| 2018-09-05 23:59:00.000 |
+-------------------------+
USE below query:
SELECT CONVERT(DATETIME, #Column_Name)
FROM Table;
I am selecting a date column which is in the format "YYYY-MM-DD".
I want to cast it to a timestamp such that it will be "YYYY-MM-DD HH:MM:SS:MS"
I attempted:
select CAST(mycolumn as timestamp) from mytable;
but this resulted in the format YYYY-MM-DD HH:MM:SS
I also tried
select TO_TIMESTAMP(mycolumn,YYYY-MM-DD HH:MM:SS:MS) from mytable;
but this did not work either. I cannot seem to figure out the correct way to format this. Note that I only want the first digit of the milliseconds.
//////////////second question
I am also trying to select numeric data such that there will not be any trailing zeros.
For example, if I have values in a table such as 1, 2.00, 3.34, 4.50.
I want to be able to select those values as 1, 2, 3.34, 4.5.
I tried using ::float, but I occasionally strange output. I also tried the rounding function, but how could I use it properly without knowing how many decimal points I need before hand?
thanks for your help!
It seems that the functions to_timestamp() and to_char() are unfortunately not perfect.
If you cannot find anything better, use these workarounds:
with example_data(d) as (
values ('2016-02-02')
)
select d, d::timestamp || '.0' tstamp
from example_data;
d | tstamp
------------+-----------------------
2016-02-02 | 2016-02-02 00:00:00.0
(1 row)
create function my_to_char(numeric)
returns text language sql as $$
select case
when strpos($1::text, '.') = 0 then $1::text
else rtrim($1::text, '.0')
end
$$;
with example_data(n) as (
values (100), (2.00), (3.34), (4.50))
select n::text, my_to_char(n)
from example_data;
n | my_to_char
------+------------
100 | 100
2.00 | 2
3.34 | 3.34
4.50 | 4.5
(4 rows)
See also: How to remove the dot in to_char if the number is an integer
SELECT to_char(current_timestamp, 'YYYY-MM-DD HH:MI:SS:MS');
prints
2016-02-05 03:21:18:346
just add ::timestamp without time zone
select mycolumn::timestamp without time zone from mytable;
I'm working on a query with a varchar column called ALCOHOL_OZ_PER_WK. Part of the query includes:
where e.ALCOHOL_OZ_PER_WK >= 14
and get the errors:
Arithmetic overflow error converting varchar to data type numeric.
and:
Error converting data type varchar to numeric.
Looking into the values actually stored in the column, the largest look close to 100, but some of the entries are ranges:
9 - 12
1.5 - 2.5
I'd like to get the upper limit (or maybe the midpoint of the range) from rows with entries like this and have it be the value being compared to 14.
What would be the (or an) easy way to do this?
As always, thank you!
Your DB is obviously result of some survey, and it seems to contain the original survey data. The usual way is to run this through an ECCD (Extract, Clean, Conform, Deliver) process and store clean and standardized data into a separate database (maybe a warehouse) which can then be used for analytics and reporting.
If you have SSIS use data profiling task to get an idea of types of strings you have in there. The Column Pattern Profile reports a set of regular expressions on the string column, so you will get an idea of what's inside those strings. If you do not have SSIS, you can use eobjects DataCleaner to do the same.
If you can not spare a new database or at least a new table -- at minimum add a numeric column to this table and then extract numeric values form those strings into the new column. You may want to use "something else" (SSIS, Pentaho Kettle, Python, VB, C#) to do this -- in general T-SQL in not very good at string processing.
My guess is that this is not the only column that has garbage inside, so any analysis that you may run on this may be worthless.
And if you still think that the ranges are the only problem, this example may help:
First some data
DECLARE #myTable TABLE (
AlUnits varchar(10)
) ;
INSERT INTO #myTable
(AlUnits )
VALUES ( '10' )
, ( '15' )
, ( '20' )
, ( '7 - 12' )
, ( '3 - 5' )
;
The query splits records into two groups, numeric and not numeric -- assumed ranges.
;
WITH is_num
AS ( SELECT CAST(AlUnits AS decimal(6, 2)) AS Units_LO
,CAST(AlUnits AS decimal(6, 2)) AS Units_HI
FROM #myTable
WHERE ISNUMERIC(AlUnits) = 1
),
is_not_num
AS ( SELECT CAST( RTRIM(LTRIM(LEFT(AlUnits,
CHARINDEX('-', AlUnits) - 1)))
AS decimal(6,2)) AS Units_LO
,CAST(RTRIM(LTRIM(RIGHT(AlUnits,
LEN(AlUnits)
- CHARINDEX('-', AlUnits))))
AS decimal(6,2)) AS Units_HI
FROM #myTable
WHERE ISNUMERIC(AlUnits) = 0
)
SELECT Units_LO
,Units_HI
,CAST(( Units_LO + Units_HI ) / 2.0 AS decimal(6, 2)) AS Units_Avg
FROM is_num
UNION ALL
SELECT Units_LO
,Units_HI
,CAST(( Units_LO + Units_HI ) / 2.0 AS decimal(6, 2)) AS Units_Avg
FROM is_not_num ;
Returns:
Units_LO Units_HI Units_Avg
----------- ----------- ----------
10.00 10.00 10.00
15.00 15.00 15.00
20.00 20.00 20.00
7.00 12.00 9.50
3.00 5.00 4.00
Not sure about easy ways.
A proper way is to store the numbers in two columns, ALCOHOL_OZ_PER_WK_MIN and ALCOHOL_OZ_PER_WK_MAX.
As you say you need to calculate numeric values, which you can then use in your query.
Probably the easiest way is to use some simple logic to calculate the average or upper limit using string functions, and string to numeric functions.
If all you want is the upper limit, just get the characters after the '-' and use that.
"probably because some are ranges" - do you get that "range" is not a SQL Server Data type? You've got non-numeric data you're trying to convert into numeric data, and you've got a scalar value you're comparing to a non-scalar value.
This database has some issues.