Requesting help converting DB2 sql to Oracle - sql

Looking for a little help converting a query from DB2 to Oracle. I'm using an existing SQL query to add to a new report, but that report query has been written in DB2 SQL. I am trying to convert it to Oracle, and having issues with how the dates are setup.
trunc_timestamp(CASE ......
ELSE ((
CASE
WHEN
TRFRDATE IS NULL OR
TRFRTIME IS NULL
THEN
NULL
ELSE timestamp(substr(char(TRFRDATE), 1, 4) || '-' ||
substr(char(TRFRDATE), 5, 2) || '-' || substr(char(TRFRDATE), 7, 2)
|| '-' || substr(digits(TRFRTIME), 1, 2) || '.' ||
substr(digits(TRFRTIME), 3, 2) || '.' || substr(digits(TRFRTIME), 5, 2)
|| '.000000')
END) + (-1) DAY)
END, 'dd') AS "NewOrderDate",
As you can see above, I have a case statement that will be converted to a timestamp, the difficult part is the last part, where after calculating the timestamp, I need to subtract -1 day, and that's where I keep falling into one error or the other.
Just to be clear, the ELSE part of the above query is just concatenating the date and time and converting to timestamp. Any help figuring this out would be great.
EDIT: My query now is this, and im getting the following error: hour must be between 1 and 12
SELECT CASE
WHEN
CASE
WHEN
CARRSERV IN ('1DYT','1PRT','SATN')
THEN 1
WHEN
CARRSERV IN ('2DYT','SAT2')
THEN 2
ELSE 3
END = 1 AND
TO_CHAR(
CASE
WHEN TFRDATE IS NULL OR TFRTIME IS NULL THEN NULL
ELSE TO_TIMESTAMP(substr(TFRDATE, 1, 2) || '-' || substr(TFRDATE, 4, 3) || '-' ||
substr(TFRDATE, 8, 2) || ' ' || substr(TFRTIME, 1, 2) || '.' ||
substr(TFRTIME, 3, 2) || '.' || substr(TFRTIME, 5, 2))
END,'hh24:mi:ss') > TO_CHAR('17:00:00','hh24:mi:ss')
THEN
CASE
WHEN TFRDATE IS NULL OR TFRTIME IS NULL THEN NULL
ELSE TO_TIMESTAMP(substr(TFRDATE, 1, 2) || '-' || substr(TFRDATE, 4, 3) || '-' ||
substr(TFRDATE, 8, 2) || ' ' || substr(TFRTIME, 1, 2) || '.' ||
substr(TFRTIME, 3, 2) || '.' || substr(TFRTIME, 5, 2))
END
END AS NEW_DATE from table

If I get this right you're truncating any hour/minute/second/... portion anyway so just ignore it from the beginning.
You can simply use to_date() to convert a string into a date:
...
to_date(trfrdate, 'YYYYMMDD') - 1 AS "NewOrderDate"
...
But you should really consider to use appropriate data types and strings types aren't appropriate for date/times, date/time types are. You could for example have one date column instead of trfrdate and trfrtime.
And it's a CASE expression you have there, not a statement. SQL knows no control flow statements at all.

Related

SQL: Converting number to time

I have a database column having the following values
column (hh/mm/ss)
042336
050623
Now using sql i want to covert it to like
column
04:23:63:000
05:06:23:000
I have been trying to_date function but no success yet.
You have a string so you can use string operations to insert the additional characters:
select (substr(x, 1, 2) || ':' || substr(x, 3, 2) || ':' || substr(x, 5, 2) || ':000')
from (select '042336' as x from dual) t

Combining a CASE WHEN statement & a column containing strings of type "Column 1 || '_' || Column 2" in Oracle SQL

