Convert date - "0713" to "July 2013" - sql

I have a table on sql server (SSMS) with a column that contains 4-digit codes. Say 0713 stands for July 2013, and 0114 stands for Jan 2014. Now I want to convert the former to the latter, I wonder what can be the most efficient sql query to convert?
Thanks for any advice!

Probably the most efficient way is a case statement:
select (case left(col, 2)
when '01' then 'January '
when '02' then 'Feburary '
. . .
when '12' then 'December '
end) + right(col, 2)
This has the fewest calls to functions. There are more concise ways to do this, such as:
select datename(month, cast('2013' + col as datetime)) + right(col, 2)
Of course, to get the most efficient, you should set up a test in your environment on something like 1 million records and actually test the different timings.
For instance, it might be fastest to have a reference table with 366 entries, one for each day, and use a join to do the conversion.

On your final format it looks like you are wavering between values (Jan for January, but the full month name for July, instead of Jul). In that case, a look up table for the formatted months and a query to it.
Your FormattedMonth table could contain MonthName (the string you want to use) and MonthNumber (this would be most efficient as an int)
For Example:
SELECT originaldates.identifier, FormattedMonth.MonthName + ' 20' + originaldates.year
FROM (Select identifier, left(shortdate,2) as month, right(shortdate,2) as year
from Tablecontainingfield) originaldates
JOIN FormattedMonth
ON cast(FormattedMonth.MonthNumber as int) = originaldates.month

Related

Calculating age from incomplete SQL data

Two columns in table looks like this:
Year of birth
ID
2005
-
1997
-
85
-
95...
How do I create a SQL SELECT from all the data that will return the age of each person based only on the year of birth, and if the whole is not given or only the ID is given, then:
-if only two digits of the year are given such as 85 then by default the year of birth is 1985
-if no year is given then on the basis of the ID whose first two digits are the year of birth as above i.e. ID 95...- first two digits are 95 so the year of birth is 1995
MySQL
A simple example of using MySQL CASE function:
SELECT
CASE
WHEN year_of_birth REGEXP '^[0-9]{4}$' THEN year_of_birth
WHEN year_of_birth REGEXP '^[0-9]{2}$' THEN CONCAT("19", year_of_birth)
ELSE CONCAT("19", ID)
END as year_of_birth
FROM Accounts;
First, check for 4 digit year_of_birth, if not found, check for 2 digit, if not found then get ID. Using CONCAT function to prepend "19" to the 2 digit year and 2 digit ID. Also using REGEXP to check for 4 or 2 digit years.
Try it here: https://onecompiler.com/mysql/3y6yc7mv2
Firstly, I would suggest structuring your database in a cleaner way. Having some years formatted as four digits (e. g. 1985), and others as two is confusing and causes issues such as the one you have run into.
That being said, here is an ad-hoc transact sql formula that will calculate the age based on the incomplete data.
IF 'Year of Birth' IS NULL
SELECT YEAR(NOW()) - (1900 + CAST(LEFT('ID',2) AS INT));
ELSE
IF 'Year of Birth' < 100
SELECT YEAR(NOW()) - (1900 + 'Year of Birth');
ELSE
SELECT YEAR(NOW()) - 'Year of Birth'
This code is untested, and I assumed that the ID column is a string. You'll likely have to make adjustments to make it actually work for your database
To fix the structure of your table, however, a better approach might be cleaning the data and then calculating the date, using the following commands
Filling in null year values:
UPDATE table_name
SET 'Year of Birth' = CAST(LEFT('ID',2) AS INT)
WHERE IS_NULL('Year of Birth')
Making all year values 4 digits long:
UPDATE table_name
SET 'Year of Birth' = 1900 + 'Year of Birth'
WHERE 'Year of Birth' < 100
Now, you can simply subtract the current year from the 'Year of Birth' Column to calculate the age.
Good Luck!
Here is some relevant documentation
If-Else in SQL
Year Function in SQL
String Slicing in SQL
Casting Strings to Integers in SQL
You can follow these steps:
filter out all null values (using the WHERE clause and the COALESCE function)
transform each number to a valid year
year of birth has length 2 > map it to a value smaller than the current year (e.g. 22 -> 2022, 23 -> 1993)
year of birth has length 4 > skip
cast the year of birth string to a number
compute the difference between current year and retrieved year
Here's the full query:
WITH cte AS (
SELECT COALESCE(yob, ID) AS yob
FROM tab
WHERE NOT (yob IS NULL AND ID IS NULL)
)
SELECT yob,
YEAR(NOW()) -
CASE WHEN LENGTH(yob) = 2
THEN IF(CONCAT('20',yob) > YEAR(NOW()),
CONCAT('19',yob),
CONCAT('20',yob) )
WHEN LENGTH(yob) = 1
THEN CONCAT('200', yob)
ELSE yob
END +0 AS age
FROM cte
Check the demo here.
Lots of opportunities to clean up what you started with, and lots of open questions too, but the code below should get you started.
drop table if exists #x
create table #x (YearOfBirth nvarchar(4), ID nvarchar(50))
insert into #x values
('2005', NULL),
('1997', NULL),
('85', NULL),
(NULL, '951234567890')
select
year(getdate()) -
case when len(isnull(YearOfBirth, '')) <> 4
then year(convert(date, '01/01/' +
case when YearOfBirth is NULL
then left(ID, 2)
else YearOfBirth end))
else YearOfBirth end
as PossibleAge
from #x
where (isnumeric(YearOfBirth) <> 0 and len(YearOfBirth) in (2, 4))
or (YearOfBirth is NULL and isnumeric(ID) <> 0)
One and three digit years will be ignored. Lots of ways to adjust this, but without knowing data types, etc. it's just meant to be a rough start.

