Contatenating two date values into one column - oracle

Should be a pretty simple question. I have two fields - one a year field and the other a month field. The month field is an integer and if there is only one digit such as 6 for June there is no leading zero. I want to concatenate the two fields together to get 201406 not 20146 if I concatenate them together now. I tried
year||to_char(month,'09') but the field is being displayed as 2014 06 with a space in-between the year and month. Is there a way to do this without a space?

If your output contains a space, then either your year or your month column contains a space. To get rid of these, you can use TRIM:
with v_data(year, month) as (
select '2015 ', ' 1' from dual union all
select ' 2014 ', ' 12 ' from dual union all
select '2014', '3' from dual
)
select trim(year) || lpad(trim(month), 2, '0')
from v_data
(this assumes that you really have two string columns - if you indeed have two date columns, please add example input to your question)
UPDATE
If you want to use to_char() instead, you should use the FM format modifier to get rid of the space:
select trim(year) || trim(to_char(month, 'FM09'))
from v_data

The issue is that, by default, to_char leaves a space in front of a positive formatted number, so that they line up well with negative numbers. To prevent this, use to_char(month,'fm09').

Related

Understanding a specific WHERE clause in Oracle

I saw this as part of an Oracle SQL code
where employer.eab_yr || employer.eab_no >= 20173
and employer.eab_yr || employer.eab_no <= 20202
Can anyone explain to me what this part of the code is doing?
It appears it's fetching values from the employer table where eab_yr is between the 3rd month of 2017 and the second month of 2020. Is that correct?
|| is the string concatenation operator, >= is the greater than or equal operator and <= is the less than or equal operator.
So it matches all rows where the string concatenation of the columns eab_yr and eab_no of employer is numerically greater than or equal to 20173 and numerically less than or equal to 20202.
That's all there is. What that means semantically cannot be answered without knowing the semantics of the relation(s (there might be more than one, we cannot tell as there was no FROM clause given)) and the attributes. You didn't disclose those.
Assuming the code is concatenating year and month and trying to find a range then the code does not work as you would expect.
If you put the range from September 2017 to December 2017 then you will get the query:
where employer.eab_yr || employer.eab_no >= 20179
and employer.eab_yr || employer.eab_no <= 201712
Now, 20201 is greater than the number 20179 and is less than the number 201712 so you would find that January 2020 is returned (and so would many other unexpected months).
If you want to fix it then use a 2-digit month:
where employer.eab_yr || LPAD( employer.eab_no, 2, '0' ) BETWEEN 201709 AND 201712
or use date literals:
where TO_DATE( employer.eab_yr || '-' || employer.eab_no, 'YYYY-MM' )
BETWEEN DATE '2017-09-01' AND DATE '2017-12-01'
or, even better, fix your table so that you have a single DATE column rather than individual year and month columns.
db<>fiddle here

Single Month Digit Date Format issue in Oracle

Am getting the below issue when am using 'mon-d-yyyy' to convert date to char, as i need a single day digit for values from 1 to 9 days in a month.
When i use the 'mon-d-yyyy' format, am losing out on 5 days and getting a wrong date. Any help on this would be great.
select to_char(sysdate-22,'mon-d-yyyy') from dual;--aug-2-2017
select to_char(sysdate-22,'mon-dd-yyyy') from dual;--aug-07-2017
select sysdate-22 from dual;--07-AUG-17 11.06.43
In Oracle date formats, d gets the day of week. The 2 in your output means monday, not august the 2nd.
Try using Fill Mode as Format Model Modifier
select to_char(sysdate-22,'mon-fmdd-yyyy') from dual;
One option might be to piece together the date output you want:
SELECT
TO_CHAR(sysdate-22, 'mon-') ||
TRIM(LEADING '0' FROM TO_CHAR(sysdate-22, 'dd-')) ||
TO_CHAR(sysdate-22, 'yyyy')
FROM dual;
The middle term involving TRIM strips off the leading zeroes, if present, from the date.
Output:
Demo here:
Rextester
SQL>SELECT TO_CHAR(TO_DATE('29-AUG-2017','DD-MON-YYYY') - 22,'"WEEKDAY :"D, MON-FMDD-YYYY') "Before22Days" FROM DUAL;
D- Gives you a numeric weekday(2nd weekday in a week) on AUG-07-2017.
DD-Gives a Numeric Month Day i.e,07th
FMDD-Gives 7th
Before22Days
----------------------
WEEKDAY :2, AUG-7-2017

Oracle DB: calculate age from strings

I need to calculate age for the people and their birthdates are saved in varchar2 like 19900130, and some people don't have their birthdates recorded and default value is 00000000.
Here is my code:
SELECT
e.id_number,
e.birth_dt,
(CASE WHEN SUBSTR(e.birth_dt, 1, 4) = '0000' THEN 0
WHEN SUBSTR(e.birth_dt, 1, 4) <> '0000' THEN
ROUND(MONTHS_BETWEEN(SYSDATE, TO_DATE(e.birth_dt, 'YYYYMMDD')) / 12)
ELSE -1
END) age
FROM employee e
The error is:
ORA-01843: Not a valid month
Is anything wrong? I couldn't figure out.
Is anything wrong? Yes, your case is not covering the possibility that some strings have something other than 01, 02, ..., 12 in character positions 5 and 6. Likewise, your case is not covering the possibility that the day of the month character positions are something other than 01, 02, ... 31 (and the covering of the possibility that a day of the month is not valid for a particular month).
If I were you, I'd add a proper date column, modify the app to populate both columns, fix the app so that it stops putting bad data into the table, decide what to do with the bad data, modify the app to stop populating the varchar2 column, and then drop the varchar2 column.