I have a table consisting of three columns, which are called the following:
1) Month
2) Store_Type
3) City
I need this table to be expanded to contain five columns and the two columns that I wish to be added are detailed below.
Firstly, the query needs to create a new column called Store_Code. The Store_Code columns job is to store a numerical value which corresponds to what type of store it is.
I presume this would done using a CASE WHEN statement of the type:
SELECT Month,Store_Type,City,
CASE
WHEN Store_Type = 'Corner Shop' THEN '1'
WHEN Store_Type = 'Megastore' THEN '2'
WHEN Store_Type = 'Petrol Station' THEN '3'
....
ELSE '10'
END Store_Code
FROM My_Table
After this is complete, I need to create a column known as "Store_Key". The values contained within the Store_Key column need to be of the following form:
"The Month For That Row""The Store Type For That Row""The City associated with that row"_"The Store Code for that row"
I imagine the best way to create this column would be to use a query similar to the following:
SELECT (My_Table.Month || '_' || My_Table.Store_Type || '_' || My_Table.City || '_' ||
My_Table.Store_Code)
FROM My_Table
What I need is for these two separate queries to be combined into one query. I imagine this could be done by sub-setting the different SELECT queries but I am open to and grateful for any alternative solutions.
Thank you for taking the time to read through this problem and all solutions are greatly appreciated.
Do the case expression part inside a derived table (the subquery):
SELECT (My_Table2.Month || '_' || My_Table2.Store_Type || '_' || My_Table2.City || '_' ||
My_Table2.Store_Code)
FROM
(
SELECT Month,Store_Type,City,
CASE
WHEN Store_Type = 'Corner Shop' THEN '1'
WHEN Store_Type = 'Megastore' THEN '2'
WHEN Store_Type = 'Petrol Station' THEN '3'
....
ELSE '10'
END Store_Code
FROM My_Table
) My_Table2
If this is you trying to populate your new columns, then you need an update statement. I would use two updates to ensure you get the store_case committed for your store_code. Otherwise if you're deriving it in real time, the subquery select answer would be the way to go.
update my_table
set store_case =
case store_type
when 'Corner Shop' then 1
when 'Megastore' THEN 2
when 'Petrol Station' THEN 3
...
else 10
end case;
commit;
update my_table
set store_code = Month || '_' || to_char(Store_Type) || '_' || City || '_' || Store_Code;
commit;
Why to use sub query? It can be done within single query as following:
SELECT My_Table.Month || '_' || My_Table.Store_Type || '_' || My_Table.City || '_' ||
CASE
WHEN Store_Type = 'Corner Shop' THEN '1'
WHEN Store_Type = 'Megastore' THEN '2'
WHEN Store_Type = 'Petrol Station' THEN '3'
....
ELSE '10'
END as result
FROM My_Table
or you can use DECODE function as following:
SELECT My_Table.Month || '_' || My_Table.Store_Type || '_' || My_Table.City || '_' ||
DECODE(Store_Type,
'Corner Shop', '1',
'Megastore', '2',
'Petrol Station', '3'
....,
'10') -- this is default value same as else part of the case statement
as result
FROM My_Table
Cheers!!

Oracle SQL developer shows invalid number for the code below

I'm trying to subtract two columns which holds "systimestamp" as the date and time value. i'm getting
'ORA-01722: invalid number'
as the error message. Would be of great help if somebody can help out.
I tried googling this error and it says the character string might not be a valid literal . It works fine for other records , only problem is when i'm trying to subtract it.
SELECT ETL_BATCH_ID,
ETL_BATCH_GROUP_NAME,
TO_CHAR(BATCH_START_TS,'DD-MON-YY')
|| ' '
||TO_CHAR(BATCH_START_TS,'HH24:MI:SS') "BATCH_START_TS",
TO_CHAR(DW_DM_END_TS ,'DD-MON-YY')
||' '
||TO_CHAR(DW_DM_END_TS , 'HH24:MI:SS') "DW_DM_END_TS" ,
(TO_CHAR(DW_DM_END_TS , 'HH24:MI:SS')) - (TO_CHAR(BATCH_START_TS,'HH24:MI:SS')) "COMPLETION_TIME"
FROM bi_etl.bi_etl_batch
WHERE ETL_BATCH_GROUP_NAME = 'CMD';
For ex ,
BATCH_START_TS || DW_DM_END_TS || COMPLETION_TIME
01-OCT-19 3:18:00 ||01-OCT-19 3:20:00 || 00:02:00
So the completion time is (DW_DM_END_TS) - (BATCH_START_TS) = COMPLETION_TIME
But it's throwing the particular error as shown above
Apply Substraction directly on those timestamp values :
select DW_DM_END_TS - BATCH_START_TS as "COMPLETION_TIME"
from bi_etl_batch;
COMPLETION_TIME
-000000000 00:02:00.000000
Demo
Substraction is impossible and has no sense among two string type values.
U cann't any arifmetic operation on char.
so change To_char -> To_date, or remove to_char if fields is date
For an exact result, You need to retrieve hour, minute and second from the difference in the timestamp , Like the following:
SELECT
ETL_BATCH_ID,
ETL_BATCH_GROUP_NAME,
BATCH_START_TS,
DW_DM_END_TS,
LPAD(EXTRACT(HOUR FROM COMPLETION_TIME), 2, 0)
|| ':'
|| LPAD(EXTRACT(MINUTE FROM COMPLETION_TIME), 2, 0)
|| ':'
|| LPAD(ROUND(EXTRACT(SECOND FROM COMPLETION_TIME)), 2, 0) AS COMPLETION_TIME
FROM
(
SELECT
ETL_BATCH_ID,
ETL_BATCH_GROUP_NAME,
TO_CHAR(BATCH_START_TS, 'DD-MON-YY')
|| ' '
|| TO_CHAR(BATCH_START_TS, 'HH24:MI:SS') "BATCH_START_TS",
TO_CHAR(DW_DM_END_TS, 'DD-MON-YY')
|| ' '
|| TO_CHAR(DW_DM_END_TS, 'HH24:MI:SS') "DW_DM_END_TS",
DW_DM_END_TS - BATCH_START_TS "COMPLETION_TIME"
FROM
BI_ETL.BI_ETL_BATCH
WHERE
ETL_BATCH_GROUP_NAME = 'CMD'
);
Example:
SQL> SELECT
2 LPAD(EXTRACT(HOUR FROM DIFF), 2 , 0) || ':' ||
3 LPAD(EXTRACT(MINUTE FROM DIFF), 2 , 0) || ':' ||
4 LPAD(ROUND(EXTRACT(SECOND FROM DIFF)), 2 , 0) as diff
5 FROM
6 (
7 SELECT
8 SYSTIMESTAMP - ( SYSTIMESTAMP - 2 / 1440 ) AS DIFF
9 FROM
10 DUAL
11 );
DIFF
--------------------------
00:02:01
SQL>
Cheers!!

