Unexpected error converting text to date in SQL Server - sql

I have a problem converting int values of year, month and day into date value.
SELECT datefromparts(b.Install_year, b.shipm_month, b.shipm_day)
I have already found a solution to the problem, discovering that some values of DAY were 0. But I am curious why things work certain way.
SELECT datefromparts(2005, 4, b.shipm_day)
For testing purpose I set year and month values fixed. Putting there 1,3,5,7,8,10,12 as month and running that in Management Studio I can see the results. Values : 2,4,6,9, and 11 cause the error below:
"Cannot construct data type date, some of the arguments have values which are not valid."
Anyone has idea why certain values work and other don't?

This would presumably be because the "day" value is 31 and those months do not have 31 days.
datefromparts() only constructs valid dates.
This is not that easily fixed because datefromparts() doesn't have a "try_" version.
Here is one method:
select coalesce(try_convert(date, concat(2005, '-', 4, '-', b.shipm_day)),
try_convert(date, concat(2005, '-', 4, '-', b.shipm_day - 1)),
try_convert(date, concat(2005, '-', 4, '-', b.shipm_day - 2)),
try_convert(date, concat(2005, '-', 4, '-', b.shipm_day - 3))
)

Related

Combine concat with substr in a sql query

I am doing a sql query (in DB2) and I need to extract a date that is in the following form:
2022-01-01
In the Where condition I am using:
CONCAT(SUBSTR('$P!{FLIB}', 1, 4), SUBSTR('$P!{FLIB}', 5, 2), SUBSTR('$P!{FLIB}', 9, 2))
Where '$P!{FLIB}' is the date, but I get the following error:
Invalid number of arguments for CONCAT function.
It should be like 20220101
As said in a comment, a different approach is to first take the entire 'date' substring, and then replacing the '-' with empty string:
SELECT REPLACE(SUBSTR(x, 1, 10), '-', '')
FROM (VALUES('2022-01-01'))V(x)
db<>fiddle

CONVERT is presenting the wrong year

I'm using SQL Server 2014, and I'm trying to perform a data conversion, the value being passed '04-SEP-44'
Since this was the birth year, it needs to be 1944, not 2044.
I've tried
select convert(DATETIME, '04-SEP-44')
select convert(DATETIME, '04-SEP-44', 1)
select convert(DATETIME, '04-SEP-44', 13)
But they are all using the year as 2044.
Are there any quick fixes to this issue?
Possibilities:
If the value is greater than today subtract 100 years...
Force "19" into the string...
But it really depends on your business rules, is it always going to be 1900, or could some dates be early 2000's? You should really update your front end to capture the full date.
select case when Y.[Value] > getdate() then dateadd(year, -100, Y.[Value]) else Y.[Value] end
, convert(date, substring(X.[Value], 1, 7) + '19' + substring(X.[Value], 8, 2))
from (values ('04-SEP-44')) as X ([Value])
cross apply (values (convert(date, X.[Value]))) as Y ([Value]);
Note: why would you convert a date to a datetime? Don't use a datetime if you don't have a time component.

SQL Vertica, Cast Long issue

