How can I query to get the rows according to the certain column's value in oracle? [duplicate] - oracle

This question already has an answer here:
how to duplicate my sql results? [duplicate]
(1 answer)
Closed 2 years ago.
Table A is:
--------------
C1 C2
--------------
A 3
B 2
--------------
select * from
(
select 'A' as C1, 3 as C2 from dual
union all
select 'B' as C1, 2 as C2 from dual
)
I want to get the following result view with one query statement:
--------------
C1 N1
--------------
A 1
A 2
A 3
B 1
B 2
--------------
I need to generate rows as many as C2 value
Is this possible?
Thank you.

We can handle this via the use of a calendar/sequence table. Consider:
WITH nums AS (
SELECT 1 AS val FROM dual UNION ALL
SELECT 2 FROM dual UNION ALL
SELECT 3 FROM dual
)
SELECT
a.C1,
n.val AS N1
FROM TableA a
INNER JOIN nums n
ON n.val <= a.C2
ORDER BY
a.C1,
n.val;
Demo
Note that in practice, you might use a dedicated table containing a sequence of numbers to cover all possible values in your table. Or, you might use an Oracle sequence.

Alternatively:
SQL> with test as
2 (select 'A' as C1, 3 as C2 from dual
3 union all
4 select 'B' as C1, 2 as C2 from dual
5 )
6 select c1, column_value n1
7 from test cross join table(cast(multiset(select level from dual
8 connect by level <= c2
9 ) as sys.odcinumberlist))
10 order by c1, column_value;
C N1
- ----------
A 1
A 2
A 3
B 1
B 2
SQL>

Related

Oracle Apex - Format number in millions