Dynamically generate sql statement based on metadata about the data being queried

I would like to dynamically query data that is staged as a long string by defining how to read the string and how to split it up.
So I can define the data with the following elements
FIELD_NAME VARCHAR2(30) NOT NULL,
DATA_TYPE VARCHAR2(20) NOT NULL,
COLUMN_ID NUMBER NOT NULL,
FIELD_START_POS NUMBER,
FIELD_END_POS NUMBER,
FIELD_LEN NUMBER,
ROW_TYPE VARCHAR2(10),
DATE_MASK VARCHAR2(12)
sample data in this table
can I take that info to create a select that would look something like
SELECT CASE cd.data_type
WHEN 'DATE'
THEN
TO_DATE (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len), cd.date_mask)
WHEN 'NUMBER'
THEN
TO_NUMBER (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len))
ELSE
TRIM (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len))
END
AS cd.field_name
FROM staged_data sd, column_definitions cd
I am having difficulties trying to tie the 2 together.
I know I could pivot the column names in the definition out like so:
SELECT *
FROM column_definitions
PIVOT (max(field_name) FOR column_id IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20))
but this still results in many rows
My goal is to generate this statement so that is can be run via the EXECUTE IMMEDIATE so it could work for many different files just by defining how to read the string.
I also have the need to read different row types hence the row_type column which will be defined for the same file but had their own column order and columns.
So I have been able to generate a string that is the select I am looking for based on the metadata about the staged file like this:
DECLARE
select_items VARCHAR2 (4000);
BEGIN
FOR c IN ( SELECT *
FROM column_definitions
WHERE file_pk = 1 AND row_type = 1
ORDER BY column_id)
LOOP
IF c.data_type = 'NUMBER'
THEN
select_items :=
select_items
|| 'CASE WHEN is_number(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| ')) = ''TRUE'' THEN TO_NUMBER(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| ')) ELSE NULL END AS '
|| c.field_name
|| ',';
ELSIF c.data_type = 'DATE'
THEN
select_items :=
select_items
|| 'CASE WHEN ISDATE(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| '))=''true'' THEN TO_DATE(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| '),'''
|| c.date_mask
|| ''') ELSE NULL END AS '
|| c.field_name
|| ',';
ELSE
select_items :=
select_items
|| 'TRIM(SUBSTR(row_data,'
|| c.field_start_pos
|| ','
|| c.field_len
|| ')) AS '
|| c.field_name
|| ',';
END IF;
END LOOP;
select_items := SUBSTR (select_items, 1, LENGTH (select_items) - 1);
select_items :=
'SELECT '
|| select_items
|| ' FROM STAGED_FILE where row_type=1 AND rownum <= 1000;';
DBMS_OUTPUT.PUT_LINE (select_items);
END;
this spits out something like this:
SELECT CASE
WHEN is_number (SUBSTR (row_data, 1, 1)) = 'TRUE'
THEN
TO_NUMBER (SUBSTR (row_data, 1, 1))
ELSE
NULL
END
AS REC_TYPE_IND,
SUBSTR (row_data, 11, 4) AS SRVC_LOC,
CASE
WHEN ISDATE (SUBSTR (row_data, 15, 8)) = 'true'
THEN
TO_DATE (SUBSTR (row_data, 15, 8), 'YYYYMMDD')
ELSE
NULL
END
AS BEGIN_DT,
CASE
WHEN ISDATE (SUBSTR (row_data, 23, 8)) = 'true'
THEN
TO_DATE (SUBSTR (row_data, 23, 8), 'YYYYMMDD')
ELSE
NULL
END
AS END_DT,
SUBSTR (row_data, 31, 50) AS ID,
SUBSTR (row_data, 101, 2) AS COUNTY_CD,
SUBSTR (row_data, 103, 30) AS ADDR_LN_1,
SUBSTR (row_data, 133, 30) AS ADDR_LN_2,
SUBSTR (row_data, 163, 18) AS CITY,
SUBSTR (row_data, 181, 2) AS STATE_CD,
CASE
WHEN is_number (SUBSTR (row_data, 183, 5)) = 'TRUE'
THEN
TO_NUMBER (SUBSTR (row_data, 183, 5))
ELSE
NULL
END
AS ZIP_CD,
CASE
WHEN is_number (SUBSTR (row_data, 188, 4)) = 'TRUE'
THEN
TO_NUMBER (SUBSTR (row_data, 188, 4))
ELSE
NULL
END
AS ZIP_CD4,
CASE
WHEN is_number (SUBSTR (row_data, 192, 10)) = 'TRUE'
THEN
TO_NUMBER (SUBSTR (row_data, 192, 10))
ELSE
NULL
END
AS PHONE_NUM
FROM staged_FILE
WHERE row_type = 1 AND ROWNUM <= 1000;
Now off to solve how to dynamically create an associative array to stuff the data into or another way to work with the data.
In your example, you use a CASE statement. Your first expression has a DATE datatype, the second has NUMBER and the third is a VARCHAR2. From the documentation:
For a simple CASE expression, the expr and all comparison_expr values
must either have the same datatype or must all have a
numeric datatype.
Basically, you can't do this because there's no way to know at compile time what the datatype of the field_name column is.
This is not a straightforward problem to solve, since you don't know what your datatype is going to be until runtime. Even once you get a dynamic SQL statement, what sort of variable are you going to select the data into?
I think you're basically going to have to:
Using column_definitions, construct a string that contains a SQL statement appropriate for the data type in question.
Create a TYPE that contains members of all the possible resulting data types.
Use either EXECUTE IMMEDIATE or DBMS_SQL to parse and execute that string, then fetch the result into an instance of that type.
You may actually be best off not doing this via SQL at all. Instead, I would probably do the following:
Get the data type of interest from column_definitions.
Use SUBSTR to extract the region of interest from the string in staged_data.
Do something like:
.
l_token := SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len);
IF l_datatype = 'DATE' THEN
l_date := TO_DATE( l_token, 'yyyy-mm-dd' );
ELSIF l_datatype = 'NUMBER' THEN
l_number := TO_NUMBER( l_token);
....
END IF;
I would not expect high performance from this sort of approach.

ORACLE SQL to replace/lpadd '-' with zero(s) depending on the length of the integer after '-'

I have a Table column in oracle db where values vary as 4123456-1-2-3, 4123456-11-2-3, 4123456-2-221-3 etc
The requirement is to find '-' from the value and replace/padd it with zeros depending upon the length of the character/integer after '-'.
Meaning if the length of integer afer '-' is 1 then padd 5 zeros and if the length of integer after '-' is 2 then padd 4 zeros to it.
The output should be like
1. 4123456-000001-000002-000003
2. 4123456-000011-000002-000003
3. 4123456-000002-000221-000003
Please help me to figure how to frame the query , i am trying to use REGEXP_REPLACE and LPAD but unable to get through
This uses a combination of regex_substr() to split the string and lpad() to pad it with zeroes:
select (regexp_substr(val, '[^-]+', 1, 1) || '-' ||
lpad(regexp_substr(val, '[^-]+', 1, 2), 6, '0') || '-' ||
lpad(regexp_substr(val, '[^-]+', 1, 3), 6, '0') || '-' ||
lpad(regexp_substr(val, '[^-]+', 1, 4), 6, '0')
)