Oracle, Select Distinct columns, with corresponding columns - oracle

I want to select DISTINCT results from the user_id column but I need the corresponding columns as well.
Result set needs to return two role_id that are Distnct user_id and be not an 'Unassigned' status.
The query I am using:
SELECT role_id, user_id, role_code, status_code FROM table where school_id=5 and status_code= 'DRAFT';
This an example of my table:
ROLE_ID USER_ID SCHOOL_ID CAMPUS_ID ROLE_CODE STATUS_CODE
1 4 5 7 Unassigned DRAFT
2 4 5 7 TEST DRAFT
3 4 5 8 TEST DRAFT
4 5 5 9 Unassigned DRAFT
5 5 5 9 TEST DRAFT
6 5 5 10 TEST DRAFT
I have tried to add group by based on user_id but I get an ORA-00979.

You can use ROW_NUMBER() to identify the rows you want. For example:
select *
from (
select t.*,
row_number() over(partition by user_id order by role_id) as rn
from t
where role_code <> 'Unassigned'
) x
where rn = 1

DISTINCT is across the entire set of columns and not for one specific column. Therefore, if you want to get the DISTINCT rows which are not Unassigned you can use:
SELECT DISTINCT
role_id,
user_id,
role_code,
status_code
FROM table
where school_id = 5
and status_code = 'DRAFT'
and role_code != 'Unassigned';
If you want to get a single row for each user_id then you can use GROUP BY and find the minimum role_id:
SELECT MIN(role_id) AS role_id,
user_id,
MIN(role_code ) KEEP (DENSE_RANK FIRST ORDER BY role_id) AS role_code,
MIN(status_code) KEEP (DENSE_RANK FIRST ORDER BY role_id) AS status_code
FROM table
where school_id = 5
and status_code = 'DRAFT'
and role_code != 'Unassigned'
GROUP BY
user_id;

Related

Last record row count in one to many laravel

