Related
I am working on to Sort the Revision column of an Oracle DB table in the asc order as per below.
At first the numeric revisions to be sorted (1,2,3,…).
Thereafter Alpha-Numeric to be sorted as following: A, B, B1, C, C1, C2,…,Y, Y2, Y3, Z, AA, AB,..,DA, …ZZ, etc.
Row_Number() in the SELECT statement to be filled with 1,2,3… for each document# (ABC, XYZ) after revision sorting out.
See the uploaded image for the required table.
I tried with SUBSTR , Order by, etc but failed to sort out as per above requirement.
Can someone help me on this ? Thanks!
As I understand your question, you want to put last revisions that contain only two characters and no digits.
You can use a conditional sort:
select
t.*,
row_number() over(
partition by doc#
order by
case when regexp_like(revision, '^\w\d?$') then 0 else 1 end,
revision
) rn
from t
order by doc#, rn
The regular expression describes a string starting with an alphanumeric character, optionally followed by a digit: these revisions should come first.
Demo on DB Fiddle:
with t as (
select 'ABC' doc#, '1' revision from dual
union all select 'ABC', '2' from dual
union all select 'ABC', '3' from dual
union all select 'ABC', 'A' from dual
union all select 'ABC', 'B' from dual
union all select 'ABC', 'B1' from dual
union all select 'ABC', 'C' from dual
union all select 'ABC', 'C1' from dual
union all select 'ABC', 'D' from dual
union all select 'ABC', 'AA' from dual
union all select 'ABC', 'AB' from dual
union all select 'ABC', 'BA' from dual
union all select 'ABC', 'DA' from dual
)
select
t.*,
row_number() over(
partition by doc#
order by
case when regexp_like(revision, '^\w\d?$') then 0 else 1 end,
revision
) rn
from t
order by doc#, rn
DOC# | REVISION | RN
:--- | :------- | -:
ABC | 1 | 1
ABC | 2 | 2
ABC | 3 | 3
ABC | A | 4
ABC | B | 5
ABC | B1 | 6
ABC | C | 7
ABC | C1 | 8
ABC | D | 9
ABC | AA | 10
ABC | AB | 11
ABC | BA | 12
ABC | DA | 13
There is well known old method: rpad(col, max-length, '0')
For example rpad(col, max(length(col)) over(), '0'
I am trying to sort a combination of string and number in descending order .
Input :
P9S1
P7S1
P13S1
P12S2
P10S1
Expected output:
P13S1
P12S2
P10S1
P9S1
P7S1
Here is what I tried
Sample code:
with
inputs (firmware) as (
select 'P9S1' from dual union all
select 'P7S1' from dual union all
select 'P13S1' from dual union all
select 'P12S2' from dual union all
select 'P10S1' from dual
)
select firmware
from inputs
order by
regexp_replace(firmware, '\d+\.\d+') desc ;
But this doesn't work as expected. Any help would be appreciated.
Thanks
You did not actually explain how the strings should turned to numbers.
This would work for your dataset:
order by to_number(regexp_replace(firmware, '\D', '')) desc
The idea is to remove all non-digits characters from the string, turn the resulting string to a number, and use it for sorting.
with inputs (firmware) as (
select 'P9S1' from dual union all
select 'P7S1' from dual union all
select 'P13S1' from dual union all
select 'P12S2' from dual union all
select 'P10S1' from dual
)
select firmware
from inputs
order by to_number(regexp_replace(firmware, '\D', '')) desc ;
| FIRMWARE |
| :------- |
| P13S1 |
| P12S2 |
| P10S1 |
| P9S1 |
| P7S1 |
I want to create a regular expression that replaces every character in a string except the last 2 with a '*'. For example:
'abcdefgh' --> '******gh'
I am using oracle's regexp_replace, I have written something like:
regexp_replace('dfdfdfdfsdf','(.*)(..)','*\2',1,0)
but it ends up with one "*"
dfdfdfdfsdf --> *df
I would appreciate your kind assistance
You can use LPAD.
select LPAD(SUBSTR('dfdfdfdfsdf',-2),LENGTH('dfdfdfdfsdf'),'*') from dual;
OUTPUT
*********df
CHECK LIVE DEMO HERE
So long as you are not worried about 1 or 2 character strings then you can use the regular expression .(..$)?:
Query
WITH test_data ( value ) AS (
SELECT NULL FROM DUAL UNION ALL
SELECT 'A' FROM DUAL UNION ALL
SELECT 'AB' FROM DUAL UNION ALL
SELECT 'ABC' FROM DUAL UNION ALL
SELECT 'ABCD' FROM DUAL UNION ALL
SELECT 'ABCDE' FROM DUAL UNION ALL
SELECT 'ABCDEF' FROM DUAL
)
SELECT value,
REGEXP_REPLACE(
value,
'.(..$)?',
'*\1'
)
FROM test_data
Outputs:
VALUE | REGEXP_REPLACE(VALUE,'.(..$)?','*\1')
:----- | :------------------------------------
null | null
A | *
AB | **
ABC | *BC
ABCD | **CD
ABCDE | ***DE
ABCDEF | ****EF
db<>fiddle here
You can try replacing this pattern by *:
.(?=.{2})
Live example: https://regex101.com/r/uueD6B/1
I try to pivot my table but keeping additional rows (in my example eeeeee ) Is there a way in Oracle SQL to do this?
select * from (
select
mat_table.material, attribute_table.attribute, attribute_table.value
from
mat_table mat_table
inner join
attribute_table on mat_table.rel= attribute_table.rel
where
material = 'Material_A'
)
material |attribute| value
_____________________________________
Material_A |aaaaaa |
Material_A |bbbbbb | hello
Material_A |cccccc | val_1
Material_A |dddddd | 2
Material_A |eeeeee | 15
Material_A |eeeeee | 16
Material_A |eeeeee | 24
when I use pivot under the where clause
pivot (
max(attribute) as max_value for attribute IN ( 'aaaaaa',
'bbbbbb',
'cccccc',
'dddddd',
'eeeeee'
))
I am getting closer to what I want but for eeeee I get only one value
material |aaaaaa | bbbbbb | cccccc | dddddd | eeeeee |
__________________________________________________________
Material_A | | hello | val_1 | 2 | 24 |
but what I want is something like
material |aaaaaa | bbbbbb | cccccc | dddddd | eeeeee_1 | eeeeee_2 | eeeeee_3 |
__________________________________________________________________________________
Material_A | | hello | val_1 | 2 | 15 16 | 24
If there are always 3 values for eeeeee then you can do it as following
SQL> with mat_table (material, attribute, value) as
2 (
3 select 'Material_A', 'aaaaaa', null from dual
4 union all select 'Material_A', 'bbbbbb', 'hello' from dual
5 union all select 'Material_A', 'cccccc', 'val_1' from dual
6 union all select 'Material_A', 'dddddd', '2' from dual
7 union all select 'Material_A', 'eeeeee', '15' from dual
8 union all select 'Material_A', 'eeeeee', '16' from dual
9 union all select 'Material_A', 'eeeeee', '24' from dual
10 )
11 select *
12 from (select t.*,
13 row_number() over(partition by attribute order by value) rn
14 from mat_table t)
15 pivot (max(value) for (attribute, rn) in
16 (
17 ('aaaaaa', 1), ('bbbbbb', 1), ('cccccc', 1), ('dddddd', 1),
18 ('eeeeee', 1), ('eeeeee', 2), ('eeeeee', 3)
19 ));
MATERIAL 'aaaa 'bbbb 'cccc 'dddd 'eeee 'eeee 'eeee
---------- ----- ----- ----- ----- ----- ----- -----
Material_A hello val_1 2 15 16 24
If, however, you expect Oracle to dynamically create columns for any number of values for eeeeee then that is not possible.
Please read detailed explanation here Oracle Dynamic Pivoting
You can generate XML for any combinations of attribute and value but if you want to display result using SQL then eventually all the columns must be specified (alternative approach is parsing XML on client side).
SQL> with mat_table (material, attribute, value) as
2 (
3 select 'Material_A', 'aaaaaa', null from dual
4 union all select 'Material_A', 'bbbbbb', 'hello' from dual
5 union all select 'Material_A', 'cccccc', 'val_1' from dual
6 union all select 'Material_A', 'dddddd', '2' from dual
7 union all select 'Material_A', 'eeeeee', '15' from dual
8 union all select 'Material_A', 'eeeeee', '16' from dual
9 union all select 'Material_A', 'eeeeee', '24' from dual
10 )
11 select material, x.*
12 from mat_table
13 pivot xml (count(*) as dummy for (attribute, value) in (any, any))
14 -- parsing output
15 , xmltable('/PivotSet' passing attribute_value_xml
16 columns
17 aaaaaa varchar2(10) path '/PivotSet/item[column="aaaaaa"]/column[2]',
18 bbbbbb varchar2(10) path '/PivotSet/item[column="bbbbbb"]/column[2]',
19 cccccc varchar2(10) path '/PivotSet/item[column="cccccc"]/column[2]',
20 dddddd varchar2(10) path '/PivotSet/item[column="dddddd"]/column[2]',
21 eeeeee_1 varchar2(10) path '/PivotSet/item[column="eeeeee"][1]/column[2]',
22 eeeeee_2 varchar2(10) path '/PivotSet/item[column="eeeeee"][2]/column[2]',
23 eeeeee_3 varchar2(10) path '/PivotSet/item[column="eeeeee"][3]/column[2]') x;
MATERIAL AAAAAA BBBBBB CCCCCC DDDDDD EEEEEE_1 EEEEEE_2 EEEEEE_3
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
Material_A hello val_1 2 15 16 24
In this case there is no guarantee that EEEEEE_1/EEEEEE_2/EEEEEE_3 will be 15/16/24 in exactly this order.
EDIT: TS commented that it did not work for multiple materials. So I expanded the answer to account for that.
You could simply concat a row_number (partioned by material, attribute) to the attribute in your first query. You can add ordening by value if you like. To account for multiple materials the row_number to also partitioned by material. This means that the same attributes for different materials will get the same name and will end up in the same column.
replace attribute_table.attribute with
concat(attribute_table.attribute,'_', row_number() over (partition by attribute_table.material, attribute_table.attribute order by attribute_table.attribute, attribute_table.value))
Complete code and result:
with mat_table as
(
select 'Material_A' as material, 'aaaaaa' as attribute, null as value
union all select 'Material_A', 'bbbbbb', 'hello'
union all select 'Material_A', 'cccccc', 'val_1'
union all select 'Material_A', 'dddddd', '2'
union all select 'Material_A', 'eeeeee', '15'
union all select 'Material_A', 'eeeeee', '16'
union all select 'Material_A', 'eeeeee', '24'
union all select 'Material_B' , 'aaaaaa', 'lol'
union all select 'Material_B', 'bbbbbb', 'hi'
union all select 'Material_B', 'cccccc', 'max_val'
union all select 'Material_B', 'dddddd', '4'
union all select 'Material_B', 'eeeeee', '67'
union all select 'Material_B', 'eeeeee', '99'
union all select 'Material_B', 'eeeeee', null
)
select *
from (
select t.material,
t.value ,
concat(t.attribute,'_', row_number() over (partition by t.material , t.attribute order by t.attribute, t.value)) as numbered_attribute
from mat_table t) as d
pivot (
max(d.value)
for numbered_attribute IN ( [aaaaaa_1],
[bbbbbb_1],
[cccccc_1],
[dddddd_1],
[eeeeee_1],
[eeeeee_2],
[eeeeee_3]
)) as total
order by total.material
Note: I used SQL-Server. Maybe you'll have to change some syntax like [eeeeee_3] => 'eeeeee_3'
Base table with numbered attributes:
Final result after pivot:
Please help me to confirm is that behavior explained below is a bug, or clearly explain why it's right.
There are a high probability that I misunderstood some concept, but now for me it looks like a bug.
All examples below simplified as much as possible to demonstrate core of the issue. Real situation is very complex, so only general answers and workarounds related to principle of query construction is acceptable.
You are welcome to ask clarifying questions in comments and i'll try to do my best to answer them.
Thank you for attention. :)
Question
Why in last Example (Example 5) collection instance in (select count(1) ... subquery from first row mapped to all rows of the table, while expected result is to map each collection instance to it's own row?
At the same time collections used in cardinality(...) expression chosen properly.
Same situation (not covered in examples) exists if constructed in this way collections used in from or where part of a query.
Test schema setup
(SQLFiddle)
create or replace type TabType0 as table of varchar2(100)
/
create table Table0( tab_str_field varchar2(100), tab_field TabType0)
nested table tab_field store as tab_field_table
/
insert into table0 (tab_str_field, tab_field) values (
'A',
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
) as TabType0)
)
/
insert into table0 (tab_str_field, tab_field) values (
'B',
cast(multiset(
select 'B' from dual union all
select 'C' from dual
) as TabType0)
)
/
insert into table0 (tab_str_field, tab_field) values (
'C',
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
) as TabType0)
)
/
insert into table0 (tab_str_field, tab_field) values (
'D',
cast(multiset(
select 'A' from dual
) as TabType0)
)
/
select 'Initial table data' caption from dual
/
select * from table0
/
table data:
| TAB_STR_FIELD | TAB_FIELD |
-----------------------------
| A | A,B,C |
| B | B,C |
| C | A,B,C,D |
| D | A |
Examples
Example 1 (SQLFiddle) - work with nested table fields - OK
select 'Work with nested table - OK' caption from dual
/
select
tab_field tab_field,
-- cardinality
cardinality(tab_field) tab_cardinality,
-- select from table field of current row
(select count(1) from table(tab_field)) tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(tab_field)
where column_value = tab_str_field
) same_value
from table0
/
results:
| TAB_FIELD | TAB_CARDINALITY | TAB_COUNT | SAME_VALUE |
--------------------------------------------------------
| A,B,C | 3 | 3 | A |
| B,C | 2 | 2 | B |
| A,B,C,D | 4 | 4 | C |
| A | 1 | 1 | (null) |
Example 2 (SQLFiddle) - work with constructed source data alone - OK
select 'Work with constructed source data alone - OK' caption from dual
/
with table_data_from_set as (
select
'A' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'B' tab_str_field,
cast(multiset(
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'C' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
) as TabType0) tab_field
from dual union all
select
'D' tab_str_field,
cast(multiset(
select 'A' from dual
) as TabType0) tab_field
from dual
)
select
tab_field tab_field,
-- cardinality
cardinality(tab_field) tab_cardinality,
-- select from table field of current row
(select count(1) from table(tab_field)) tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(tab_field)
where column_value = tab_str_field
) same_value
from table_data_from_set
/
results:
| TAB_FIELD | TAB_CARDINALITY | TAB_COUNT | SAME_VALUE |
--------------------------------------------------------
| A,B,C | 3 | 3 | A |
| B,C | 2 | 2 | B |
| A,B,C,D | 4 | 4 | C |
| A | 1 | 1 | (null) |
Example 3 (SQLFiddle) - join table with multisets constructed in WITH - OK
select 'Join table with multisets constructed in WITH - OK' caption from dual
/
with table_data_from_set as (
select
'A' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'B' tab_str_field,
cast(multiset(
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'C' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
) as TabType0) tab_field
from dual union all
select
'D' tab_str_field,
cast(multiset(
select 'A' from dual
) as TabType0) tab_field
from dual
)
select
table0.tab_field table0_tab_field,
table_data_from_set.tab_field set_tab_field,
-- cardinality
cardinality(table0.tab_field) table0_tab_cardinality,
cardinality(table_data_from_set.tab_field) set_tab_cardinality,
-- select from table field of current row
(select count(1) from table(table_data_from_set.tab_field)) set_tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(table_data_from_set.tab_field)
where column_value = table0.tab_str_field
) same_value
from
table0,
table_data_from_set
where
table_data_from_set.tab_str_field = table0.tab_str_field
/
results:
| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
| A,B,C | A,B,C | 3 | 3 | 3 | A |
| B,C | B,C | 2 | 2 | 2 | B |
| A,B,C,D | A,B,C,D | 4 | 4 | 4 | C |
| A | A | 1 | 1 | 1 | (null) |
Example 4 (SQLFiddle) - join table with multisets constructed in WITH + subquery - OK
select 'Join table with multisets constructed in WITH and subquery - OK' caption from dual
/
with table_data_from_set as (
select
'A' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'B' tab_str_field,
cast(multiset(
select 'B' from dual union all
select 'C' from dual
) as TabType0) tab_field
from dual union all
select
'C' tab_str_field,
cast(multiset(
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
) as TabType0) tab_field
from dual union all
select
'D' tab_str_field,
cast(multiset(
select 'A' from dual
) as TabType0) tab_field
from dual
)
select
table0_tab_field table0_tab_field,
set_tab_field set_tab_field,
-- cardinality
cardinality(table0_tab_field) table0_tab_cardinality,
cardinality(set_tab_field) set_tab_cardinality,
-- select from table field of current row
(select count(1) from table(set_tab_field)) set_tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(set_tab_field)
where column_value = table0_tab_str_field
) same_value
from (
select
table0.tab_str_field table0_tab_str_field,
table0.tab_field table0_tab_field,
table_data_from_set.tab_str_field set_tab_str_field,
table_data_from_set.tab_field set_tab_field
from
table0,
table_data_from_set
where
table_data_from_set.tab_str_field = table0.tab_str_field
)
/
results:
| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
| A,B,C | A,B,C | 3 | 3 | 3 | A |
| B,C | B,C | 2 | 2 | 2 | B |
| A,B,C,D | A,B,C,D | 4 | 4 | 4 | C |
| A | A | 1 | 1 | 1 | (null) |
Example 5 (SQLFiddle) - join table with multisets constructed on the fly - FAILED
select 'Join table with multisets constructed on the fly - FAIL (set_tab_count wrong)' caption from dual
/
with string_set as (
select 'A' str_field from dual union all
select 'B' str_field from dual union all
select 'C' str_field from dual union all
select 'D' str_field from dual union all
select 'E' str_field from dual
)
select
table0_tab_field table0_tab_field,
set_tab_field set_tab_field,
-- cardinality
cardinality(table0_tab_field) table0_tab_cardinality,
cardinality(set_tab_field) set_tab_cardinality,
-- select from table field of current row
(select count(1) from table(set_tab_field)) set_tab_count,
-- select from field of current row while joining
-- with another field of same row
( select column_value from table(set_tab_field)
where column_value = table0_tab_str_field
) same_value
from (
select
table0.tab_str_field table0_tab_str_field,
table0.tab_field table0_tab_field,
(
cast(multiset(
select
string_set.str_field
from
string_set,
table(table0.tab_field) tab_table
where
string_set.str_field = tab_table.column_value
) as TabType0)
) set_tab_field
from
table0
)
/
result (all values in set_tab_count column are same - wrong! ) :
| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
| A,B,C | A,B,C | 3 | 3 | 3 | A |
| B,C | B,C | 2 | 2 | 3 | B |
| A,B,C,D | A,B,C,D | 4 | 4 | 3 | C |
| A | A | 1 | 1 | 3 | (null) |
Oracle version information
Instance 1
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for IBM/AIX RISC System/6000: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
Instance 2
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Express Edition Release 11.2.0.2.0 - Production
PL/SQL Release 11.2.0.2.0 - Production
CORE 11.2.0.2.0 Production
TNS for 32-bit Windows: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
SQLFiddle with all queries together.
It's a bug. Adding a /*+ NO_MERGE */ hint to the second inline view in the last example will generate the expected results. See this SQL Fiddle for an example. Regardless of the query, that hint should never change the results. There are a couple of other seemingly unrelated changes you can make that will generate the correct results, such as removing some of the columns, or adding an unused ROWNUM in the middle.
Oracle is re-writing your query to optimize it, but doing something wrong. You could probably get some more information by tracing the query, but I doubt you'll be able to truly fix the issue. Work around it for now and submit a service request to Oracle so they can create a bug and eventually fix it.