Calculation of precalculated values PL/SQL - oracle

i'm trying to do two calculations in one procedure in pl/sql. The simple version would look like
select a1+a2
into a
from table c;
select b1+b2
into b
from table c;
...
in the end i would like to further calculate the results of a and b, so let's say
select (a*b)
into c
from x
insert
into x (A,B,C)
values (a,b,c)
would that be possible? In case I split the two into two procedures (to first calculate a, b and then a second procedure to calculate c) i got results like
A | B | C
a | b | null
null | null | c
what i want to get is
A | B | C
a | b | c

Related

converting multiple comma separated columns into rows

I have an Oracle table which holds comma separated values in many columns. For example :
Id Column1 Column2
1 A,B,C H
2 D,E J,K
3 F L,M,N
I want to split all the columns into rows and the output should be this :
ID Column1 Column2
1 A H
1 B H
1 C H
2 D J
2 D K
2 E J
2 E K
3 F L
3 F M
3 F N
I found some suggestions which uses regexp_substr and connect by but it deals with only one column that has comma separated values. I have tried sub-query method also where I will be dealing with one column at a time in inner query and send the inner query output as input it outer query, this takes more time and the columns that hold comma separated values are more. So I cannot use the sub-query method.
For one column you can CROSS JOIN a TABLE() collection expression containing a correlated sub-query that uses a hierarchical query to split the column value up into separate strings. For two columns, you just do the same thing for the second column and the CROSS JOIN takes care of ensuring that each delimited value in column1 is paired with each delimited value in column2.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( Id, Column1, Column2 ) AS
SELECT 1, 'A,B,C', 'H' FROM DUAL UNION ALL
SELECT 2, 'D,E', 'J,K' FROM DUAL UNION ALL
SELECT 3, 'F', 'L,M,N' FROM DUAL;
Query 1:
SELECT t.id,
c1.COLUMN_VALUE AS c1,
c2.COLUMN_VALUE AS c2
FROM table_name t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.Column1, '[^,]+', 1, LEVEL )
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.Column1, '[^,]+' )
) AS SYS.ODCIVARCHAR2LIST
)
) c1
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.Column2, '[^,]+', 1, LEVEL )
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.Column2, '[^,]+' )
) AS SYS.ODCIVARCHAR2LIST
)
) c2
Results:
| ID | C1 | C2 |
|----|----|----|
| 1 | A | H |
| 1 | B | H |
| 1 | C | H |
| 2 | D | J |
| 2 | D | K |
| 2 | E | J |
| 2 | E | K |
| 3 | F | L |
| 3 | F | M |
| 3 | F | N |
Below will give you an idea about to how to convert the comma separated string into rows. You can use this logic to satisfy your need.
select regexp_substr('a,b,c,v,f', '[^,]+',1,level)
from dual
connect by level <= regexp_count('a,b,c,v,f', ',') + 1;

How to use comma separated value from column to IN clause [duplicate]

This question already has answers here:
Using the "IN" clause with a comma delimited string from the output of a replace() function in Oracle SQL
(6 answers)
Closed 6 years ago.
I have a table, which contains below values(small snap of data):
Row#| Column A | Column B
--------------------------
1 |A | A,B
2 |C | A,D
3 |M | Z
4 |C |A,B,L
5 |F |F
6 |C |O
List goes on and on. Column A and B is populated by other application, so its combinations are not predictable. Table has more than 4000 rows.
My search values are not fixed; I have to use search value from Column B of same row for which I am looking for data in Column A.
I want to check Column A's value is IN Column B or not. Basically compare Column A with B, if find match in B result should skip that row.
In above case Row#1's Column A's value is in B which is not true for row#2.
My query should reach all rows and compare Column A with B. Query should return Row#1 and 5
This question is different Using the "IN" clause with a comma delimited string from the output of a replace() function in Oracle SQL
As it has fixed set of search value
select * from my_table where instr(columnB, columnA) > 0;
Example: Should contain
select * from (
select 1 a, 'A' b, 'A,B' c from dual union all
select 2, 'C', 'A,D' from dual ) t
where instr(c,b) > 0;
Result:
A B C
-----------
1 A A,B
Example 2: Shouldn't contain
select * from (
select 1 a, 'A' b, 'A,B' c from dual union all
select 2, 'C', 'A,D' from dual ) t
where instr(c,b) = 0;
Result:
A B C
-----------
2 C A,D

