Change Date format in SQL - sql

I have a variable "month" in SQL DB with 2 different formats: yyyy-mm and yyyy-m (e.g., 2015-11, 2016-1). When I try to sort the 'month" (ascending or descending), it is not showing properly due to this format difference. How to change the yyyy-m format to yyyy-mm?

If you can be certain that it's never going to be anything other than those exact formats you can insert a zero at a known point in the string -
SELECT MonthField,
CASE WHEN LEN(MonthField)=6 THEN
LEFT(MonthField,5)+'0' + RIGHT(MonthField,1)
ELSE MonthField
END AS MonthField_Cleaned
FROM SourceTable
But if at all possible you'd be better off cleaning whatever's creating the data, and ideally storing the data as actual dates rather than strings representing the months to ensure they can't get mixed up and you can use the DBMS' date manipulation functions.

Related

Conversion failing for date formatting

I have an nvarchar(100) column which has a value ' 8/11/2022'.
I receive and error when trying to convert it to date...
select convert(date,[date],103)
from [Source].[TableName] s_p
--Msg 241, Level 16, State 1, Line 96
--Conversion failed when converting date and/or time from character string.
I have tried a number of different ways to approach but I can't find one to give me '08/11/2022'
select Date = REPLACE(LEFT([Date],10),' ','0')
from [Source].[TableName] s_p
--Outcome 8/11/2022
select REPLACE([DATE],' 8/','08/')
from [Source].[TableName] s_p
--Outcome 8/11/2022
select convert(nvarchar,[date],103)
from [Source].[TableName] s_p
--Outcome 8/11/2022
The strange thing is when I copy and paste from the results grid then do a replace it works fine...
select REPLACE(' 8/11/2022',' 8/','08/')
--Outcome 08/11/2022
Please help me to get to '08/11/2022' or any single digit to having a leading 0.
Thanks, Will
Different languages and cultures have their own formatting preferences around date values. Some places like M/dd/yyyy. Some places like dd/MM/yyyy. Or perhaps d-M-YYYY (different separators and conventions around leading zeros). The point is it's not okay to go into a place and impose our own preferences and norms on that culture.
The SQL language is no different. It really is it's own language, and as such has it's own expectations around date handling. If you violate these expectations, you should not be surprised when there are misunderstandings as a result.
The first expectation is for date and datetime values to be stored in datetime columns. It's hard to understate how much of a difference this can make for performance and correctness.
But let's assume that's not an option, and you have no choice but to use a string column like varchar or nvarchar. In that situation, there is still an expectation around how date values should be formatted.
Any database will do better if you use a format which stores the date parts in order by descending length. For example, ISO-8601 yyyy-MM-ddTHH:mm:sss[.fff] This is important to allow greater than/less than comparisons to work, it can greatly help with indexes and performance, and it makes cast/convert operations to datetime values MUCH more likely to succeed and be accurate.
For SQL Server specifically, there are three acceptable formats:
yyyy-MM-ddTHH:mm:sss[.fff],
yyyyMMdd HH:mm:ss[.fff], and
yyyyMMdd.
Anything else WILL have date values that don't parse as expected. Any string manipulation done to call the CONVERT() method should focus on reaching one of these formats.
With that in mind, and assuming 8/11/2022 means November 8 and not August 11 (given the 103 convert format), you need something like this:
convert(datetime,
right([date], charindex('/', reverse([date]))-1) -- year
+ right('0' + replace(substring([date], charindex('/', [date])+1, 2), '/', ''), 2) -- month
+ right('0' + left([date], charindex('/',[date])-1),2) -- day
)
And you can see it work here:
https://dbfiddle.uk/lM8sVySh
Yes, that's a lot of code. It's also gonna be more than a little slow. And again, the reason why it's so slow and complicated is you jumped in with your own cultural expectations and failed to respect the language of the platform you're using.
Finally, I need to question the premise. As the fiddle above shows, SQL Server is perfectly happy to convert this specific value without error. This tells me you probably have more rows, and any error is in fact coming from a different row.
With that in mind, one thing to remember is a WHERE clause condition will not necessarily run or filter a table before a CONVERT() operation in the SELECT clause. That is, if you have many different kinds of value in this table, you cannot guarantee your CONVERT() expression will only run on the date values, no matter what kind of WHERE clause you have. Databases do not guarantee order of operations in this way.
The problem could also be some invisible unicode whitespace.
Another possibility is date formats. Most cultures that prefer a leading day, instead of month or year, tend to also strongly prefer to see the leading 0 in the first place. That the zero is missing here makes me wonder if you might have a number of dates in the column that were formatted by, say, Americans. So then you try to parse a column with values both like 02/13/2022 and 13/02/2022. Obviously those dates can't both use the same format, since there is no 13th month.
In that case, best of luck to you, because you no longer have any way to know for certain whether 2/3/2022 means March 2nd or February 3rd... and trying to guess (by say, assuming your own common format) is just exacerbating the same mistake that got you into this mess in the first place.
It's worth noting all three of these possibilities would be avoided had you used DateTime columns from the beginning.
You'll want to use LPAD to add 0 to string, then CAST() string as date if you want to change to date data type

