Convert single row result into two column result. Unpivot error 'expression must have same datatype as corresponding expression' - oracle

Please read only EDIT section which is more relevant now.
I have table in oracle 12c database, say table_1.
I have to run a simple SQL which returns only 1 row and all columns from from table_1. Say like
select * from table_1 where col_1 = 65
But the result I want is like:
I did some search and found this link which is very similar to what i want.
But I was only able to get the first column in the expected result by folowing it. I am unable to get actual data in the second column of the expected result.
So far I was able to write only:
select v
from (
select 'col_1' as col_1, 'col_2' as col_2, 'col_3' as col_3, 'col_4' as col_4 from dual
) t
unpivot
(
v for val in (col_1,col_2,col_3,col_4)
) u;
How can I add the second column and the condition where col_1 = 65?
EDIT: (Above part is less relevant now)
I fount this link where there us unpivot example which I could use.
select * from olympic_medal_tables
Gives:
desc olympic_medal_tables
Name Null? Type
------------- ----- -----------
NOC VARCHAR2(3)
GOLD_MEDALS NUMBER
SILVER_MEDALS NUMBER
BRONZE_MEDALS NUMBER
Following SQL gives me what I could use, but I don't want the NOC column in the result:
select * from olympic_medal_tables
unpivot (medal_count for medal_colour in (
gold_medals as 'GOLD',
silver_medals as 'SILVER',
bronze_medals as 'BRONZE'
));
Result:
So when I add NOC as well (so it is not added as a column in the result) like:
select * from olympic_medal_tables
unpivot (medal_count for medal_colour in (
gold_medals as 'GOLD',
silver_medals as 'SILVER',
bronze_medals as 'BRONZE',
noc as 'NOC'
));
I get error: ORA-01790: expression must have same datatype as corresponding expression on line 6 Error at Line: 44 Column: 3
I have tried using TO_NUMBER and TO_CHAR as well but then I get various syntax errors.
Question: How can I get expected result with just 2 columns. Is it possible with this approach using unpivot?

Here's one option, which requires you to first select one row, and then apply UNPIVOT to it:
SQL> with test (col1, col2, col3, col4) as
2 (select '12', 'hfkds' , 'hk435k' , '32' from dual union
3 select '34', 'ldkfgj', 'fsjd4653', '324' from dual union
4 select '65', 'ifd' , 'dkfjs345', '23' from dual union
5 select '87', 'dg' , '345jh' , '65' from dual
6 ),
7 one_row as
8 (select * From test
9 where col1 = '65'
10 )
11 select *
12 from one_row
13 unpivot (col_value for col_name in
14 (col1 as 'col1', col2 as 'col2', col3 as 'col3', col4 as 'col4'));
COL_ COL_VALU
---- --------
col1 65
col2 ifd
col3 dkfjs345
col4 23
SQL>

Related

How to sort texts with '_' in Oracle exactly like EXCEL?

In Excel When I sort the texts in ascending order, results shows as below. Text with the underscore character precedes the others. And in Excel cell, when I type in ="_" < "A", then "True" shows as expected.
C10_
C10A
C20_
C20A
But, In Oracle, when I sort in ascending order, results shows as below.
(I guess, Oracle treats '_' < 'A' False)
C10A
C10_
C20A
C20_
How can I make Oracle sort the list exactly as Excel does? I have changed ASC to DESC, but the result was not what I expect.
My sorting code is as below,
WITH DATAA AS (
SELECT *
FROM
(
SELECT 'C10_'rr FROM DUAL
UNION
SELECT 'C10A' rr FROM DUAL
UNION
SELECT 'C20_' rr FROM DUAL
UNION
SELECT 'C20A' rr FROM DUAL
)
)
SELECT *
FROM DATAA
ORDER BY rr ASC;
You can achieve this by altering the sorting method using NLS_SORT as following:
According to ORACLE Documentation:
NLS_SORT specifies the type of sort for character data. It overrides
the default value that is derived from NLS_LANGUAGE.
NLS_SORT contains either of the following values:
NLS_SORT = BINARY | sort_name
BINARY specifies a binary sort. sort_name specifies a linguistic sort
sequence.
Here is how you can achieve the result.
SQL> -- Your original query
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10A
C10_
C20A
C20_
--
SQL> -- Now altering the sorting method
SQL> --
SQL> --
SQL> alter session set NLS_SORT = German;
Session altered.
SQL> --
SQL> --
SQL> -- Now see the result
SQL> --
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10_
C10A
C20_
C20A
SQL>
Cheers!!