I have a one to many relationship
shipment
id
bill
1
2222
2
4255
shipment_status
id
status
1
created
1
Shipped
2
created
2
Shipped
2
Delivered
3
created
3
Shipped
4
Created
4
Shipped
4
Delivered
5
Created
What I want to get
Results
count
status
2
Shipped
2
Delivered
1
Created
What I've done :
SELECT a.status,
b.total_status
FROM hipmenthistories a
INNER JOIN (
SELECT status,
MAX(created_at) last_status,
COUNT(*) total_status
FROM shipmenthistories
GROUP BY status
) b ON a.status = b.status`
Actual data in the db fiddle
Use ROW_NUMBER() and order the results by shipment and most recent status date. Then filter on the most recent status row:
See also db<>fiddle
WITH CTE AS (
SELECT status
, shipment_id
, ROW_NUMBER() OVER (PARTITION BY shipment_id ORDER BY created_at DESC, id DESC) AS rowNum
FROM shipmenthistories
)
SELECT status, COUNT(shipment_id) numOfShipments
FROM CTE
WHERE RowNum = 1
GROUP BY status;

Problem in merge condition in oracle when merging two tables

I have a table which has data as:
id payor_name
---------------
1 AETNA
2 UMR
3 CIGNA
4 METLIFE
4 AETNAU
5 ktm
6 ktm
Id and payor_name are two columns.So,
My expected output is:
id payor_name
---------------
1 AETNA
2 UMR
3 CIGNA
4 METLIFE
4 AETNAU
6 ktm ...> I want to change the id of this row to be 6 from 5.
6 ktm
I want one to one mapping between id and payor_name.So,this is what I tried:
MERGE INTO offc.payor_collec A
USING (select id from offc.payor_collec where payor_name in(
select payor_name from offc.payor_collec group by payor_name having count(distinct id)>=2)) B
ON (A.id=B.id)
WHEN MATCHED THEN
UPDATE SET A.id=B.id
But when I compiled I got error as:
Error at line 1
ORA-38104: Columns referenced in the ON Clause cannot be updated: "A"."ID"
Id is number where as payor_name is varchar2.
How can I achieve this result?
MERGE works, but slightly different than your code.
SQL> select * from test;
ID PAYOR
---------- -----
1 aetna
2 umr
5 ktm
6 ktm
SQL> merge into test t
2 using (select max(t1.id) id,
3 t1.payor_name
4 from test t1
5 group by t1.payor_name
6 ) x
7 on (x.payor_name = t.payor_name)
8 when matched then update set
9 t.id = x.id;
4 rows merged.
SQL> select * from test;
ID PAYOR
---------- -----
1 aetna
2 umr
6 ktm
6 ktm
SQL>
Use a correlated subquery:
UPDATE PAYOR_COLLEC pc
SET pc.ID = (SELECT MAX(pc2.ID)
FROM PAYOR_COLLEC pc2
WHERE pc2.PAYOR_NAME = pc.PAYOR_NAME)
dbfiddle here
You can use a MERGE statement, as you tried and as Littlefoot has shown.
You can also use a correlated subquery as Bob Jarvis has shown, but that will be quite inefficient.
Many Oracle developers are unaware that you can also update through a join. Worse, there are many who say "there is no such thing in Oracle."
In your problem, you need to join your table to an aggregate query (picking just the max id for each payor_name) and the join is on the group by column in the aggregate. This already guarantees that the join column will be unique in the right-hand table; that is all Oracle needs to allow the update through join.
Here is a complete example, starting with the create table statement, then the update and then showing the table after the update. Note that I don't need any constraints (like primary key, not null, unique, etc.) or indexes on the base table. If they do exist, so much the better, but the solution works in the most general case.
create table t (id, payor_name) as
select 1, 'AETNA' from dual union all
select 2, 'UMR' from dual union all
select 3, 'CIGNA' from dual union all
select 4, 'METLIFE' from dual union all
select 4, 'AETNAU' from dual union all
select 5, 'ktm' from dual union all
select 6, 'ktm' from dual;
Table T created.
update
(
select id, payor_name, max_id
from t inner join
(select max(id) as max_id, payor_name from t group by payor_name)
using (payor_name)
)
set id = max_id where id != max_id
;
1 row updated.
select * from t;
ID PAYOR_NAME
----- ----------
1 AETNA
2 UMR
3 CIGNA
4 METLIFE
4 AETNAU
6 ktm
6 ktm
Notice the where clause at the end of the update statement, too. You don't want to update rows to their pre-existing value; that will still generate undo and redo data (although I understand that Oracle has changed that in more recent versions - it now doesn't generate undo and redo unless a row did indeed change). I assume ID is NOT NULL - otherwise you should rewrite the where clause as
where decode(id, max_id, 0) is null
or equivalent

ORDER BY BASED ON COLUMN

I have two tables,PRODUCTS AND LOOKUP TABLES.Now i want to order the KEY Column in products table based on KEY column value in LOOKUP TABLE.
CREATE TABLE PRODUCTS
(
ID INT,
KEY VARCHAR(50)
)
INSERT INTO PRODUCTS
VALUES (1, 'EGHS'), (2, 'PFE'), (3, 'EGHS'),
(4, 'PFE'), (5, 'ABC')
CREATE TABLE LOOKUP (F_KEY VARCHAR(50))
INSERT INTO LOOKUP VALUES('PFE,EGHS,ABC')
Now I want to order the records in PRODUCTS table based on KEY (PFE,EGHS,ABC) values in LOOKUP table.
Example output:
PRODUCTS
ID F_KEY
-----------
2 PFE
4 PFE
1 EGHS
3 EGHS
5 ABC
I use this query, but it is not working
SELECT *
FROM PRODUCTS
ORDER BY (SELECT F_KEY FROM LOOKUP)
You can split the string using XML. You first need to convert the string to XML and replace the comma with start and end XML tags.
Once done, you can assign an incrementing number using ROW_NUMBER() like following.
;WITH cte
AS (SELECT dt,
Row_number()
OVER(
ORDER BY (SELECT 1)) RN
FROM (SELECT Cast('<X>' + Replace(F.f_key, ',', '</X><X>')
+ '</X>' AS XML) AS xmlfilter
FROM [lookup] F)F1
CROSS apply (SELECT fdata.d.value('.', 'varchar(500)') AS DT
FROM f1.xmlfilter.nodes('X') AS fdata(d)) O)
SELECT P.*
FROM products P
LEFT JOIN cte C
ON C.dt = P.[key]
ORDER BY C.rn
Online Demo
Output:
ID F_KEY
-----------
2 PFE
4 PFE
1 EGHS
3 EGHS
5 ABC
You may do it like this:
SELECT ID, [KEY] FROM PRODUCTS
ORDER BY
CASE [KEY]
WHEN 'PFE' THEN 1
WHEN 'EGHS' THEN 2
WHEN 'ABC' THEN 3
END

using Not in in SubQuery with not equal to

Following two queries are resulting into different number of records by interchanging the condition in following way:
select count(1) from clientlist where userid
not in (select distinct userid from Clientlist
where userid in (select uniqueid from employee e where emplstatus = 'Y' ))
Returning 38885 number of records
select count(1) from clientlist where userid
in (select distinct userid from Clientlist
where userid in (select uniqueid from employee e where emplstatus != 'Y' ))
Returning 3630 number of records
would any one please be able to explain what difference the change in condition is causing that much difference in results?
Table ClientList Table employee
Id Id Status
-- -- ------
1 1 Y
2 2 N
3 3 null
4
First query counts id's: 2, 3, 4 - rows which are in ClientList and are not in employee and marked as Y.
Second query: id 2 - shows rows which are in both tables and in table 2 are marked as 'N'.

Query to exclude row based on another row's filter

I'm using Oracle 10g.
Question: How can I write query to return just ID only if ALL the codes for that ID end in 6? I don't want ID=1 because not all its codes end in 6.
TABLE_A
ID Code
===============
1 100
1 106
2 206
3 316
3 326
4 444
Desired Result:
ID
==
2
3
You simply want each ID where the count of rows for that id is the same as the count of rows where the third digit is six.
SELECT ID
FROM TABLE_A
GROUP BY ID
HAVING COUNT(*) = COUNT(CASE WHEN SUBSTR(code,3,1) = '6' THEN 1 END)
Try this:
SELECT DISTINCT b.id
FROM (
SELECT id,
COUNT(1) cnt
FROM table_a
GROUP BY id
) a,
(
SELECT id,
COUNT(1) cnt
FROM table_a
WHERE CODE LIKE '%6'
GROUP BY id
)b
WHERE a.id = b.id
AND a.cnt = b.cnt
Alternative using ANALYTIC functions:
SELECT DISTINCT id
FROM
(
SELECT id,
COUNT(1) OVER(PARTITION BY id) cnt,
SUM(CASE WHEN code LIKE '%6' THEN 1 ELSE 0 END) OVER(PARTITION BY id) sm
FROM table_a
)
WHERE cnt = sm

Resources