SQL: date type column with only mm-dd

I want to create a column of data type having only 'mm-dd' values.
Is it possible and if yes how should I do it?
Note: Instead of "2022-06-07", I want "07-06"
There is no date type that can store that format - in fact none of the date types store a date and/or time in any of the formats you typically recognize.
For your specific requirement, that looks like a char(5) for the data type, but how you constrain it so that it will only accept valid date values, I have no idea. You'd think this would work:
CHECK (TRY_CONVERT(date, string_column + '-2022', 105) IS NOT NULL)
But what about leap years? February 29th is sometimes valid, but you've thrown away the only information that can make you sure. What a bunch of mess to store your favorite string and trust that people aren't putting garbage in there.
Honestly I would store the date as a date, then you can just have a computed column (or a column in a view, or just do this at query time:
d_slash_m_column AS CONVERT(char(5), date_column, 105)
Why not just in your query (or only in a view) say:
[output] = CONVERT(char(5), data_in_the_right_type, 105)
?
I'd personally stay away from FORMAT(), for reasons I've described here:
FORMAT() is nice and all, but…
FORMAT is a convenient but expensive function - Part 1
FORMAT is a convenient but expensive function - Part 2
You can use the SQL Server FORMAT function:
FORMAT(col1, 'dd/MM')
Check the demo here.
In such cases using char or varchar is not the best option as in those cases the underlying DB constraints that validate the integrity of the data do not kick in.
Best option is to use an arbitrary year and then put in a proper date, so for example for storing 01-Jan, the db column should store proper date with year as any arbitrary value, e.g. 2000. So your db should say 2000-01-01.
With such a solution you are still able to rely on the DB to raise an error if you tried month 13. Similarly sorting will work naturally as well.

How to check that cells contain data in date format (oracle)

I need verify that all cells in column contain data in only date format. How it possible to verify?
*I think it isn't LIKE function.
DATE doesn't have any format. What you see is for display purpose so that it could be easily interpreted.
DATE datatype is stored in a proprietary format internally in 7 bytes. It is a bad idea and makes no sense to verify the format while date is stored in an internal format. As I said, format is only for display.
If the date column is not a DATE data type, then it is a design flaw. And, any application based on such a flawed database design is on the verge to break anytime.
Storing DATE values other than date data type is just like not understanding the basics.
You should first fix the design to get a permanent solution. Any solution to your question is just another workaround.
Let me show a small example how it creates even more confusion.
The following date :
01/02/2015
Is it:
1st Feb 2015 or,
2nd Jan 2015
There is no way to tell that. It could be either DD or MM. This being just one among so many other problems due to the incorrect data type.
Store date values as DATE data type only, period.
Based on your last question, I think you are looking for something like this:
SELECT COUNT(*) FROM ...
WHERE NOT REGEXP_LIKE (A, '^XXX/MOSCOW/XXXMSX/[0-9]{4}-[0-9]{2}-[0-9]{2}$')
If count is greater than zero, something doesn't match. If you want more detail on what doesn't match, change your SELECT clause appropriately.
If you are looking for multiple date formats, you can change your regular expression appropriately. The | operator in most flavors of regular expression, including Oracle's, lets you define multiple patterns in the same space. You might use something like
SELECT COUNT(*) FROM ...
WHERE NOT
REGEXP_LIKE (A,
'^XXX/MOSCOW/XXXMSX/[0-9]{4}-[0-9]{2}-[0-9]{2}$|^[0-9]{4}-[0-9]{2}-[0-9]{2}$')
adding as many different matching patterns as you need.
Try
SELECT *
FROM POL
WHERE NOT REGEXP_LIKE(TR_KRY, '^(0[1-9]|([1-2][0-9])|30|31)-(([0][1-9])|10|11|12)-[0-9]{4}$')
This will return you all rows where TR_KRY is not formatted as 'DD-MM-YYYY', where DD is '01'-'31', MM is '01'-'12', and YYYY is any four numeric digits.
As others have said, storing dates as character strings is not a good idea. In the field you're looking at, it might be that the date is stored as DD-MM-YYYY (day-month-year - the usual case in Europe and perhaps elsewhere), or it might be that the date is stored as MM-DD-YYYY (month-day-year - a common practice in the US). If possible, I suggest you should convert this field to the DATE data type so that the TO_CHAR function can be used to produce a text version of the date in whatever format is desired.
Given the example data you've shown in comments (and that's also not good practice - you should go back and edit the question when you want to include additional information) it appears the dates are formatted as DD-MM-YYYY and I've set up the regular expression above to deal with this as best as possible.