How to select second split of column data from oracle database

I want to select the data from a Oracle table, whereas the table columns contains the data as , [ex : key,value] separated values; so here I want to select the second split i.e, value
table column data as below :
column_data
++++++++++++++
asper,worse
tincher,good
golder
null -- null values need to eliminate while selection
www,ewe
from the above data, desired output like below:
column_data
+++++++++++++
worse
good
golder
ewe
Please help me with the query
According to data you provided, here are two options:
result1: regular expressions one (get the 2nd word if it exists; otherwise, get the 1st one)
result2: SUBSTR + INSTR combination
SQL> with test (col) as
2 (select 'asper,worse' from dual union all
3 select 'tincher,good' from dual union all
4 select 'golder' from dual union all
5 select null from dual union all
6 select 'www,ewe' from dual
7 )
8 select col,
9 nvl(regexp_substr(col, '\w+', 1, 2), regexp_substr(col, '\w+', 1,1 )) result1,
10 --
11 nvl(substr(col, instr(col, ',') + 1), col) result2
12 from test
13 where col is not null;
COL RESULT1 RESULT2
------------ -------------------- --------------------
asper,worse worse worse
tincher,good good good
golder golder golder
www,ewe ewe ewe
SQL>

Select default in case of no value returned

I am trying to get some default value in my resultset if query does not return anything. I am trying nvl for the same but it is not returning the expected default value. To simulate, Consider following query,
select nvl(null, '10') from dual where 1=0;
I want to get 10 in case of given condition is not true and query does not return any value. However above query not returning any row.
Your query returns zero rows. NVL() isn't going to change that (*).
The correct solution is for the program which executes the query to handle NO_DATA_FOUND exception rather than fiddling the query.
However, you need a workaround so here is one using two sub-queries, one for your actual query, one to for the default.
When your_query returns an empty set you get this:
SQL> with your_qry as
2 ( select col1 from t42 where 1=0 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
10
SQL>
And when it returns a row you get this:
SQL> with your_qry as
2 ( select col1 from t42 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
12
13
SQL>
The WITH clause is optional here, it just makes it easier to write the query without duplication. This would have the same outcome:
select col1
from t42
where col0 is null
union all
select 10
from dual
where not exists (select col1
from t42
where col0 is null)
;
(*) Okay, there are solutions which use NVL() or COALESCE() with aggregations to do this. They work with single column projections in a single row as this question poses, but break down when the real query has more than one row and/or more than one column. Aggregations change the results.
So this looks alright ...
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual where 1=0 union all
3 select 'X' as col0, 13 as col1 from dual where 1=0 )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Y 10
SQL>
... but this not so much:
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual union all
3 select 'X' as col0, 13 as col1 from dual )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Z 13
SQL>
May be something like this is what you need
You could change WHERE clause (in this case WHERE COL > 1) similarly in both places.
WITH T(COL) AS(
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL
)
SELECT COL FROM T WHERE COL > 1
UNION ALL
SELECT 10 AS COL FROM DUAL WHERE NOT EXISTS( SELECT 1 FROM T WHERE COL > 1)
You can use aggregation. An aggregation query always returns one row:
select coalesce(max(null), '10')
from dual
where 1 = 0;
I prefer coalesce() to nvl() because coalesce() is the ANSI standard function. But, nvl() would work here just as well.

Match characters in oracle sql where condition