Is there a way to use Currency Format (or some other standard formatter) to format numbers like this in Oracle Apex:
1,000,000 => 1.00M
1,234,567 => 1.23M
1,234,567,890 => 1234.57M
Not declaratively, as far as I can tell. But, you can do it yourself (perhaps).
col is original value
c1 is that number (col) divided by a million
c2 rounds the result to the 2nd decimal (which is OK, but - decimals are missing (see 1)
c3 uses to_char function
final_result is c3 concatenated with an M
Note that col, c1 and c2 are numbers, while c3 and final_result are strings.
SQL> with test (col) as
2 (select 1000000 from dual union all
3 select 1234567 from dual union all
4 select 1234567890 from dual
5 )
6 select col,
7 col/1e6 c1,
8 round(col/1e6, 2) c2,
9 to_char(col/1e6, 'fm99990D00') c3,
10 --
11 to_char(col/1e6, 'fm99990D00') || 'M' final_result
12 from test;
COL C1 C2 C3 FINAL_RESULT
---------- ---------- ---------- --------- ---------------
1000000 1 1 1,00 1,00M
1234567 1,234567 1,23 1,23 1,23M
1234567890 1234,56789 1234,57 1234,57 1234,57M
SQL>

How to change rownum only if the next row is distinct

I have this
RowNum
Value
1
X
2
X
3
Y
4
Z
5
Z
6
Z
7
V
and I want something like this
RowNum
Value
1
X
1
X
2
Y
3
Z
3
Z
3
Z
4
V
How can I do that in Oracle?
Thanks
Here is one way - using the match_recognize clause, available since Oracle 12.1. Note that rownum is a reserved keyword, so it can't be a column name; I changed it to rnum in the input and rn in the output, adapt as needed.
The with clause is not part of the query - it's there just to include your sample data. Remove it before using the query on your actual data.
with
inputs (rnum, value) as (
select 1, 'X' from dual union all
select 2, 'X' from dual union all
select 3, 'Y' from dual union all
select 4, 'Z' from dual union all
select 5, 'Z' from dual union all
select 6, 'Z' from dual union all
select 7, 'V' from dual
)
select rn, value
from inputs
match_recognize (
order by rnum
measures match_number() as rn
all rows per match
pattern ( a+ )
define a as value = first(value)
);
RN VALUE
-- -----
1 X
1 X
2 Y
3 Z
3 Z
3 Z
4 V
In Oracle 11.2 and earlier, you can use the start-of-group method (the flags created in the subquery and counted in the outer query):
select count(flag) over (order by rnum) as rn, value
from (
select rnum, value,
case lag(value) over (order by rnum)
when value then null else 1 end as flag
from inputs
)
;

Oracle: How functional table works in cross join?

I explored technique to unfold comma separated list in column into rows:
with tbl as (
select 1 id, 'a,b' lst from dual
union all select 2 id, 'c' lst from dual
union all select 3 id, 'e,f,g' lst from dual)
select
tbl.ID
, regexp_substr(tbl.lst, '[^,]+', 1, lvl.column_value) elem
, lvl.column_value lvl
from
tbl
, table(cast(multiset(
select level from dual
connect by level <= regexp_count(tbl.lst, ',')+1) as sys.odcinumberlist)) lvl;
Result is:
ID ELEM LVL
1 a 1
1 b 2
2 c 1
3 e 1
3 f 2
3 g 3
As you can see LVL depends on value of regexp_count, so second functional table in cross join is parametrized by first table.
How is it working? How is it called? Can I paramertize third table based on two preceding in cross join and so forth?
Is parametrization limited to cross join or can be applied in join syntax too?
Reference: Splitting string into multiple rows in Oracle
From the documentation:
LATERAL
Specify LATERAL to designate subquery as a lateral inline
view. Within a lateral inline view, you can specify tables that appear
to the left of the lateral inline view in the FROM clause of a query.
You can specify this left correlation anywhere within subquery (such
as the SELECT, FROM, and WHERE clauses) and at any nesting level.
-- a variation of the query in your question ...
select
dt.id
, dt.list
, regexp_substr( dt.list, '[^,]+', 1, dt2.lvl ) elements
, dt2.lvl
from (
select 1 id, 'a,b' list from dual union all
select 2, 'c' from dual union all
select 3, 'e,f,g' from dual
) dt, lateral (
select level lvl from dual
connect by level <= regexp_count(dt.list, ',') + 1
) dt2
;
-- output
ID LIST ELEMENTS LVL
1 a,b a 1
1 a,b b 2
2 c c 1
3 e,f,g e 1
3 e,f,g f 2
3 e,f,g g 3
Example with 3 tables:
--drop table t1 ;
--drop table t2 ;
--drop table t3 ;
-- tables/data
create table t1
as
select 1 id, 'a' letter from dual union all
select 2, 'b' from dual union all
select 3, 'c' from dual ;
create table t2
as
select 1 id, 'd' letter from dual union all
select 2, 'e' from dual union all
select 3, 'f' from dual ;
create table t3
as
select 1 id, 'g' letter from dual union all
select 2, 'h' from dual union all
select 3, 'i' from dual ;
-- query
select *
from
t1
, lateral ( select letter from t2 where id = t1.id ) t2
, lateral ( select letter from t3 where id = t2.id )
;
-- output
ID LETTER LETTER LETTER
1 a d g
2 b e h
3 c f i
Also (using the same tables)
-- reference t1 <- t2,
-- reference t1 and t2 <- t3
select *
from
t1
, lateral ( select letter from t2 where id = t1.id ) t2
, lateral ( select letter || t1.letter from t3 where id = t2.id )
;
-- output
ID LETTER LETTER LETTER||T1.LETTER
1 a d ga
2 b e hb
3 c f ic
Whereas a "standard" cross join would give us ...
select *
from
t1 cross join t2 cross join t3
;
ID LETTER ID LETTER ID LETTER
1 a 1 d 1 g
1 a 1 d 2 h
1 a 1 d 3 i
1 a 2 e 1 g
1 a 2 e 2 h
1 a 2 e 3 i
...
-- 27 rows
Related topics: CROSS APPLY (see documentation and examples here).

sum of column based on distinct value of other column in Oracle

Table A
A1 A2
1 7
2 8
1 9
Table B
A1 B2
1 2
2 3
i want something like this
select A.A1,sum(case when distinct A.A1 then B2),sum(A.A2) from
A,B
where A.A1=B.A1(+)
group by A.A1
After joining my table will be
A1 A2 B2
1 7 2
2 8 3
1 9 2
Resulting Table
A1 A2 B2
1 7+9 2(only once)
2 8 3
how to get sum of B2 when distinct A1 after joining the tables as stated above.
Thanks in advance
Use JOIN and GROUP BY.
Query
SELECT t1.A1, SUM(t1.A2) AS A1, SUM(t2.B2) AS B2
FROM TableA t1
JOIN TableB t2
ON t1.A1 = t2.A1
GROUP BY t1.A1;
Since table_b.a1 is unique, the best way to do this would be to work out the sum of table_a.a2 first to reduce the number of rows you're joining against, and then join to table_b. Then you don't need to worry about summing the distinct table_b.b2 values, which you would otherwise have to do.
WITH table_a AS (SELECT 1 a1, 7 a2 FROM dual UNION ALL
SELECT 2 a1, 8 a2 FROM dual UNION ALL
SELECT 1 a1, 9 a2 FROM dual),
table_b AS (SELECT 1 a1, 2 b2 FROM dual UNION ALL
SELECT 2 a1, 3 b2 FROM dual)
-- end of mimicking your two tables with sample_data in them;
-- see the sql below:
SELECT ta.a1,
ta.a2,
tb.b2
FROM (SELECT a1, SUM(a2) a2
FROM table_a
GROUP BY a1) ta
INNER JOIN table_b tb ON ta.a1 = tb.a1;
A1 A2 B2
---------- ---------- ----------
1 16 2
2 8 3
If you absolutely must join the two tables first (I don't recommend; this is making more work for the database to do), then you could do something like:
WITH table_a AS (SELECT 1 a1, 7 a2 FROM dual UNION ALL
SELECT 2 a1, 8 a2 FROM dual UNION ALL
SELECT 1 a1, 9 a2 FROM dual),
table_b AS (SELECT 1 a1, 2 b2 FROM dual UNION ALL
SELECT 2 a1, 3 b2 FROM dual)
SELECT ta.a1,
SUM(ta.a2) a2,
MAX(tb.b2) b2
FROM table_a ta
INNER JOIN table_b tb ON ta.a1 = tb.a1
GROUP BY ta.a1;
A1 A2 B2
---------- ---------- ----------
1 16 2
2 8 3
Since there can only be one distinct value for table_b.b2 per table_a.a1, we can just pick one of the values to use via MAX (we could have used MIN or SUM(distinct tb.b2) instead, fyi).

Build dynamic query in oracle

Could you help me building a query as below?
TABLE_A
id brand color size model yn_buy
1 A blue M A -
2 A grey X C -
3 B red X B -
4 C blue S C -
TABLE_B
brand critery
A color=grey and size=X
B color=red
C size=M
I want to build a query to update table A and the answer should be:
TABLE_A
id brand color size model yn_buy
1 A blue M A N
2 A grey X C Y
3 B red X B Y
4 C blue S C N
As you can see the data on the column "critery" should be the decisor to buy or not
I'd like to use a single merge, like the following
merge into TABLE_A a
using
(
select id, brand, CASE WHEN critery THEN 'Y' ELSE 'N' END yn_buy
TABLE_A a
left join TABLE_B b ON a.brand = b.brand
) b
ON (a.id = b.id)
WHEN MATCHED THEN UPDATE set a.yn_buy = b.yn_buy
Is it possible to do something like this? maybe using execute immediate, some kind of bind...?
thank you
If I don't miss something in your particular case you don't need dynamic SQL - you can simply change TABLE_B structure and use static MERGE (because I don't know how complex your criteria can be this is just an illustration):
SQL> create table table_a (id, brand, color, size#, model#, tn_buy) as
2 select 1, 'A', 'blue', 'M', 'A', cast(null as varchar2(3)) from dual union all
3 select 2, 'A', 'grey', 'X', 'C', cast(null as varchar2(3)) from dual union all
4 select 3, 'B', 'red', 'X', 'B', cast(null as varchar2(3)) from dual union all
5 select 4, 'C', 'blue', 'S', 'C', cast(null as varchar2(3)) from dual
6 /
Table screated.
SQL> create table TABLE_B (brand, color, size#)
2 as
3 select 'A', 'grey', 'X' from dual union all
4 select 'B', 'red', null from dual union all
5 select 'C', null, 'M' from dual
6 /
Table created.
SQL> merge into TABLE_A a USING (
2 select a.id, a.brand, CASE
3 WHEN a.color = nvl(a.color, a.color) and a.size# = nvl(b.size#,a.size#)
4 THEN 'Y' ELSE 'N' END yn_buy FROM
5 TABLE_A a
6 left join TABLE_B b ON a.brand = b.brand
7 ) b
8 ON (a.id = b.id)
9 WHEN MATCHED THEN UPDATE set a.yn_buy = b.yn_buy
10 /
4 rows merged.
SQL> select * from table_a order by id;
ID B COLO S M YN_
---------- - ---- - - ---
1 A blue M A N
2 A grey X C Y
3 B red X B Y
4 C blue S C N
But of your criteria are difficult enough to be implemented using simple static condition, then you can use dynamic SQL:
SQL> create table TABLE_B1 (brand, criteria)
2 as
3 select 'A', q'[color='grey' and size# in ('X','M')]' from dual union all
4 select 'B', q'[color = 'red']' from dual union all
5 select 'C', q'[size#='M']' from dual
6 /
Table created.
SQL> update table_a set yn_buy = null;
4 rows updated.
SQL> commit;
Committed.
SQL> begin
2 for cur in (select brand, criteria from table_b1) loop
3 execute immediate
4 'update table_a set yn_buy = case when '||cur.criteria||
5 q'[ then 'Y' else 'N' end where brand = :1]' using cur.brand;
6 end loop;
7 end;
8 /
PL/SQL procedure completed.
SQL> select * from table_a;
ID B COLO S M YN_
---------- - ---- - - ---
1 A blue M A N
2 A grey X C Y
3 B red X B Y
4 C blue S C N

Resources