Updating row, according to Rownum in Oracle - oracle

I have two tables in Oracle
TableProducts
Product_Code, and 20 others fields
BGU
LSO
MPA
MPA4
MPA5
TPA
UGU
For this example, now I have 7 values, but maybe 9 values later.
CREATE TABLE TableContacts AS SELECT *
FROM Contacts
WHERE Rownum <= (4*(SELECT Count(Distinct Product_Code) FROM TableProducts));
Now I have 28 Rows in my TableContacts.
Now I need To UPDATE the rows in order to create combinations test.
TableContacts
Product_Code, Email, PDF, and 17 others fields.
Email and PDF has two possible values 'N' or 'Y'.
I need to fill the TableContacts with the combinations of Product_Code, Email and PDF fields, according to Rownum position.
Rownum = 1 -> Product_Code='BGU', Email='N', PDF='N'
Rownum = 2 -> Product_Code='BGU', Email='N', PDF='Y'
Rownum = 3 -> Product_Code='BGU', Email='Y', PDF='N'
Rownum = 4 -> Product_Code='BGU', Email='Y', PDF='Y'
Rownum = 5 -> Product_Code='LSO', Email='N', PDF='N'
If I have 7 values for Product_Code, 2 by Email and 2 by PDF, then I will need to fill (7 * 2 *2) = 28 Rows.
How to create and SQL for this situation updating TableContacts?