Avoiding multiple look ups and improving the performance

I have a case, where I need to do multiple joins(lookups) like below query.Sample scenario was given.
I have around 200 CAT_CODE. I thought few solutions and I listed it down as cases.Is there is any different way to write a sql query to have better performance? or any better approach in ETL tool?
Primary Table(PRIM):
NUM CAT1_CODE CAT2_CODE CAT3_CODE
A 1 y q
B 2 e a
C 3 s z
Secondary Table(LOV):
CATEGORY COLUMN_LKP EXT_CODE
CAT1_CODE 1 AB
CAT1_CODE 2 CD
CAT1_CODE 3 HI
CAT2_CODE y JL
CAT2_CODE e QD
CAT2_CODE s AH
CAT3_CODE q CD
CAT3_CODE a MS
CAT3_CODE z EJ
CASE-1: Through SQL:
I have written a simple query to accomplish this task. Do you think, this would be right approach? Any other ways, to improve this query? Right now, I'm using both Oracle and Postgres.
SELECT
NUM,
(SELECT EXT_CODE FROM TEST_LOV
WHERE CATEGRY='CAT1_CODE' AND COLUMN_LKP=A.CAT1_CODE) CAT1,
(SELECT EXT_CODE FROM TEST_LOV
WHERE CATEGRY='CAT2_CODE' AND COLUMN_LKP=A.CAT2_CODE) CAT2,
(SELECT EXT_CODE FROM TEST_LOV
WHERE CATEGRY='CAT3_CODE' AND COLUMN_LKP=A.CAT3_CODE) CAT3
FROM
TEST_PRIM A
REQUIRED OUTPUT:
NUM CAT1 CAT2 CAT3
A AB JL CD
B CD QD MS
C HI AH EJ
CASE-2: ETL:
Same case can be accomplished through ETL. We need to use lookups to get that done.
Scenario-1:
LOV(CAT1_CODE) LOV(CAT2_CODE) LOV(CAT3_CODE)
| | |
| | |
PRIM---->LOOKUP---------->LOOKUP------------>LOOKUP-------->TARGET
I don't think, that would be right approach. We have 200 codes, we cannot use 200 lookup. Is there is any better approach to handle that in ETL(Datastage, Talend, BODS)with better performance?
Scenario-2:
Pivoting PRIM(converting CAT1_CODE,CAT2_CODE,CAT3_CODE columns in to rows) like below and doing one lookup.But pivoting will take much time, because we have data around 600 million and 200 columns.
NUM CATGRY CODE
A CAT1_CODE 1
A CAT1_CODE y
A CAT1_CODE q
B CAT2_CODE 2
B CAT2_CODE e
B CAT2_CODE a
C CAT3_CODE 3
C CAT3_CODE s
C CAT3_CODE z
Kindly suggest me some best way to handle this approach.It can be through ETL or through sql. Thanks in advance.
You can use the LATERAL keyword to do the magic that you are looking for.
The following code could help:
SELECT
NUM,
MAX(ext_code) FILTER (WHERE c.CATEGORY='CAT1_CODE') AS CAT1,
MAX(ext_code) FILTER (WHERE c.CATEGORY='CAT2_CODE') AS CAT2,
MAX(ext_code) FILTER (WHERE c.CATEGORY='CAT3_CODE') AS CAT3
FROM TEST_PRIM a
CROSS JOIN LATERAL (
SELECT *
FROM TEST_LOV b
WHERE
(a.CAT1_CODE=b.COLUMN_LKP AND B.CATEGORY = 'CAT1_CODE')
OR (a.CAT2_CODE=b.COLUMN_LKP AND B.CATEGORY = 'CAT2_CODE')
OR (a.CAT3_CODE=b.COLUMN_LKP AND B.CATEGORY = 'CAT3_CODE')
) c
GROUP BY NUM
ORDER BY NUM;
Output
num | cat1 | cat2 | cat3
-----+------+------+------
A | AB | JL | CD
B | CD | QD | MS
C | HI | AH | EJ

