SELECT only rows that aren't repeated - oracle

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;

Related

Row Values To Appear In Separate Columns

I have the following data
create table company (com_cd varchar2(100), act_id number(10));
insert into company values ('IBM',100);
insert into company values ('IBM',200);
insert into company values ('IBM',300);
insert into company values ('HP',50);
insert into company values ('HP',85);
Then I ran this query
select COM_CD,
REGEXP_SUBSTR(DUP_ACT_ID,'[^,]+',1,1) AS DUP_ACT_1,
REGEXP_SUBSTR(DUP_ACT_ID,'[^,]+',1,2) AS DUP_ACT_2,
REGEXP_SUBSTR(DUP_ACT_ID,'[^,]+',1,3) AS DUP_ACT_3
FROM (
SELECT COM_CD, LISTAGG(ACT_ID,',') DUP_ACT_ID
FROM COMPANY
GROUP BY COM_CD HAVING COUNT(*) > 1
);
COM_CD DUP_ACT_1 DUP_ACT_2 DUP_ACT_3
HP 50 85
IBM 100 200 300
It gave me the correct output but I won't know in a table with thousands of values whether there would be 1 or 10 duplicate ACT_ID. So I am looking for a solution where I don't use regexp_substr and instead get the output somehow based on number of values in the DUP_ACT_ID column. Any help is appreciated.
If you don't have to have a single row per com_cd, you could use something like the following:
SELECT com_cd,seq,act_id dup_act_id from
(select distinct com_cd,act_id,
dense_rank() over (partition by com_cd order by act_id) seq,
count(distinct act_id) over (partition by com_cd) cnt
from company)
where cnt > 1
order by com_cd,seq
COM_CD SEQ DUP_ACT_ID
HP 1 50
HP 2 85
IBM 1 100
IBM 2 200
IBM 3 300
You didn't mention what should happen if we see the same act_id multiple times within a com_cd, I assumed you would only want to see it once.
If you need a new column for each dup_act_id, you could pivot the above query:
WITH dup_accts as (SELECT com_cd,seq,act_id dup_act_id from
(select distinct com_cd,act_id,
dense_rank() over (partition by com_cd order by act_id) seq,
count(distinct act_id) over (partition by com_cd) cnt
from company)
where cnt > 1)
select * from dup_accts pivot(max(dup_act_id) for seq in (1 dup_act_1,2 dup_act_2,3 dup_act_3))
COM_CD DUP_ACT_1 DUP_ACT_2 DUP_ACT_3
IBM 100 200 300
HP 50 85
But in that case you'd still have to add a new section to the in() clause for each additional dup_act_id.

why does both the block number is shown when I want to show the block with maximum departments?

I have a table department with 3 column(department_name , department_id, department_block_number) so I want to fetch the department_block_number in which maximum number of department is located ? I have two department_block_number 303, 202 and each has 4 and 2 departments respectively? how can i do it?
select q1.department_block_number , max(c)
(select department_block_number , count(department_id)as c from department group by department_block_number)q1,
group by department_block_number ;
select q1.department_block_number , max(c)
(select department_block_number , count(department_id)as c from department group by department_block_number)q1,
group by department_block_number ;
Now i want to show only 303 as it is the block number with maximum departments in it but my query is showing both 303, 202 please help me . If you know some other way so that i can fetch the result so please help
In standard SQL if you are looking for one row, you would do:
select d.department_block_number, count(*)
from department d
group by d.department_block_number
order by count(*) desc
fetch first 1 row only;
Some databases spell fetch first 1 row only as limit 1 or select top (1) or in even more arcane ways.
In older versions of Oracle (fetch is supported in 12c+), you can do:
select department_block_number, cnt
from (select d.department_block_number, count(*) as cnt
from department d
group by d.department_block_number
order by count(*) desc
) d
where rownum = 1;

How to make existing table column value unique in vertica which are duplicate?

I have a vertica table "Product" it contains product_id, order_id and some more columns. order_id is a varchar column.
it has data like this:
product_id order_id
1 a:111
2 a:222
3 a:111
2 a:444
1 a:222
4 a:111
now i want to update order_id each row which are duplicate(want to make unique). like this:
product_id order_id
1 a:112
2 a:222
3 a:113
2 a:444
1 a:223
4 a:114
how to do this?
I am not very sure if updating would be the right idea for vertica.
However, you can use the below select statement.
INSERT INTO new_table
SELECT product_id
-- , order_id
, order_id + ROW_NUMBER() OVER (
PARTITION BY product_id
, rank ORDER BY order_id
) AS as order_id
FROM (
SELECT product_id
, order_id
, DENSE_RANK() OVER (
PARTITION BY product_id ORDER BY order_id
) AS rank
FROM abc
) sub;
Let us know if this works for you.

Oracle - Group by the most frequent entries

I have 2 tables on my Oracle DB
One with a product list
PRODUCT_ID - PRODUCT_NAME - PRODUCT_PRICE
1 P_1 50
2 P_2 60
3 P_3 70
4 P_4 80
And one with the orders
CLIENT_ID - PRODUCT_ID - ORDER_PRICE
1 1 50
2 3 60
3 2 70
4 2 70
I need to make a query so it returns the product_list table but ordered by the most frequent Product_id in the orders table. So in this case the Product ID=2 must be first on the list.
I have found some examples but i cant find something that will work for this case.
You can use subquery for aggregation on orders table to find count for each product id and then left join it with the product_list table to use the calculated count for ordering.
select p.*
from product_list p
left join (
select product_id,
count(*) as cnt
from orders
group by product_id
) o on p.product_id = o.product_id
order by o.cnt desc nulls last;
LEFT Join is used since not all products could have orders and we need to find the count of orders for each product.
GROUP BY is used because we use the aggregate count() to find the occurrence of orders for a given Product.
ORDER BY DESC is used so the count is ordered highest count of product orders first to lowest. However when ties exist, we don't know what order will be returned as a second level of order by is not defined. Could be order We could add a Product_ID so they are low to high after that...
.
SELECT PL.Product_ID, PL.Product_Name, PL.Product_Price, count(O.Product_ID) cnt
FROM Product_List
LEFT JOIN Orders O
on O.Product_ID = PL.Product_ID
GROUP BY PL.Product_ID, PL.Product_Name, PL.Product_Price
ORDER BY cnt Desc

Oracle : how to remove row without distinct

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'

Resources