I have the following SQL statement
cast('long', ((substr(D.business_day, 1, 4)||substr(D.business_day, 6, 2))||substr(D.business_day, 9, 2))) AS bdate_id_yyyymmdd,
I have tried to change the 'long' to integer to see if that would work.
cast(integer, ((substr(D.business_day, 1, 4)||substr(D.business_day, 6, 2))||substr(D.business_day, 9, 2))) AS bdate_id_yyyymmdd,
Also tried integer(8)
getting error:
Error: [Vertica][VJDBC](4856) ERROR: Syntax error at or near ","
SQLState: 42601
ErrorCode: 4856
It's not only a syntax error. Vertica is relatively pecky at data types, too.
So you want to make an integer consisting of yyyymmdd from something that looks like an ISO date.
A string?
A date?
There are two ways to go about that. You can't SUBSTR() on a date; you can't call a function getting the day, month, or year, number, from an ISO formatted string.
This example illustrates what you can do. Remember, extracting integers from dates is faster than working with strings and substrings. If your business_day is a DATE type, do what I do with business_date below.
Oh, and Vertica also supports this syntax:
<expression>::INTEGER
to cast to integer.
Happy playing ...
Marco
WITH
d(business_date,business_day) AS (
SELECT DATE '2018-03-04', '2018-03-04'
UNION ALL SELECT DATE '1957-04-22', '1957-04-22'
UNION ALL SELECT DATE '1959-09-27', '1959-09-27'
)
SELECT
CAST(
substr(d.business_day, 1, 4)
||substr(d.business_day, 6, 2)
||substr(d.business_day, 9, 2)
AS int
) AS with_substr
, ( YEAR (business_date)*10000
+ MONTH(business_date)*100
+ DAY (business_date)
) AS with_calc
FROM d;
with_substr|with_calc
20,180,304|20,180,304
19,570,422|19,570,422
19,590,927|19,590,927

SQL: Error when converting varchar to datetime

WITH Valid_dates (ValidDate)
AS
(
SELECT '19' + SUBSTRING(column, 0, 3) + '-' + SUBSTRING(column, 3, 2) + '-' + SUBSTRING(column, 5, 2) AS ValidDate
FROM table
WHERE SUBSTRING(column, 3, 2) <= '12' --Max month
AND SUBSTRING(column, 3, 2) >= '01' --Min month
AND ISNULL(SUBSTRING(column, 3, 2), '') <> '' --Empty string
AND SUBSTRING(column, 5, 2) <= '31' --Max day
AND SUBSTRING(column, 5, 2) >= '01' --Min month
AND ISNULL(SUBSTRING(column, 5, 2), '') <> '' --Empty string
AND LEN('19' + SUBSTRING(column, 0, 3) + '-' + SUBSTRING(column, 3, 2) + '-' + SUBSTRING(column, 5, 2)) = 10 --Must match 10 character format
)
SELECT CONVERT(DATETIME, ValidDate)
FROM Valid_dates
When I execute this query in SQL Server Management Studio, I get the following error message: "The conversion of a varchar data type to a datetime data type resulted in an out-of-range value."
I have filtered out all invalid dates. Non-existent dates such as Feb 30, Apr 31, Jun 31 etc. are not filtered out per se as you can see, but I have checked for them, and they do not exist. Running only the CTE query yields plenty of results with which I can not find any discrepancies.
This is an issue with SQL Server. It does not guarantee the order of evaluation of clauses in the query. There is no sense that the where (even in the CTE) is evaluated "before" the rest of the query.
Note: This is a feature of the optimizer. By rearranging the evaluation of different components of the query, the optimizer can create a better query plan.
In SQL Server 2012+, the simplest method is TRY_CONVERT():
with . . .
select try_convert(datetime, validdate)
from valid_dates;
In earlier versions, you can use case:
select (case when <all your conditions here>
then convert(datetime, validdate)
end)
SQL Server does guarantee the sequential evaluation of case statements under normal circumstances (there are some quirky situations with aggregations), so this also fixes the problem.
I'm pretty sure that this is culture related.
You did not specify the actual input, from your code I suppose it is some unseparated format without a year like 920130 meaning the 30th of January in 1992?
This will be transformed in something like 1992-01-30.
Your default culture is probably one using something like yyyy-dd-mm which will lead into out-of-range error with a "month" higher than 12...
Try to use 102 as culture key:
SELECT CONVERT(DATETIME, ValidDate,102)
FROM Valid_dates
UPDATE
If my assumptions are true, it was much easier to try
SELECT TRY_CAST('19' + column AS DATE) FROM table
SQL Server allows natively to cast unseparated strings looking like yyyymmdd. Using TRY_CAST will return NULL if the cast is not possible.
With older versions (<2012) use this
SELECT CASE WHEN ISDATE('19' + column)=1 THEN CAST('19' + column AS DATE) ELSE NULL END FROM table