Update with group by

I'm stumped on what seemed to be a simple UPDATE statement.
I'm looking for an UPDATE that uses two values. The first (a) is used to group, the second (b) is used to find a local minimum of values within the respective group. As a little extra there is a threshold value on b: Any value 1 or smaller shall remain as it is.
drop table t1;
create table t1 (a number, b number);
insert into t1 values (1,0);
insert into t1 values (1,1);
insert into t1 values (2,1);
insert into t1 values (2,2);
insert into t1 values (3,1);
insert into t1 values (3,2);
insert into t1 values (3,3);
insert into t1 values (4,1);
insert into t1 values (4,3);
insert into t1 values (4,4);
insert into t1 values (4,5);
-- 1,0 -> 1,0
-- 1,1 -> 1,1
-- 2,1 -> 2,1
-- 2,2 -> 2,2
-- 3,1 -> 3,1
-- 3,2 -> 3,2
-- 3,3 -> 3,2 <-
-- 4,1 -> 4,1
-- 4,3 -> 4,3 <-
-- 4,4 -> 4,3 <-
-- 4,5 -> 4,3 <-
Obviously not sufficient is:
update t1 x
set b = (select min(b) from t1 where b > 1)
;
Whatever more complicated stuff I try, e.g.
UPDATE t1 x
set (a,b) = (select distinct a,b from (
select a, min(b) from t1 where b > 1 group by a)
)
;
I get
SQL-Fehler: ORA-01427: Unterabfrage für eine Zeile liefert mehr als eine Zeile
01427. 00000 - "single-row subquery returns more than one row"
which is not overly surprising as I need a row for each value of a.
Of course I could write a PL/SQL Procedure with a cursor loop but is it possible in a single elegant SQL statement? Maybe using partition by?
Your question is a bit confusing.
You say that you would like to set value b to a minimum value from partition a that column b is in row with, while the rows containing b = 1 should remain untouched.
From what I can see in your question as comments (I assume it's your expected output) you also want to get the minimum value that follows 1 within a partition - so you basically want the minimum value of b that is greater than 1.
Below is SQL query that does this
UPDATE t1 alias
SET b = (
SELECT min(b)
FROM t1
WHERE alias.a = t1.a
AND t1.b > 1 -- this would get the minimum value higher than 1
GROUP BY a
)
WHERE alias.b > 1 -- update will not affect rows with b <= 1
Output after update
a | b
---+---
1 | 0
1 | 1
2 | 1
2 | 2
3 | 1
3 | 2
3 | 2
4 | 1
4 | 3
4 | 3
4 | 3

How to find all tuples in a table if and only if the tuple appears once?

I have a table:
x | y | z
------------
1 | 1 | *
1 | 1 | *
1 | 3 | *
2 | 2 | *
2 | 3 | *
3 | 4 | *
3 | 4 | *
3 | 3 | *
What is the relational algebra representation of only returning all unique (x, y) tuples?
For example, I would like the following (x,y) tuples returned in the above table: (1,3), (2,2) (2,3), and (3,3).
Thanks
Rename R to S
S := ρS/R(R)
Join R and S on x,y
D := R ⋈S.x = R.x ∧ S.y = R.y S
This squares the number of tuples with a particular value for (x,y). Particularly, if a value for (x,y) appears only once in R, it appears only once in D.
Join R and S on x,y,z
E := R ⋈S.x = R.x ∧ S.y = R.y ∧ S.z = R.z S
This basically adds some columns to R. It does not add or remove tuples.
Subtract E from D and project to the attributes of R
F := πx,y,z(D\E)
This removes the tuples from D, that where created by joining a tuple from R to the corresponding tuple in S. The remaining tuples are the ones that where created by joining a tuple in R to a different tuple in S. Particularly, if a value for (x,y) appears only once in R, no tuple in F exists with that value.
Remove the tuples in F from R
R\F

Resources