PHP SQL Select between 4 columns

I´m looking for a solution, where I can select the entries between 2 dates. My table is like this
ID | YEAR | MONTH | ....
Now i want to SELECT all entries between
MONTH 9 | YEAR 2015
MONTH 1 | YEAR 2016
I don´t get any entries, because the 2nd month is lower than the 1st month. Here is my query:
SELECT *
FROM table
WHERE YEAR >= '$year'
AND MONTH >= '$month'
AND YEAR <= '$year2'
AND MONTH <= '$month2'
I can´t change the columns of the table, because a csv import is like this. Can anyone help me on this?
The years aren't disconnected from the months, so you can't test them separately.
Try something like
$date1 = $year*100+$month; // will be 201509
$date2 = $year2*100+$month2; // will be 201602
...
SELECT * FROM table WHERE (YEAR*100)+MONTH >= '$date1' AND (YEAR*100)+MONTH <= '$date2'
Make sure you protect against SQL injection though.
SELECT
*
FROM
`my_table`
WHERE
((`YEAR` * 12) + `MONTH`) >= (($year * 12) + $month)
AND ((`YEAR` * 12) + `MONTH`) <= (($year2 * 12) + $month2)
Since they aren't date fields, you need to convert to numbers that can be compared against. Multiplying the year by 12 and adding the month will give you a unique number specific to that month of the year. Then you can compare on that.
There are a couple of good answers, but assuming taht you don't/can't change the date's format something you can do is
WHERE ((YEAR>'$year') OR
(YEAR=='$year' AND MONTH>='$month')
AND ((YEAR<'$year2') OR
(YEAR=='$year2' AND MONTH<='$month2')
I would suggest the workarounds though (like alphabetically comparing in YYYYMM[DD] format).
You need to pad the month to make sure it starts with a zero. Otherwise 20162 will be lower than 201512, for example.
$date1 = $year . str_pad($month, 2, "0", STR_PAD_LEFT);
$date2 = $year2 . str_pad($month2, 2, "0", STR_PAD_LEFT);
"SELECT * FROM dates WHERE concat(`year`, LPAD(`month`, 2, '0')) >= '$date1' AND concat(`year`, LPAD(`month`, 2, '0')) <= '$date2'"
Though there are a lot of ways to solve this problem, but the best way is to convert these values into a proper date type in mysql query using str_to_date it is PHP's equivalent of strtotime, your new query should look like this
SELECT
d.*
from
dates as d
where
STR_TO_DATE( concat('1,',d.month,',',d.year) ,'%d,%m,%Y') > STR_TO_DATE('1,5,2015','%d,%m,%Y')
and
STR_TO_DATE( concat('1,',d.month,',',d.year) ,'%d,%m,%Y') < STR_TO_DATE('1,4,2016','%d,%m,%Y')
Using this technique you can easily compare dates and do much more and not worry about other complexities of calendars.
Source: MySQL date and time functions

where condition for year and month

I have a datatable with year and month columns. I store these values as number, like 2014 and 1 for jan 2014, etc.
Now, I would like to select all values between 2014-01 and f. e. 2015-5.
I have tried it different ways (between the two values, concating them), but it exists always values, which the result set don't contains.
How should be look the query?
How about the following:
SELECT * FROM MyTable m
WHERE CDate(m.month & "/1/" & m.year) Between #1/1/2014# AND #5/1/2015#
It appears you want:
All data from 2014
2015 data from Jan through May
If that's correct, you can ask Access to give you those data directly ... without the need to first transform your year and month fields to a date value.
SELECT *
FROM datatable
WHERE
year_field = 2014
OR
(
year_field = 2015 AND month_field <= 5
);
With year_field and month_field indexed, this query should be much faster than an alternative where you apply CDate or Format to every row of the table and thereby forego any opportunity to benefit from indexed retrieval.
On the other hand, if your table is small enough, you probably wouldn't notice the horrible inefficiency. :-)
You are not defining your RBDMS, so I'm supposing you use ORACLE, you can change the syntax of course.
SELECT *
FROM [table]
WHERE (SELECT to_date(to_char(year) || '/' || to_char(month) || '/01', 'yyyy/mm/dd')
FROM dual)
BETWEEN (SELECT to_date('year/mm/01', 'yyyy/mm/dd' FROM dual)
AND (SELECT to_date('year2/mm2/01', 'yyyy/mm/dd' FROM dual)
Untested.
Notice you need to replace year and mm for the beginning date and year2 and mm2 for the ending date.
I got yelled at one time because between is not safe apparently, so you may prefer
SELECT *
FROM MYTABLE AS m
WHERE Format(m.month & "/1/" & m.year,"mm/dd/yyyy") >=
Format(#1/1/2014#,"mm/dd/yyyy") AND
Format(m.month & "/1/" & m.year,"mm/dd/yyyy") <=
Format(#5/1/2015#,"mm/dd/yyyy")
Also base code stolen from Linger
You should convert the two parts of all of the involved parameters into actual dates and use BETWEEN on them instead.
SELECT [field_list]
FROM [table_name] t
WHERE CDate(t.year + '/' + t.month + '/02')
BETWEEN #2014/01/01#
AND #2014/05/03#;
If you have an actual day field you can use that, but for simplicity, and assuming you aren't using the day, the above setup should work fine for you.

SQL DB2 - Possible to shorten long-listed 'case when' statement?

Sometimes my queries have long case when statements. For example,
CASE WHEN BASE_YM = TO_CHAR(DEFAULT_YM, 'YYYYMM') THEN '00'
WHEN BASE_YM = TO_CHAR(DEFAULT_YM - 1 MONTHS, 'YYYYMM') THEN '01'
WHEN BASE_YM = TO_CHAR(DEFAULT_YM - 2 MONTHS, 'YYYYMM') THEN '02'
.
.
.
WHEN BASE_YM = TO_CHAR(DEFAULT_YM - 35 MONTHS, 'YYYYMM') THEN '35'
WHEN BASE_YM = TO_CHAR(DEFAULT_YM - 36 MONTHS, 'YYYYMM') THEN '36'
This case when statement itself takes up 37lines and I was wondering if there might be maybe a way to shorten the case when statement by using i=00, ..., 36 something like that?
It seems like all you're really doing is finding out how many months are between BASE_YM and DEFAULT_YM, and making that a character type. I think the same result can be retrieved by doing some date math (and I'm guessing it'd be more efficient, because it wouldn't have to calculate up to 36 different TO_CHAR calls) This whole statement would replace the CASE. I tested similar logic on DB2 for Linux/Unix/Windows v.9.7, and z/OS v10:
SELECT
LPAD(
ABS(
MONTH(
TIMESTAMP_FORMAT(BASE_YM, 'YYYYMM') -
DEFAULT_YM
)
)
,2,'0')
FROM your_table
I added the extra spacing so that you could (hopefully) follow along a little easier. Basically, it just subtracts DEFAULT_YM from the converted form of BASE_YM. Then, I take the Absolute Value of the difference in months, and then convert it to a zero-padded character field.
make a new table with 2 columns containing your 36 rows of distinct values, then join it on? this will eliminate the need for a case statement at all

Good way to append two integers into another integer in TSQL statement?

We're using SQL Server 2005 and wanted to append two integers into another one. Right now we're summing the month and year of a date field, which isn't really what we want. Instead, we'd like to append them together, but retain an integer.
Here's what we have right now:
SELECT
YEAR( MeetingDate) + MONTH( MeetingDate) AS DateGroup
We'd like DateGroup to read 20118, 20119, 201110, 201111 etc. Trying to learn some good ways of doing this.
Consider using a leading zero for one digit months, i.e. 201108 rather than 20118, then it's easy:
select
year(MeetingDate) * 100 + month(MeetingDate) as DateGroup
Otherwise you have to treat some months differently:
select
year(MeetingDate) * case when month(MeetingDate) < 10 then 10 else 100 end
+ month(MeetingDate) as DateGroup
Cast your integers to varchar and then add them togehter.
select cast(cast(year(MeetingDate) as varchar(4)) +
cast(month(MeetingDate) as varchar(2)) as int) as dategroup