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;
Related
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;
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
I have a simple table with values (ID) in groups (GRP_ID).
create table tst as
select 1 grp_id, 1 id from dual union all
select 1 grp_id, 1 id from dual union all
select 1 grp_id, 2 id from dual union all
select 2 grp_id, 1 id from dual union all
select 2 grp_id, 2 id from dual union all
select 2 grp_id, 2 id from dual union all
select 3 grp_id, 3 id from dual;
It is straightforward to find a maximum value per group using analytical functions.
select grp_id, id,
max(id) over (partition by grp_id) max_grp
from tst
order by 1,2;
GRP_ID ID MAX_GRP
---------- ---------- ----------
1 1 2
1 1 2
1 2 2
2 1 2
2 2 2
2 2 2
3 3 3
But the goal is to find the maximum value excluding the value of the current row.
This is the expected result (column MAX_OTHER_ID):
GRP_ID ID MAX_GRP MAX_OTHER_ID
---------- ---------- ---------- ------------
1 1 2 2
1 1 2 2
1 2 2 1
2 1 2 2
2 2 2 2
2 2 2 2
3 3 3
Note that in the GRP_ID = 2 a tie on the MAX value exists, so the MAX_OTHER_ID remains the same.
I did manage this two step solution, but I'm wondering if there is a more straightforward and simple solution.
with max1 as (
select grp_id, id,
row_number() over (partition by grp_id order by id desc) rn
from tst
)
select GRP_ID, ID,
case when rn = 1 /* MAX row per group */ then
max(decode(rn,1,to_number(null),id)) over (partition by grp_id)
else
max(id) over (partition by grp_id)
end as max_other_id
from max1
order by 1,2
;
I wish the window functions supported multiple range specifications something like:
max(id) over (
partition by grp_id
order by id
range between unbounded preceding and 1 preceding
or range between 1 following and unbounded following
)
But unfortunately they don't.
As a workaround, you can avoid subqueries and CTEs using the function twice on the different ranges and call coalesce on that.
select grp_id,
id,
coalesce(
max(id) over (
partition by grp_id
order by id
range between 1 following and unbounded following
)
, max(id) over (
partition by grp_id
order by id
range between unbounded preceding and 1 preceding
)
) max_grp
from tst
order by 1,
2
Coalesce works out of the box because of the ordering clause as the result of the window function call will be either the max in the given window or a null value.
Demo - http://rextester.com/SDXVF13962
SELECT GRP_ID,ID, (SELECT Max(ID) FROM TEST A WHERE A.ROWID<>B.ROWID AND A.GRP_ID=B.GRP_ID) maX_ID FROM TEST B;
Got the expected result with Co-Related Query ! Hope this helps .
So I have these 2 tables on Oracle:
CLIENT
cl_id cl_name
1 John
2 Maria
PAYMENTS
pa_id pa_date pa_status cl_id
1 2017-01-01 1 1
2 2017-01-01 1 2
3 2017-02-01 1 1
4 2017-02-01 1 2
5 2017-03-01 0 1
6 2017-03-01 1 2
I need a select statemant that gives me the client ID, NAME and the STATUS of his last payment. So the end result of my select should be:
cl_id cl_name pa_status
1 John 0
2 Maria 1
This is the CLIENT select that works:
select cl_id, cl_name from CLIENT;
This is the last status of the PAYMENT select that works:
select * from (
select pa_status from PAYMENT ORDER BY PA_DATE DESC)
where rownum = 1;
So now, I need to make them work together. I tried 2 ways that didn't work:
select cl_id, cl_name, (select * from (
select pa_status from PAYMENT ORDER BY PA_DATE DESC)
where rownum = 1 and PAYMENT.cl_id = CLIENT.CL_ID) as last_status from CLIENT;
error: invalid identifier
AND this:
select cl_id, cl_name, (select * from (
select pa_status from PAYMENT ORDER BY PA_DATE DESC)
where rownum = 1 ) as last_status from CLIENT;
which don't give me any errors, but only shows the same last status of John that is the last record:
cl_id cl_name last_status
1 John 0
2 Maria 0
Can anyone give me a hint?
Thanks
you need to use analystic function.
This kind of functions let you split your data to some groups, and rank the data for each group as you wish.
In your case:
Select * from (
Select id, name, status, row_number () over (partition by p.cl_id order by p.pa_date desc) as rw
From client c join payments p on p.cl_id = c.cl_id)
Inn where inn.rw = 1;
First take the max of date from for each clientid.
Select cl_id, max(pa_date) as pa_date from PAYMENTS group by cl_id
Now you take ur client table and join with above subquery
select c.cl_id, c.cl_name,
(select pa_status from PAYMENT t where t.pa_date=p.pa_date and t.cl_id=p.cl_id)
from CLIENT c join (Select cl_id, max(pa_date) as pa_date from PAYMENTS group by cl_id) p on p.cl_id=c.cl_id
You can use Oracle's KEEP LAST here:
select cl_id, c.cl_name, last_payment.status
from client
join
(
select
cl_id,
max(pa_status) keep (dense_rank last order by pa_date) as status
from payments
group by cl_id
) last_payment using (cl_id);
(If you want to include clients without payments, change the join to LEFT OUTER JOIN.)
This gets the max date for the client
and then gets the highest payment id with that date.
with max_date as (
select max(date) as max_date, cl_id from payments group by cl_id
)
select c.cl_id, c.cl_name, p.pa_sttus from client c
join payments p
on c.cl_id = p.cl_id
where p.pa_id = (select max(p2.pa_id) from payments p2
join max_date md
on p2.cl_id = md.cl_id
where p.cl_id = p2.cl_id
and p2.pa_date = md.max_date
)
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