I am working on query :
Table 1-
Id Customer status
------------------
1 AB Active
2 AB Inactive
1 BC Inactive
2 BC Inactive
Output
------
Customer
BC
As customer status is inactive in all the IDs need to select only that customer.
One straightforward way to do this uses aggregation by customer:
SELECT Customer
FROM Table1
GROUP BY Customer
HAVING COUNT(CASE WHEN status <> 'Inactive' THEN 1 END) = 0;
Related
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;
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 have a table which stores status of a customer reply in oracle. I have to count last consecutive declines from the customer.
For ex:
Id Status
-----------------------------
1. Declined
2. Accepted
3. Declined
4. Declined
This will have count = 2.
As last two were declined.
NOTE: Original Poster clarified in a "comment" that she needed a different requirement - addressing it in a separate Answer. Keeping this since it shows one possible solution for a more complicated problem that the OP's.
Assuming you want the count of the most recent consecutive "Declines" (even if followed by "Accepted" - and you want to allow for more than one customer - here is one possible solution.
Input table (I called it "t"):
SQL> select * from t;
CUSTOMER_ID DECISION_ID STATUS
----------- ----------- --------------------
10 1 Accepted
10 2 Declined
10 3 Declined
10 4 Accepted
10 5 Accepted
10 6 Declined
10 7 Declined
30 1 Declined
30 2 Accepted
30 3 Declined
30 4 Accepted
30 5 Declined
30 6 Declined
30 7 Declined
30 8 Accepted
30 9 Accepted
Query:
with t1 as
(
select customer_id,
decision_id - row_number() over
(partition by customer_id order by decision_id) as idx
from t
where status = 'Declined'
),
t2 as (select customer_id, max(idx) as max_idx from t1 group by customer_id)
select t1.customer_id, count(1) as ct
from t1 join t2 on t1.customer_id = t2.customer_id
where t1.idx = t2.max_idx
group by t1.customer_id
order by t1.customer_id
/
Query output:
CUSTOMER_ID CT
----------- ----------
10 2
30 3
#Gurmeet - Then the problem is much easier. Here is one way to solve it. If you need the result ordered by customer_id, add order by customer_id right at the end. nvl in the definition of d_A in CTE t2 is needed in case that customer never had a transaction with "Accepted" status.
Input: Same as in my other Answer.
Query: (MODIFIED to meet OP additional requirement):
with t0 as (select customer_id, status, row_number() over
(partition by customer_id order by decision_id) rn from t),
t1 as (select customer_id, max(rn) as d_all from t0 group by customer_id),
t2 as (select customer_id, nvl(max(rn), 0) as d_A from t0
where status = 'Accept' group by customer_id)
select customer_id, d_all - d_A as ct from t1 natural join t2
/
Result:
CUSTOMER_ID CT
----------- ----------
30 0
10 2
2 rows selected.
i have two tables in oracle --> master_file and payment tables.
master_file table:
id (primary key), nama, status
41121, john, PL
41122, ryan, UP
41121, john, UP
there are duplicate data in columns id ( id 41121 ) . I do not know where the mistake , so the id column can store duplicate data .
payment table:
id, idr
41121, 1000
41122, 500
my query :
select a.id,a.nama,a.status,b.idr from master_file a, payment b
where a.id=b.id(+)
result
id, nama, status, idr
41121, john, PL, 1000
41122, ryan, UP, 500
41121, john, UP, 1000
i want result
id, nama, status, idr
41121, john, PL, 1000
41122, ryan, UP, 500
i know, i cannot use distinct to remove row : 41121, john, UP, 1000.
give me solution, please. thanks before. :)
Your master table cannot have a primary key on ID as it has duplicate values; perhaps it has a foreign key or a composite primary key, say on ID and status.
Assuming your data and data model are correct and you don't want to modify it, you need to establish the precedence of the possible status values. From your question it seems PL takes precedence over UP, but there may be other possible values, and you may have more than two rows for a given ID. Once you know the precedence you can use that in a case statement to create a numeric equivalent and use that to rack the 'duplicate' rows:
select a.id, a.nama, a.status, b.idr,
dense_rank() over (partition by a.id order by
case a.status when 'UP' then 1 when 'PL' then 2 else 0 end desc) as rnk
from master_file a
left join payment b on b.id = a.id
order by a.id;
ID NAMA STATUS IDR RNK
---------- ---------- ------ ---------- ----------
41121 john PL 1000 1
41121 john UP 1000 2
41122 ryan UP 500 1
You can then use that as an inline view and filter out all but the highest-precedence rows:
select id, nama, status, idr
from (
select a.id, a.nama, a.status, b.idr,
dense_rank() over (partition by a.id order by
case a.status when 'UP' then 1 when 'PL' then 2 else 0 end desc) as rnk
from master_file a
left join payment b on b.id = a.id
)
where rnk = 1
order by id;
ID NAMA STATUS IDR
---------- ---------- ------ ----------
41121 john PL 1000
41122 ryan UP 500
It's also possible to use an inline view that filter the master_file before joining, but that's largely a matter of preference.
SQL Fiddle demo.
If the possible status values are in another reference table, and their precedence is established in that table, then you could join to that instead of doing it manually through case.
You want to remove the row with 'UP' in it?
In that case you need to include it as another item in your WHERE clause:
... where a.id=b.id(+) and status = 'UP'
So I have a table like this. This is a standard Order header - Order Detail table:
order id order_line
----------- -----------
100 1
100 2
100 3
101 1
102 1
103 1
103 2
104 1
105 1
Now, how can I make a SELECT that will only pick the orders that only have one line?
In this case I don't want orders 100 and 103.
Thanks!
Tiago
Counting lines using "group by order_id" is a good solution, however counting is not needed, simpler Max function works fine:
select order_id from orders
group by order_id
having max(order_line)=1;
In case order_line has consecutive values further "optimization" is possible:
select order_id from orders
where order_line <= 2
group by order_id
having max(order_line)=1;
Group by the order_id and take only those having 1 record per group
select order_id
from orders
group by order_id
having count(*) = 1
If you need the complete record then do
select t1.*
from orders t1
join
(
select order_id
from orders
group by order_id
having count(*) = 1
) t2 on t1.order_id = t2.order_id
You can try following query too :
select order_id , order_line
from Order_Detail
group by order_id ,order_line
having count(order_id)<2;