Partially, requirement doesn't make sense. Rows in a table within the relational databases aren't sorted in any way, so - saying that you want to refer to a rownum is ... strange. That's why I modified the contacts table and added yet another column - rn - which shows that rownum of yours.
Also, this example shows only 3 products (didn't feel like typing all of them). Code that follows doesn't care about number of those products and will work the same regardless.
Products:
SQL> select * From products;
CODE
-----
BGU
LSO
MPA
Insert into Contacts:
SQL> insert into contacts (rn, code, email, pdf)
2 with temp as
3 (select p.code, x.lvl
4 from products p cross join (Select level lvl from dual connect by level <= 4) x
5 )
6 select t.lvl,
7 t.code,
8 case when t.lvl in (1, 2) then 'N'
9 when t.lvl in (3, 4) then 'Y'
10 end email,
11 --
12 case when t.lvl in (1, 3) then 'N'
13 when t.lvl in (2, 4) then 'Y'
14 end pdf
15 from temp t;
12 rows created.
Result:
SQL> select * From contacts
2 order by code, rn;
RN CODE E P
---------- ----- - -
1 BGU N N
2 BGU N Y
3 BGU Y N
4 BGU Y Y
1 LSO N N
2 LSO N Y
3 LSO Y N
4 LSO Y Y
1 MPA N N
2 MPA N Y
3 MPA Y N
4 MPA Y Y
12 rows selected.
SQL>
[EDIT: how to update table that contains rows?]
If I understood you correctly, this is what you initially have in the CONTACTS table:
SQL> select code, rownum from contacts;
CODE ROWNUM
----- ----------
BGU 1
LSO 2
MPA 3
BGU 4
LSO 5
MPA 6
BGU 7
LSO 8
MPA 9
BGU 10
LSO 11
MPA 12
12 rows selected.
SQL>
As I previously said: rownum is irrelevant here, it can change, you can't tell which rownum belongs to which code.
Anyway, such an update (merge, actually) does the job:
SQL> merge into contacts a
2 using (select c.code,
3 c.rowid,
4 row_number() over (partition by c.code order by null) rn
5 from contacts c
6 ) x
7 on (a.rowid = x.rowid)
8 when matched then update set
9 a.email = case when x.rn in (1, 2) then 'N'
10 when x.rn in (3, 4) then 'Y'
11 end,
12 a.pdf = case when x.rn in (1, 3) then 'N'
13 when x.rn in (2, 4) then 'Y'
14 end;
12 rows merged.
SQL> select * From contacts order by code, email, pdf;
CODE EMAIL PDF
----- ----- -----
BGU N N
BGU N Y
BGU Y N
BGU Y Y
LSO N N
LSO N Y
LSO Y N
LSO Y Y
MPA N N
MPA N Y
MPA Y N
MPA Y Y
12 rows selected.
SQL>

Related

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

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>

Oracle - Assign count value for a column based on another column in select query

Consider, I have the following in a select query:
ID Flag
5 Y
5 Y
5 N
6 Y
6 Y
6 Y
6 N
I should be adding a new column count in the same select which counts the number of 'Y' records for the ID and assigns it to all. (Eg: ID=5 has 3 records. All of them should be assigned the count value as '2').
Output required in select query:
ID Flag count
5 Y 2
5 Y 2
5 N 2
6 Y 3
6 Y 3
6 Y 3
6 N 3
Use a window function:
select id,
flag,
count(case when flag = 'Y' then 1 end) over (partition by id) as "count"
from the_table
order by id;
The case expression will return null for flags with N and thus they will be ignored by the count() function

Select the sum of occurances of first alphabetical character

Hi what i need to do is create a select statement which outputs the sum of the first character in a field within the table so the output would look something like
A,12
B,0
C,20
D,14
E,0
ect...
The table is called contacts, in the above there was 12 occurrences of people whose names begin with the letter A
I hope i have explained this correctly
Let's understand this with EMP table example.
SQL> with
2 letters
3 as
4 (select chr( ascii('A')+level-1 ) letter
5 from dual
6 connect by level <= 26
7 )
8 SELECT substr(ename, 1, 1) AS init_name,
9 count(*) cnt
10 FROM emp
11 WHERE substr(ename, 1, 1) IN (SELECT letter from letters)
12 GROUP BY substr(ename, 1, 1)
13 UNION
14 SELECT l.letter AS init_name,
15 0 cnt
16 FROM letters l
17 WHERE l.letter NOT IN (SELECT substr(ename, 1, 1) FROM emp)
18 ORDER BY init_name
19 /
I CNT
- ----------
A 2
B 1
C 1
D 0
E 0
F 1
G 0
H 0
I 0
J 2
K 1
L 0
M 2
N 0
O 0
P 0
Q 0
R 0
S 2
T 1
U 0
V 0
W 1
X 0
Y 0
Z 0
26 rows selected.
SQL>
So, it gives the count of each letter of first name, and for the other letters which does not exist in the first name, the count is 0.
Generate the 26 letters using connect then left join to the first letter of the name and count them:
select letter, count(name) count
from (select chr(ascii('A')+level-1) letter from dual connect by level < 27) l
left join emp on substr(name, 1, 1) = letter
group by letter order by 1
See SQLFiddle
Attribution: My technique of generating letters uses elements of Lalit's answer.

Find factorial in Oracle

I have a table which has a column call numbers
Numbers
------
3
5
I am trying to get the factorial of those. I am using the below logic but not with proper result
Select
Numbers
,EXP(SUM(LN(Numbers)) OVER (ORDER BY Numbers)) Factorial
FROM testTbl
*Output
*
Numbers Factorial
------ ---------
3 3.00000000000000000000000000000000000001
5 15.0000000000000000000000000000000000002
What is wrong? Please help
Expected
--------
Numbers Factorial
------ ---------
3 6
5 120
Thanks in advance
I've had a go at this from another angle, trying to do it all in a SQL statement (using your table testTbl and the column numbers).
This is what I've come up with, see if it suits you:
SELECT testtbl.numbers,
ROUND( EXP( SUM( LN( t1.n ) ) ) ) AS factorial
FROM ( SELECT UNIQUE LEVEL n
FROM testtbl
CONNECT BY LEVEL <= numbers) t1,
( SELECT UNIQUE LEVEL n
FROM testtbl
CONNECT BY LEVEL <= numbers) t2,
testTbl
WHERE t1.n <= t2.n
AND t2.n = testTbl.numbers
GROUP BY testtbl.numbers
ORDER BY testtbl.numbers;
Gives the output:
Numbers Factorial
3 6
5 120
Hope it helps...
Were it me, I'd create a factorial function and call that user-defined function in my query. Something like
SQL> create function factorial( p_n in number )
2 return number
3 is
4 begin
5 if( p_n = 1 )
6 then
7 return p_n;
8 else
9 return p_n * factorial( p_n - 1 );
10 end if;
11 end;
12 /
Function created.
SQL> with t as (
2 select 3 num from dual
3 union all
4 select 5 from dual
5 )
6 select num,
7 factorial(num)
8 from t;
NUM FACTORIAL(NUM)
---------- --------------
3 6
5 120
If for some reason you cannot define a new function and you really want to do it in SQL, you'll can generate all the numbers less than the number in your table and then aggregate those generated numbers.
SQL> ed
Wrote file afiedt.buf
1 with t as (
2 select 3 num from dual
3 union all
4 select 5 from dual
5 )
6 select t.num,
7 exp( sum(ln(gen.num))) factorial
8 from (select level num
9 from dual
10 connect by level <= (select max(t.num) from t)) gen,
11 t
12 where gen.num <= t.num
13* group by t.num
SQL> /
NUM FACTORIAL
---------- ----------
5 120
3 6

CONNECT BY for two tables with two JOINS

I have 3 tables:
two with hierarchical structures
(like "dimensions" of recursive type of hierarchy);
one with summing data (like "facts" with X column).
They are here:
DIM1 (ID1, PARENT2, NAME1)
DIM2 (ID2, PARENT2, NAME2)
FACTS (ID1, ID2, X)
Example of DIM1 table:
-- 1 0 DIM1
---- 2 1 DIM1-A
------ 3 2 DIM1-A-A
-------- 4 3 DIM1-A-A-A
-------- 5 3 DIM1-A-A-B
------ 6 2 DIM1-A-B
-------- 7 6 DIM1-A-B-A
-------- 8 6 DIM1-A-B-B
------ 9 2 DIM1-A-C
---- 10 1 DIM1-B
------ 11 10 DIM1-B-C
------ 12 10 DIM1-B-D
---- 13 1 DIM1-C
Example of DIM2 table:
-- 1 0 DIM2
---- 2 1 DIM2-A
------ 3 2 DIM2-A-A
-------- 4 3 DIM2-A-A-A
-------- 5 3 DIM2-A-A-B
-------- 6 3 DIM2-A-B-C
------ 7 2 DIM2-A-B
---- 8 1 DIM2-B
---- 9 1 DIM2-C
Example of FACTS table:
1 1 100
1 2 30
1 3 500
-- ................
13 9 200
And I would like to create the only SELECT where I will specify the parent for DIM1 (for example ID1=2 for DIM1-A) and parent for DIM2 (for example ID2=2 for DIM2-A) and SELECT will generate a report like this:
Name_of_1 Name_of_2 Sum_of_X
--------- --------- ----------
DIM1-A-A DIM2-A-A (some sum)
DIM1-A-A DIM2-A-B (some sum)
DIM1-A-B DIM2-A-A (some sum)
DIM1-A-B DIM2-A-B (some sum)
DIM1-A-C DIM2-A-A (some sum)
DIM1-A-C DIM2-A-B (some sum)
I would like to use CONNECT BY phrase, START WITH phrase, SUM phrase, GROUP BY phrase, and OUTER or INNER (?) JOIN. I need no other extensions of Oracle 10.2.
In other words: only with "classic" SQL and
only Oracle extensions for hierarchy queries.
Is it possible?
I tried some experiments with question in
Mixing together Connect by, inner join and sum with Oracle
(where is a very nice solution but only for one
dimension table ("Tasks"), but I need to JOIN two dimension tables to one facts table), but I was not successful.
"Some sum" is not very descriptive, so I don't see why do you need CONNECT BY at all.
SELECT dim1.name, dim2.name, x
FROM (
SELECT id1, id2, SUM(x) AS x
FROM facts
GROUP BY
id1, id2
) f
JOIN dim1
ON dim1.id = f.id1
JOIN dim2
ON dim2.id = f.id2
I think what you're trying to do is get the sum of the value in the facts table for all of the children of the specified rows grouped by the topmost children. This would mean that in your example above, the results for the first row would be the sum any intersections of (DIM1-A-A, DIM1-A-A-A, DIM1-A-A-B) and (DIM2-A-A, DIM2-A-A-A, DIM2-A-A-B, DIM3-A-A-C) found in the FACTS table. With that assumption, I have come to the following solution:
SELECT root_name1, root_name2, SUM(X)
FROM ( SELECT CONNECT_BY_ROOT(name1) AS root_name,
id1
FROM dim1
CONNECT BY parent1 = PRIOR id1
START WITH parent1 = 2) d1
CROSS JOIN
( SELECT CONNECT_BY_ROOT(name2) AS root_name,
id2
FROM dim2
CONNECT BY parent2 = PRIOR id2
START WITH parent2 = 2) d2
LEFT OUTER JOIN
facts
ON d1.id1 = facts.id1
AND d2.id2 = facts.id2
GROUP BY root_name1, root_name2
(This also assumes that the columns of FACTS are named ID1, ID2, and X.)

Resources