date handling in sqlite3 confusing.

I am from non database background. I have created a table with one of the field data type TEXT.
dataF TEXT
INSERTION:
Have inserted three records with the values :
'842-2-4'
'842-2-5'
'842-2-6'
SELECTION:
Tring to get the records based on date now.
... where dateF between '842-2-4' and '842-2-10'
It fails.
Whereas,
... where dateF between '842-2-4' and '842-2-8'
retrieves all the 3 records.
What am i doing wrong ? Why the first statment fails ?
Kindly suggest.
Because you are comparing strings not dates. The computer has no idea these are dates. You have to either store as date and do a date comparison or implement your own logic to analyze strings.
Simply put, it is looking at the 1 in 10 as being less than your values rather than 10 being more. It's string comparison, not date.
Although sqlite doesn't support date types, it does have functions for dealing with them. See here:
http://www.sqlite.org/lang_datefunc.html
When comparing strings, the values are compared left to right...
As one string is shorter that the other, you are kind of comparing this...
'842-2-4'
'842-2-1'
Well, nothing is >= '842-2-4' AND <= '842-2-1'.
Because '842-2-1' comes before '842-2-4'.
And, so, '842-2-10' comes before '842-2-4' too.
Just as 'Squiggled' comes before 'Squiggly'
And as 'xxx-y-az' comes before 'xxx-y-z'
To compare as you desire, make sure all your dates are padded with 0's.
BETWEEN '0842-02-04' AND '0842-02-10'
But that will only work after you have padded out the values in your table too.
EDIT:
Also, note that this assumes that your format is YYYY-MM-DD. As a string is compared left to right, you must have the highest magnitude values to the left.
(This means that you can't use YYYY-DD-MM and still have native string comparisons behave as you would want them.)

Sorting a time value in a dataset

In my current solution, I am converting a DateTime value, "Time" in my database using CONVERT so it displays in a ##:##AM/PM format, and I realize that CONVERT just takes whatever datatype and turns it into a VarChar, or a String once its in C#. This is making my sort work incorrectly in my Gridview. I am sorting the columns in my DataSet returned from my stored procedure. However it isnt sorting by AM/PM since its a string literal, and not a DateTime.
What is the best way to sort Time values? Should I use a different datatype, like TIME in my database? Different CONVERT command? I'm stumped! I can't use a 24 hour format, that's the only restriction. Thanks!
A couple of thoughts:
Could you use AM 01:23 / PM 01:23 format - this would sort well.
If you can return the times from the database in DATETIME format, but with the date set to some 'constant date' (e.g. 2000-01-01 hh:mm), you could almost certainly put a format string on the GridView column to display just the time, whilst still enabling sorting by the underlying value.
I use ticks: http://www.codeproject.com/KB/database/DateTimeToTicks.aspx
Or (if possible) DateTime - and handle it in my grid.
You can add an additional column with the date/converted date which is hidden in you gui but used to sort.