I need to combine two fields but force the characters of the second string to be 2 characters.
I'm combining a year field and month field and want the result to be YYYY_MM. Forcing any single months (e.g. 1,2,3,4) into a two digit format e.g. (01).
Below is my formula for combining the fields, but I need help making the month two digits.
Thanks, L
WITH so_header(soh_build_year,soh_build_week) AS (
SELECT 2020, 3
UNION ALL SELECT 2020,13
)
SELECT
CAST(SO_HEADER.SOH_Build_Year AS VARCHAR)
+'_'
+CAST(SO_HEADER.SOH_Build_Week AS VARCHAR) as [Build YYYY_WW]
FROM so_header;
Try this out (Syntax: SQL Server)
SELECT
CAST(2019 AS VARCHAR)
+'_'
+CAST(format (1, '0#') AS VARCHAR) as [Build YYYY_WW]
Replace your values with your variables
Try this:
WITH so_header(soh_build_year,soh_build_week) AS (
SELECT 2020, 3
UNION ALL SELECT 2020,13
)
SELECT
CAST(SO_HEADER.SOH_Build_Year AS VARCHAR)
+ '_'
+ SUBSTR(
CAST(100+SO_HEADER.SOH_Build_Week AS VARCHAR)
, 2
, 2
) as Build_YYYY_WW
FROM so_header;
-- out Build_YYYY_WW
-- out ---------------
-- out 2020_03
-- out 2020_13
If you are using SQL Server never use varchar (or related types) with no length. The default varies by context and may not be large enough for what you want.
If you are trying to convert a date to YYYY_MM format, you can use format():
select format(getdate(), 'yyyy_MM')
I recommend using dates, if they are available. If you are not using SQL Server, most other databases have similar functionality.
If not, you an simply use:
select concat(so_header.SOH_Build_Year, '_'
right(concat('00', so_header.soh_build_week), 2)
)
concat() does not require explicitly converting the values to strings.
Related
I have a requirement where i have to pull the date/time value from string but the problem is that they can be different formats because of which substring becomes more complicated.
Here's what i came up with but is there any other method where i could simply retreive dates of different format with time and convert them all in single format?
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp (
comments varchar(500)
)
insert into #temp (comments)
(
select 'Mailed on 1/1/22 at 5 pm'
union
select 'Mailed on 01/2/2222 # 6 am'
union
select 'Mailed on 01/2/22 in night'
union
select 'Mailed on 1/02/2222 at 4 pm'
union
select 'Mailed on 1/1/2222 at 4 pm'
);
select *
from #temp
cross apply (select PATINDEX('%Mailed On%',comments) as start_pos) as start_pos
cross apply (select case when substring(comments,patindex('%Mailed On%',comments)+9,11) like '%[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]%' then 1
when substring(comments,patindex('%Mailed On%',comments)+9,8) like '%[0-9][0-9]/[0-9]/[0-9][0-9]%' then 2
when substring(comments,patindex('%Mailed On%',comments)+9,10) like '%[0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]%' then 3
when substring(comments,patindex('%Mailed On%',comments)+9,9) like '%[0-9][0-9]/[0-9][0-9]/[0-9][0-9]%' then 4
when substring(comments,patindex('%Mailed On%',comments)+9,9) like '%[0-9]/[0-9]/[0-9][0-9][0-9][0-9]%' then 5
when substring(comments,patindex('%Mailed On%',comments)+9,7) like '%[0-9]/[0-9]/[0-9][0-9]%' then 6 else null end as substr) as substr
--cross apply (select case when substring(authcomments,start_pos + 9, 11) like '%[1-9]/[0123][0-9]/[0-9][0-9][0-9][0-9]%' then 1 else null end as substr) as substr
cross apply (select case when substr = 1 then substring(comments,patindex('%Mailed On%',comments)+9,11)
when substr = 2 then substring(comments,patindex('%Mailed On%',comments)+9,8)
when substr = 3 then substring(comments,patindex('%Mailed On%',comments)+9,10)
when substr = 4 then substring(comments,patindex('%Mailed On%',comments)+9,9)
when substr = 5 then substring(comments,patindex('%Mailed On%',comments)+9,9)
when substr = 6 then substring(comments,patindex('%Mailed On%',comments)+9,7)
else null end as maileddate
) as maileddate
#user1672315 ,
Sometimes you get stuff like this and in order to fix it so that you can get the dates and times to store in a table or whatever, ya gotta do what ya gotta do to get it and, contrary to the comments, it certainly CAN be done in SQL. It's just not that difficult. Ya just gotta know some of the "gazintas" ;)
So, using the readily consumable test data that you were nice enough to provide, run the following code against it...
SELECT t.*
,TheDateAndTime = DATEADD(hh,ca4.cHour,ca3.cDate)
FROM #temp t
CROSS APPLY(VALUES(SUBSTRING(comments,PATINDEX('%[0-9]%',comments),500))) ca1(DT)
CROSS APPLY(VALUES(SUBSTRING(ca1.dt,PATINDEX('% [0-9]%',ca1.dt),500))) ca2(TM)
CROSS APPLY(VALUES(TRY_CONVERT(DATETIME,SUBSTRING(ca1.DT,1,PATINDEX('%[0-9] %',ca1.DT))))) ca3(cDate)
CROSS APPLY(VALUES(IIF(ca2.TM LIKE '%night%',23,DATEPART(hh,TRY_CONVERT(DATETIME,ca2.TM)))))ca4(cHour)
;
... and see that you CAN do it in SQL... BUT, see the warnings below the graphic below.
You also need to figure out what hour "night" is going to be assigned. I assigned "23" as the hour.
Results are as follows:
I'm thinking that your "2222" years are in error, though. :D
One thing I do agree on is that the format needs to be somewhat consistent. No code in the world, Python or otherwise, will be able to distinguish between a mm-dd-yy and dd-mm-yy format when dd and mm are both less than 13. The code I posted assumes (m)m-(d)d-yy and is based on the current LANGUAGE and DATEFORMAT that I'm using. It WILL return NULLs where the mm part isn't between 1 and 12 or if the dd part isn't between 1 and 31 or if the date is an "illegal date" like 2/29/2021, etc, though.
It also assumes that the format will always contain the numeric date as the first set of numeric values it comes across and that the time will always be the last thing in the string. We can add more checks, if needed but, like I said, unless mm is >=13, it cannot (nor can anything else) determine if it should be mm-dd-yy or dd-mm-yy because there's simply no other information in the string to indicate which format is being used. You MUST check your date format to use this, as well. If the strings are supposed to be in the dd-mm-yy format, we may have to make a change (although I believe SQL server will auto-magically accommodate that if the DATEFORMAT matches the intention of the string).
I am using Sybase IQ and have the following stored as a varchar:
01October 2010
I want to convert this from varchar to date datatype with the following format:
yyyy-mm-dd eg.2010-10-01
How would I write this SQL statement? Thanks in advance.
With difficulty. There's a reason you should never store dates and times as strings.
It's been awhile since I've used Sybase, but what we need to do is get the field into YYYY-MM-DD format, and then pass it to the DATE() or DATETIME() function.
Let's assume the first two characters are always the day of the month, and the last 4 characters are the year. That means everything in between is the month. Let's also assume that there are no leading or trailing spaces. If either of these assumptions fails, then the query will fail.
Then you can do something like this:
SELECT DATE (
RIGHT(UnnamedField,4) + '-' +
CASE LTRIM(RTRIM(SUBSTRING(Unnamed,3,LEN(Unnamed) - 6)))
WHEN 'January' THEN '01'
WHEN 'February' THEN '02'
WHEN 'March' THEN '03'
.
.
.
WHEN 'December' THEN '12'
END + '-' + LEFT(UnnamedField,2)
)
FROM UnnamedTable
Note that, as others have mentioned, the date data type is not a formatted datatype. If possible you should format it in your application. If you must do it in the query, use the CONVERT() function.
Sybase is able to convert a string to a date. So if you use substring to extract the date into a format that IQ can convert, then you can just use the convert() function.
Here's an example of how to do it:
Sample data:
create table #tmp1 (col1 varchar(100))
insert #tmp1 values ('01October 2010')
Query to convert the value to a date:
select
convert
(
date,
(
substring(col1, 3, charindex(' ', col1) - 2) -- Month
+ substring(col1, 1, 2) -- Day
+ substring(col1, charindex(' ', col1), 5) -- Year (include the leading space)
)
)
from #tmp1
Now that the value is in a date format, you can use the convert function to convert the date datatype to string, using your specified format. The default output for a date datatype is yyyy-mm-dd already.
Edit: After taking a look at #BaconBits' answer, I've realized that I could simplify the query a bit by using the substring function wrappers left, right, and the convert wrapper of date. It's the same logic; but using the simplified wrappers might make it easier to understand.
select
date
(
substring(col1, 3, charindex(' ', col1) - 2) -- Month
+ left(col1, 2) -- Day
+ ' ' + right(col1, 4) -- Year (include the leading space)
)
from #tmp1
I have a table with 2 columns: Customer_ID, which is a string, identifying each client and Time_id: a string with 14 characters, identifying timestamp of a transaction. Example:
Customer_id; Time_id
12345; 20140703144504
I want to be able to use datediff in hours datepart, but I can´t seem to be able to convert time_id properly. I use the following query:
update transation_table
set time_id= (
convert(timestamp, time_id)
)
It works, but removes hours datepart, which is what I need. For day datepart I can do it, converting to datetime. How can I keep in the table the hh?
edit: I´m running MS SQL Server 2014.
best regards
Using the convert and string concatenation below, you can use DATEPART on the resulting value.
DECLARE #tmp TABLE(
Customer_id VARCHAR(50),
Time_id VARCHAR(50)
)
INSERT INTO #tmp
SELECT '12345','20140703144504'
select
*,CONVERT(DATETIME,
SUBSTRING(Time_id,5,2) + '/' +
SUBSTRING(Time_id,7,2) + '/' +
SUBSTRING(Time_id,1,4) + ' ' +
SUBSTRING(Time_id,9,2) + ':' +
SUBSTRING(Time_id,11,2) + ':' +
SUBSTRING(Time_id,13,2)
,101
)
from #tmp
Use FORMAT to get a string representation of the value in a supported format (ODBC canonical in the Date and Time styles chart), then use TRY_CONVERT to return an actual datetime value:
SELECT TRY_CONVERT(DATETIME,
FORMAT(CAST('20140703144504' AS BIGINT),
'####-##-## ##:##:##'),
120);
This requires SQL Server 2012+.
As mentioned elsewhere, the data should be stored in a single datetime2 column, or paired date and time columns. The above functions can be used to help convert existing data to the new column(s).
I'm building a query against a DB2 database, connecting through the IBM Client Access ODBC driver. I want to pull fields that are less than 6 days old, based on the field 'a.ofbkddt'... the problem is that this field is not a date field, but rather a DECIMAL field, formatted as YYYYMMDD.
I was able to break down the decimal field by wrapping it in a call to char(), then using substr() to pull the year, month and day fields. I then formatted this as a date, and called the days() function, which gives a number that I can perform arithmetic on.
Here's an example of the query:
select
days( current date) -
days( substr(char(a.ofbkddt),1,4) concat '-' -- YYYY-
concat substr(char(a.ofbkddt),5,2) concat '-' -- MM-
concat substr(char(a.ofbkddt),7,2) ) as difference, -- DD
a.ofbkddt as mydate
from QS36F.ASDF a
This yields the following:
difference mydate
2402 20050402
2025 20060306
...
4 20110917
3 20110918
2 20110919
1 20110920
This is what I expect to see... however when I use the same logic in the where clause of my query:
select
days( current date) -
days( substr(char(a.ofbkddt),1,4) concat '-' -- YYYY-
concat substr(char(a.ofbkddt),5,2) concat '-' -- MM-
concat substr(char(a.ofbkddt),7,2) ) as difference, -- DD
a.ofbkddt as mydate
from QS36F.ASDF a
where
(
days( current date) -
days( substr(char(a.ofbkddt),1,4) concat '-' -- YYYY-
concat substr(char(a.ofbkddt),5,2) concat '-' -- MM
concat substr(char(a.ofbkddt),7,2) ) -- DD
) < 6
I don't get any results back from my query, even though it's clear that I am getting date differences of as little as 1 day (obviously less than the 6 days that I'm requesting in the where clause).
My first thought was that the return type of days() might not be an integer, causing the comparison to fail... according to the documentation for days() found at http://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm?info/db2/rbafzmst02.htm, it returns a bigint. I cast the difference to integer, just to be safe, but this had no effect.
You're going about this backwards. Rather than using a function on every single value in the table (so you can compare it to the date), you should pre-compute the difference in the date. It's costing you resources to run the function on every row - you'd save a lot if you could just do it against CURRENT_DATE (it'd maybe save you even more if you could do it in your application code, but I realize this might not be possible). Your dates are in a sortable format, after all.
The query looks like so:
SELECT ofbkddt as myDate
FROM QS36F.ASDF
WHERE myDate > ((int(substr(char(current_date - 6 days, ISO), 1, 4)) * 10000) +
(int(substr(char(current_date - 6 days, ISO), 6, 2)) * 100) +
(int(substr(char(current_date - 6 days, ISO), 9, 2))))
Which, when run against your sample datatable, yields the following:
myDate
=============
20110917
20110918
20110919
20110920
You might also want to look into creating a calendar table, and add these dates as one of the columns.
What if you try a common table expression?
WITH A AS
(
select
days( current date) -
days( substr(char(a.ofbkddt),1,4) concat '-' -- YYYY-
concat substr(char(a.ofbkddt),5,2) concat '-' -- MM-
concat substr(char(a.ofbkddt),7,2) ) as difference, -- DD
a.ofbkddt as mydate
from QS36F.ASDF a
)
SELECT
*
FROM
a
WHERE
difference < 6
Does your data have some nulls in a.ofbkddt? Maybe this is causing some funny behaviour in how db2 is evaluating the less than operation.
I have a database table with these two columns:
Amount: numeric (18,0)
DecimalPlaces: numeric (18,0)
This table can store amounts in various currencies, with the decimal place removed from the amount (I can't change this data model). For example, there might be two rows like this:
1290, 2 (This is £12.90, needs to appear as "12.90")
3400, 0 (This is 3400 Japanese Yen, needs to appear as "3400")
I need an SQL query for both Oracle and SQL Server that will format each amount with the correct number of decimal places, preserving any trailing zeroes as illustrated above. I can't use stored procedures, a reporting tool, or Excel.
Your problem is that there isn't an easy way to do this for both SQLServer and Oracle in one query.
The Correct way to do this for SQLServer is to use STR:
Select STR(Amount, 18, DecimalPlaces) from myTable;
The correct way to do this for Oracle is using to_char:
SELECT to_char (amount, '99999999999999.'||rpad('',DecimalPlaces, '0'))
from MyTable;
The queries presented by jms and Andrew won't work in an Oracle query because Oracle SQL uses LENGTH() not LEN(). And Oracle uses to_char() not Cast().
The best I've been able to come up with so far is:
select Amount/power(10, DecimalPlaces) from MyTable
But it doesn't do exactly what I want:
Oracle: the trailing zeroes are stripped, so US$15.00 looks like "15", not "15.00"
SQL Server: a whole lot of extra trailing zeroes are added, so $23.99 looks like "23.99000000000" instead of "23.99"
How about?
select 12345 amount, 2 decimalPlaces, substr( to_char( 12345 ), 1, length (to_char( 12345 ) ) - 2 )
|| '.' || substr( to_char( 12345 ), -2 ) result from dual
/
amount decimalPlaces result
---------- ------------- ------
12345 2 123.45
This is gross but worked for the current inputs on SQL server.
select
substring(
CAST(
CAST(
(amount * power(-0.100000000000000000,decimalPlaces*1.000000000000000000)) as numeric(36,18)
)as varchar(30)
)
,1,len(cast(amount as varchar(20))) + (CASE WHEN decimalPlaces = 0 THEN 0 ELSE 1 END ))
from
myTable
In SQL server you can :
select stuff(convert(varchar,amount) ,
len(convert(varchar,amount)) - DecimalPlaces - 1, 0, ".")
Martlark's answer for Oracle led me to this solution for SQL Server:
select
left(cast(Amount as varchar), len(cast(Amount as varchar)) - DecimalPlaces) +
left('.', DecimalPlaces) +
right(cast(OriginalCurrencyAmount as varchar), DecimalPlaces
) as FormattedAmount
from MyTable