I want to match Col1 from Table a to colum1 from table B.
A B
123 123-ab
234 234-bc
3443 3443-dd
However, value in table b has concatenated data. I want to match only the characters until before special character occurs(-).
I tried this : substr(table1.a,1,3) = substr(table2.b,1,3)
But this doesn’t work as some values have 4 digits.
use join and substr
select * from table_a
inner join table_b on table_a.col_a = substr(table_b.col_b, 1, length(table_a.col_a);
Using REGEXP_SUBSTR() to match on one or more numbers from the beginning of the string up to but not including the first hyphen:
SQL> with a(col1) as (
select '123' from dual union
select '234' from dual union
select '3443' from dual
),
b(col1) as (
select '123-ab' from dual union
select '234-bc' from dual union
select '3443-dd' from dual
)
select a.col1, b.col1
from a, b
where a.col1 = regexp_substr(b.col1, '^(\d+)-', 1, 1, NULL, 1);
COL1 COL1
---- -------
123 123-ab
234 234-bc
3443 3443-dd
SQL>

remove a varchar2 string from the middle of table data values

Data in the file_name field of the generation table should be an assigned number, then _01, _02, or _03, etc. and then .pdf (example 82617_01.pdf).
Somewhere, the program is putting a state name and sometimes a date/time stamp, between the assigned number and the 01, 02, etc. (82617_ALABAMA_01.pdf or 19998_MAINE_07-31-2010_11-05-59_AM.pdf or 5485325_OREGON_01.pdf for example).
We would like to develop a SQL statement to find the bad file names and fix them. In theory it seems rather simple to find file names that include a varchar2 data type and remove it, but putting the statement together is beyond me.
Any help or suggestions appreciated.
Something like:
UPDATE GENERATION
SET FILE_NAME (?)
WHERE FILE_NAME (?...LIKE '%STRING%');?
You can find the problem rows like this:
select *
from Files
where length(FILE_NAME) - length(replace(FILE_NAME, '_', '')) > 1
You can fix them like this:
update Files
set FILE_NAME = SUBSTR(FILE_NAME, 1, instr(FILE_NAME, '_') -1) ||
SUBSTR(FILE_NAME, instr(FILE_NAME, '_', 1, 2))
where length(FILE_NAME) - length(replace(FILE_NAME, '_', '')) > 1
SQL Fiddle Example
You can also use Regexp_replace function:
SQL> with t1(col) as(
2 select '82617_mm_01.pdf' from dual union all
3 select '456546_khkjh_89kjh_67_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col
8 , regexp_replace(col, '^([0-9]+)_(.*)_(\d{2}\.pdf)$', '\1_\3') res
9 from t1;
COL RES
-------------------------------------- -----------------------------------------
82617_mm_01.pdf 82617_01.pdf
456546_khkjh_89kjh_67_01.pdf 456546_01.pdf
19998_MAINE_07-31-2010_11-05-59_AM.pdf 19998_MAINE_07-31-2010_11-05-59_AM.pdf
5485325_OREGON_01.pdf 5485325_01.pdf
To display good or bad data regexp_like function will come in handy:
SQL> with t1(col) as(
2 select '826170_01.pdf' from dual union all
3 select '456546_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col bad_data
8 from t1
9 where not regexp_like(col, '^[0-9]+_\d{2}\.pdf$');
BAD_DATA
--------------------------------------
19998_MAINE_07-31-2010_11-05-59_AM.pdf
5485325_OREGON_01.pdf
SQL> with t1(col) as(
2 select '826170_01.pdf' from dual union all
3 select '456546_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col good_data
8 from t1
9 where regexp_like(col, '^[0-9]+_\d{2}\.pdf$');
GOOD_DATA
--------------------------------------
826170_01.pdf
456546_01.pdf
To that end your update statement might look like this:
update your_table
set col = regexp_replace(col, '^([0-9]+)_(.*)_(\d{2}\.pdf)$', '\1_\3');
--where clause if needed

Resources