Getting rows that are having distinct id with bulk collect - Oracle - oracle

I want to get the records that are having distinct member id using bulk collect.
My query is like below:
...
type members_nt is table of table2%rowtype;
members_bc members_nt;
...
select t2.* bulk collect
into members_bc
from table1 t1
,table2 t2
where t1.isn= t2.isn
and t1.customer= customer
and t1.insert_date between ... and ... );
t2 table has a column named member_id and what I want to get into members_bc is the rows having distinct member_id.
For example if my t2 table has something like this:
name | surname | account | member_id
john alby abc 123
john alby def 123
mary hale lkj 234
I want to bulk collect only
name | surname | account | member_id
john alby abc 123
mary hale lkj 234
or
name | surname | account | member_id
john alby def 123
mary hale lkj 234
It does not matter which one. But memberid must be unique in the members_bc.
How can I achieve this? Thank you.

Use the ROW_NUMBER() analytic function to give each row a number per member_id and then filter to only get the first row.
DECLARE
TYPE members_nt IS TABLE OF table2%ROWTYPE;
members_bc members_nt;
BEGIN
SELECT t2.name, t2.surname, t2.account, t2.member_id
BULK COLLECT INTO members_bc
FROM (
SELECT t2.*,
ROW_NUMBER() OVER ( PARTITION BY member_id ORDER BY ROWNUM ) AS rn
FROM table2 t2
) t2
WHERE rn = 1;
FOR i IN 1 .. members_bc.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( members_bc(i).member_id || ' ' || members_bc(i).name );
END LOOP;
END;
/
outputs:
123 john
234 mary
db<>fiddle here

You can avoid "select *" and just enter the desired columns by creating a cursor for your select and then creating a %rowtype of the cursor.
Using #MT0 answer as baseline template.)
declare
cursor members_cur is
select t2.name, t2.surname, t2.account, t2.member_id
from ( select t2.name, t2.surname, t2.account, t2.member_id
, row_number() over ( partition by member_id order by rownum ) as rn
from table2 t2
) t2
where rn = 1;
type members_tt is table of members_cur%rowtype;
members_bc members_tt;
begin
open members_cur;
fetch members_cur
bulk collect into members_bc;
close members_cur;
for i in 1 .. members_bc.count
loop
dbms_output.put_line(members_bc(i).member_id || ' ' ||members_bc(i).name);
end loop;
end;

A simple option is to use aggregation, e.g.
select name,
surname,
min(account) as account, --> this
member_id
from ...
group by name, surname, member_id --> and this

Related

Oracle join returning too many rows

I have the following select statments in Oracle 19:
select count(*) as facility_load_stg_cnt from facility_load_stg;
select count(*) as facility_cnt from facility;
select count(*) as loctn_typ_cnt from loctn_typ;
select count(*) as join_cnt from (
select f.facility_id, lt.loctn_typ_id
from facility_load_stg stg
inner join facility f on stg.facility_cd = f.facility_cd
inner join loctn_typ lt on stg.bldg = lt.loctn_typ_nm);
the object is simple, get the PK's for facility and loctn_typ (facility_id, loctn_typ_id) to insert into the table that will build the relationship.
The problem is when I run the above code, I get these results:
FACILITY_LOAD_STG_CNT
---------------------
987
FACILITY_CNT
------------
645
LOCTN_TYP_CNT
-------------
188
JOIN_CNT
----------
2905
why is the select with the join have 3x the rows of the facility_load_stg table? I am sure I am doing something silly, I just cannot see it.
Folks have asked to see the table definitions, here is the relavent parts:
create table FACILITY_MAINT_DATA.FACILITY_LOAD_STG
(
FACILITY_CD VARCHAR2(100) not null,
BLDG VARCHAR2(100)
)
create table FACILITY_MAINT_DATA.FACILITY
(
FACILITY_CD VARCHAR2(100),
FACILITY_ID NUMBER(14) not null
constraint PK_FACILITY
primary key
)
create table FACILITY_MAINT_DATA.LOCTN_TYP
(
LOCTN_TYP_ID NUMBER(14) default "FACILITY_MAINT_DATA"."LOCTN_TYP_ID_SEQ"."NEXTVAL" not null
constraint PK_LOCTN_TYP
primary key,
LOCTN_TYP_NM VARCHAR2(100),
)
The generall rule if you join and you encounter a higher count that you expect is that the join keys are not unique*
Here a simple example reproducting the same result as you have (limiting only to two tables).
create table tab1 as
select rownum id,
case when rownum <= 80 then 'CD_'||rownum else 'CD_X' end cd from dual connect by level <= 645;
create table tab2 as
select rownum id,
case when rownum <= 80 then 'CD_'||rownum
when rownum <= 85 then 'CD_X'
else 'CD_Y' end cd from dual connect by level <= 987;
select count(*) from tab1;
COUNT(*)
----------
645
select count(*) from tab2;
COUNT(*)
----------
987
select count(*)
from tab1
join tab2
on tab1.cd = tab2.cd;
COUNT(*)
----------
2905
Summary change the join to use the unique keys or limit the join only to such CDcolumns that are unique.
Thank you everyone for the comments, that got me looking at detail at the actual data and there are duplications. Now I just have to figure out why, just need some investigation:)

Concat Results of 2 Select Queries into 1 Column (oracle)