Convert String to Date field for SQL Oracle

I need to convert a string to a date field. The field stores 30 characters. Dates, when present, are formatted as 'yyyymmdd' (20170202). In all cases, dates have 22 spaces after. I need to format this field as a date field like this: dd-mm-yyyy.
I've tried several formulas:
TO_CHAR(PERSACTION.NEW_VALUE_02, 'dd-mm-yyyy') ,TO_CHAR(PERSACTION.NEW_VALUE_02, 'yyyymmdd'), trim(TO_CHAR(PERSACTION.NEW_VALUE_02, 'yyyymmdd')) with error message: invalid number format model. Your expertise is welcome and appreciated.
to_char(to_date( rtrim(new_value_02), 'yyyymmdd'), 'dd-mm-yyyy')
Should do the trick. rtrim removes spaces on right side of string. Then I convert it to date using the date format specified, and then convert it to a string again in the desired format.
Did tried to convert to date format and then to char again?
TO_CHAR(TO_DATE(PERSACTION.NEW_VALUE_02,'yyyymmdd'),'dd-mm-yyyy')
Please, please, please do not store DATEs and CHARACTER datatypes. This will only lead to issues that can be avoided when using the DATE datatype.
If you want to change the string 20170202 to another string and not actually a date (which would have no intrinsic formatted text representation), you could optionally use a regular expression to transform it, instead of converting to a date and back:
select regexp_replace('20170202 ', '^(\d{4})(\d{2})(\d{2}) +$', '\3-\2-\1')
from dual;
REGEXP_REPLACE(
---------------
02-02-2017
Or you could use substr instead of regexp_substr, which may perform better even if you have to call it three times; using a CTE just to avoid repeating the value:
with t(str) as (
select '20170202 ' from dual
)
select substr(str, 7, 2) ||'-'|| substr(str, 5, 2) ||'-'|| substr(str, 1, 4)
from t;
SUBSTR(STR
----------
02-02-2017
If you do convert to a date and back you would uncover any values which cannot be converted, as they will cause an exception to be thrown. That would imply you have bad data; which would have been avoided by using the right data type in the first place, of course. These will convert any old rubbish, with varying results depending on how far the strings stray from the pattern you expect - but including strings like '20170231' which represent an invalid date. And null value or strings of just spaces will be converted to odd things with the substr version, but you could filter those out.
You can see the kind of variation you would get with some sample data that doesn't match your expectations:
with t(str) as (
select '20170202 ' from dual
union all select '20170231 ' from dual
union all select '2017020c ' from dual
union all select '2017020 ' from dual
union all select '201702021 ' from dual
union all select ' ' from dual
union all select null from dual
)
select str,
regexp_replace(str, '^(\d{4})(\d{2})(\d{2}) +$', '\3-\2-\1') as reg,
substr(str, 7, 2) ||'-'|| substr(str, 5, 2) ||'-'|| substr(str, 1, 4) as sub
from t;
STR REG SUB
------------- ------------- -------------
20170202 02-02-2017 02-02-2017
20170231 31-02-2017 31-02-2017
2017020c 2017020c 0c-02-2017
2017020 2017020 0 -02-2017
201702021 201702021 02-02-2017
- -
--
With the anchors and whitespace expectation, the regular expression doesn't modify anything that doesn't consist entirely of 8 numeric characters. But it can still form invalid 'dates'.

How to remove leading zeroes from day and month values in Oracle, when parsing to string using to_char function?

I want to retrieve a date without the leading zeroes in front of the day and month values in a select statement. If I execute the following query
select to_char(sysdate, 'dd.mm.yyyy') from dual;
I will get 21.03.2014 as a result. Moreover, if today was, for example, 7th of March, 2014, I would get 07.03.2014. How can I get rid of these leading zeroes?
select to_char(sysdate,'DD.MM.YY') -- Without Fill Mode
, to_char(sysdate-20,'fmDD.fmMM.YY') -- With Fill Mode, 20 days ago
from dual;
Returns
21.03.14 | 1.3.14
FM Fill mode.
In a datetime format element of a TO_CHAR function, this modifier suppresses blanks in subsequent character elements (such as MONTH) and suppresses leading zeroes for subsequent number elements (such as MI) in a date format model. Without FM, the result of a character element is always right padded with blanks to a fixed length, and leading zeroes are always returned for a number element. With FM, which suppresses blank padding, the length of the return value may vary.
Try this:
select to_char(to_date('20-oct-2000'),'fmmm/dd/fmrr') from dual
The above query will remove the leading zeroes from day and month values in Oracle.
This is quite far from being elegant but it works:
select to_number(to_char(sysdate,'dd')) || '.' || to_number(to_char(sysdate,'mm')) || '.' || to_number(to_char(sysdate,'yyyy')) from dual
Basically, you would convert to number each part of the date

Resources