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

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

Related

Updating row, according to Rownum in 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>

Oracle: prioritizing results based on column’s value

I have a data-set in which there are duplicate IDs in the first column. I'm hoping to obtain a single row of data for each ID based on the second column's value. The data looks like so:
ID Info_Source Prior?
A 1 Y
A 3 N
A 2 Y
B 1 N
B 1 N
B 2 Y
C 2 N
C 3 Y
C 1 N
Specifically the criteria would call for prioritizing based on the second column's value (3 highest priority; then 1; and lastly 2): if the 'Info_Source' column has a value of 3, return that row; if there is no 3 in the second column for a given ID, look for a 1 and if found return that row; and finally if there is no 3 or 1 associated with the ID, search for 2 and return that row for the ID.
The desired results would be a single row for each ID, and the resulting data would be:
ID Info_Source Prior?
A 3 N
B 1 N
C 3 Y
row_number() over() usually solves these needs nicely and efficiently e.g.
select ID, Info_Source, Prior
from (
select ID, Info_Source, Prior
, row_number() over(partition by id order by Info_source DESC) as rn
)
where rn = 1
For prioritizing the second column's value (3 ; then 1, then 2) use a case expression to alter the raw value into an order that you need.
select ID, Info_Source, Prior
from (
select ID, Info_Source, Prior
, row_number() over(partition by id
order by case when Info_source = 3 then 3
when Infor_source = 1 then 2
else 1 end DESC) as rn
)
where rn = 1

In Oracle, repeatedly assign values from a set to column

I have a table in Oracle that has a column that I would like to assign a value to from a set of possible values. I like to assign the values in order of the set, repeatedly, for the entire table.
For example:
If the set of values is {1, 2, 3}. I'd like to assign the values in this pattern until the last row is reached:
rowNum someCol valueCol
1 this 1
2 is 2
3 some 3
4 other 1
5 column 2
6 in 3
7 the 1
8 table 2
I can't figure out how to do this with a traditional update statement. Anone that could help with this problem?
Use Modulo to achieve desire result
UPDATE TableName
SET valueCol= CASE WHEN rowNum % 3 == 1 then 1
WHEN rowNum % 3 == 2 then 2
WHEN rowNum % 3 == 0 then 3
END
update tablename
set valuecol = case mod(rownum, 3) when 0 then 3 else mod(rownum, 3) end
;

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.

Merge two recordset in oracle

I have following 2 recordsets :
Recordset 1:
Id isVal isVal1
1 Y N
2 Y N
Recordset 2:
Id isVal isVal1
2 N Y
3 N Y
Actual recordset required is:
Id isVal isVal1
1 Y N
2 Y Y
3 N Y
Should I use join? Can you please advice me how can I solve this?
No, you want to place the records on top of each other so you would need to use union.
select id, max(isval) as isval, max(isval1) as isval1
from ( select id, isval, isval1
from recordset1
union all
select id, isval, isval1
from recordset1
)
group by id
I use union all as you don't need to remove duplicates, for which you would remove the all.
The max works because 'Y' is "greater" than 'N'.
I'm assuming that 'Y' takes precedence over 'N' rather than values from the first record-set are less important than values from the second.

Resources