SQL Server : split string in SELECT statement

I want to split date in column in 3 fields, I use this query
SELECT
SUBSTRING(Account.date, 1, 2) AS "Month",
SUBSTRING(Account.date, 4, 2) AS "Day",
SUBSTRING(Account.date, 7, 4) AS "Year"
FROM Account
Almost all data is in format 02/11/2000, but some of it can be 02/November/2000 or 2/11/2000.
Only common thing is that data separated by /. How can I separate this column using the delimiter?
Surprisingly CAST('2/November/2000' as datetime) works (checked on SQL Server 2008), gives value 2000-11-02 00:00:00.000
SELECT
Month(CAST(Account.date AS DateTime)) "Month",
Day(CAST(Account.date AS DateTime)) "Day",
Year(CAST(Account.date AS DateTime)) "Year",
FROM Account
But as rightly pointed out in comment how do you know if "02/11/2000" is November 2, 2000 or February 11, 2000?
Also the spelling of Month names must be absolutely correct else conversion fails. Since you are storing dates as string there is chance that entry like November , Agust etc could have been made .
You should never store date values as strings.
You can do it this way by using CHARINDEX and SUBSTRING functions
select
LEFT(Account.date, CHARINDEX('/', Account.date) - 1),
SUBSTRING(Account.date, CHARINDEX('/', Account.date) + 1, LEN(Account.date) - CHARINDEX('/', Account.date) - CHARINDEX('/', Account.date, CHARINDEX('/', Account.date)) - 2),
REVERSE(LEFT(REVERSE(Account.date), CHARINDEX('/', REVERSE(Account.date)) - 1))
FROM Account
You can abuse the PARSENAME function slightly here:
SELECT FirstPart = PARSENAME(REPLACE(Account.Date, '/', '.'), 3),
SecondPart = PARSENAME(REPLACE(Account.Date, '/', '.'), 2),
ThirdPart = PARSENAME(REPLACE(Account.Date, '/', '.'), 1)
FROM (VALUES
('02/November/2000'),
('2/11/2000')
) Account (Date);
Will give:
FirstPart SecondPart ThirdPart
02 November 2000
2 11 2000
I would however, highly recommend storing your dates using the appropriate data type!. SQL Server 2012 has the TRY_CONVERT function which can make such conversions easier, but you still need to know what format your string date is in, 2/11/2000 could be the 2nd November, or 11th February depending on your regional settings.
You can use the combination of CharIndex and Substring function in SQL to split the string based on delimiter.
You can check CharIndex examples here SQL Server 2005 Using CHARINDEX() To split a string
and here SQL Server - find nth occurrence in a string
Assuming your database column account.date contains a valid datetime or date value you should using SQLServers date functions like:
select month(getDate()) as "Month",
day(getDate()) as "Day",
year(getDate()) as "Year"
I replaced your column account.date by a getDate() to have some test values. This maps to your SQL in the following way
SELECT
month(Account.date) AS "Month",
day(Account.date) AS "Day",
year(Account.date) AS "Year"
FROM Account
Storing these date values as varchars would be IMHO a design flaw of your database structure. Dates are formatted in multiple ways to text. This is the presentation of your database data. To process your data you would always preprocess your text dates. That is bad practice.
If you have indeed varchar values there are several SO questions like: How do I split a string so I can access item x?.
Try this:
DECLARE #VALUE VARCHAR(100)<BR>
SET #VALUE ='2/11/2000' <BR>
SELECT SUBSTRING(#VALUE,0,CHARINDEX('/',#VALUE,0)),
SUBSTRING(#VALUE,
CHARINDEX('/',#VALUE,0)+1,
(CHARINDEX('/',#VALUE,(CHARINDEX('/',#VALUE,0)+1)) -
CHARINDEX('/',#VALUE,0) - 1)
),RIGHT(#VALUE,4)