Im trying to insert a record into my table. But there is 1 column in which I want to get concatenated results of 2 select statements. Like the 2 statements will fetch their records and concatenate to form 1 value so that it can be inserted into the column.
insert into ABC (Name,City,Age)
Values ('John',(
(Select City from TableA where ID=1)concat(Select City from TableA where ID=2)),'22')
Or it can be comma separated but I am not getting what to use here.
Try this one:
INSERT INTO ABC (Name, City, Age)
VALUES ('John',
(
(SELECT City FROM TableA WHERE ID = 1) ||
(SELECT City FROM TableA WHERE ID = 2)
),
'22');
But ensure ... WHERE ID = 1 and ....WHERE ID = 2 return one row.
Using a cross join to select from the two tables produces a nice clear statement:
insert into ABC (Name,City,Age)
select 'John', concat(t1.city, t2.city), 22
from TableA t1
cross join TableA t2
where t1.ID = 1
and t2.ID = 2
/
Use CONCAT() or CONCAT_WS() functions for this (reference)
insert into ABC (Name,City,Age) Values (
'John',
CONCAT_WS(' ', (Select City from TableA where ID=1), (Select City from TableA where ID=2)),
'22'
)

Oracle Order by array index

I have a collection which is a table of numbers containing primary keys and I want to sort a select statement - containing those keys - by the index of the number collection.
For example:
TYPE "NUMBERCOLLECTION" AS TABLE OF NUMBER;
...
myNumberCollection numberCollection := numberCollection(45, 7799, 2187);
...
select nr
, columnA
, columnB
from myTable
where myTable.nr member of myNumberCollection
order by index of myNumberCollection
will result in
NR COLUMNA COLUMNB
-----------------------
45 xyz abc
7799 xyz abc
2187 xyz abc
SELECT nr,
columnA,
columnB
FROM myTable m
INNER JOIN
( SELECT ROWNUM AS id, COLUMN_VALUE FROM TABLE( myNumberCollection ) ) c
ON ( m.nr = c.COLUMN_VALUE )
ORDER BY c.id;

select rows having same column value

I am trying to form a query for following scenario
if my table is
signature | id | operation
abc | 1 | 1234
xyz | 1 | 1234
pqr | 2 | 1234
then my output should be
signature | id
abc | 1
xyz | 1
i.e. rows having same value in a particular column.
i have formed query like
select signature,id
from tablename
where operation = '1234'
group by signature,id
having count(*) >0;
but this is returning everything including xyz | 1 also.
Can someone suggest me correct query?
Try this:
SELECT signature,COUNT(id) FROM table_name WHERE operation = '1234' GROUP BY id;
I haven't tested this and it might be overly complicated but I THINK this will work:
SELECT Signature, ID from tablename WHERE ID in( SELECT ID FROM (SELECT ID, COUNT(*) as NumRecords from tablename GROUP BY ID HAVING NumRecords > 1)))
Try this:
We need to apply PARTITION on ID column as follows,
SELECT Result.[signature], Result.ID ,
ROW_NUMBER() over(PARTITION BY Result.ID ORDER BY Result.ID) AS [RowNum]
INTO #TempResult
FROM table_name AS Result
GROUP BY Result.[signature], Result.ID
SELECT Result.[signature], Result.ID
FROM #TempResult AS Result
where Result.[RowNum] = 1
You can use where exists for an alternative to group by
select
signature,
id
from tablename t1
where exists (
select * from tablename t2
where t1.id = t2.id
and t1.signature != t2.signature
and t1.operation = 1234
);

Insert into select query with cursor value

I want to write a SQK script with insert query in Oracle where one of the value will be fetched from cursor and rest all will be retrieved from table.
For example, consider Employee table:
Emp_No | Emp_Name
1 | AAA
...........
I am reading the table into a cursor.
Cursor c1 is select emp_no, emp_name from employee;
I am iterating the cursor and adding to a table along with information from another table.
for empCur in c1
loop
insert into employee_info(emp_no, emp_name, address, age, ... ) values (empCur.emp_no, empCur.emp_name, select t.address, t.age, ... from employee_temp_table t where t.emp_no=empCur.emp_no)
end loop;
Is my script valid? If not is there any other way to achieve it? Since few values are in cursor and few are in another table I am not sure how to handle this.
Your script isn't correct because this
select t.address, t.age, ... from employee_temp_table t where t.emp_no=empCur.emp_no
is not a valid expression. You may use a scalar subquery (one-row, one-column subquery) only, like this:
insert into t1(col1, col2, col3) values (1, (select col1 from t2), (select col2 from t2));
Or you may try insert from select:
for empCur in c1 loop
insert into employee_info(emp_no, emp_name, address, age, ... )
select empCur.emp_no, empCur.emp_name, t.address, t.age, ... from employee_temp_table t where t.emp_no=empCur.emp_no;
end loop;
If you want to use the cursor, why not just join the tables inside the cursor?
for empCur in ( select e.emp_no, e.emp_name, t.address, t.age ...
from employee e join employee_temp_table t on ( t.emp_no = e.emp_no )
) loop
insert into employee_info(...) values ( empCur.emp_no, ...);
end loop;
Or with a sql insert: (if you can choose sql over pl/sql - T Kyte says do it)
insert into employee_info
select e.emp_no, e.emp_name, t.address, t.age ...
from employee e join employee_temp_table t on ( t.emp_no = e.emp_no );

Resources