Oracle Sql : distinct value in a specific field [duplicate] - sql

This question already has answers here:
How to select records with maximum values in two columns?
(2 answers)
Closed 7 years ago.
I have the following table :
**Country Name Number**
us John 45
us Jeff 35
fr Jean 31
it Luigi 25
fr Maxime 23
ca Justin 23
This table is order by Number. I want to have a query that for each country give me the name with highest number :
**Country Name Number**
us John 45
fr Jean 31
it Luigi 25
ca Justin 23
I try to use distinct but I can't only make it on country if I want to print the all thing...
Have an idea ?'
EDIT :
The table is obtain by a subquery

SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Countries AS
SELECT 'us' AS Country, 'John' AS Name, 45 AS "Number" FROM DUAL
UNION ALL SELECT 'us' AS Country, 'Jeff' AS Name, 35 AS "Number" FROM DUAL
UNION ALL SELECT 'fr' AS Country, 'Jean' AS Name, 31 AS "Number" FROM DUAL
UNION ALL SELECT 'it' AS Country, 'Luigi' AS Name, 25 AS "Number" FROM DUAL
UNION ALL SELECT 'fr' AS Country, 'Maxime' AS Name, 23 AS "Number" FROM DUAL
UNION ALL SELECT 'ca' AS Country, 'Justin' AS Name, 23 AS "Number" FROM DUAL;
Query 1:
SELECT Country,
MAX( Name ) KEEP ( DENSE_RANK FIRST ORDER BY "Number" DESC ) AS "Name",
MAX( "Number" ) AS "Number"
FROM Countries
GROUP BY Country
Results:
| COUNTRY | Name | Number |
|---------|--------|--------|
| ca | Justin | 23 |
| fr | Jean | 31 |
| it | Luigi | 25 |
| us | John | 45 |

I do not have an Oracle db handy but I got this working in my SQL Server db and am pretty sure it will work in Oracle (meaning I think I am using ANSI sql which should work in most db's):
SELECT m.Country,m.Name,m.number
FROM mytable m
INNER JOIN (
select country, MAX(number) as number
FROM mytable GROUP BY Country
) AS tmp ON m.Country = tmp.Country and m.Number = tmp.number
ORDER BY m.Number DESC
This has the added benefit that it should give you records when you have two people in a given country that have the same number.
You didn't give us a table name so I just called it mytable.

Try below query:
SELECT Country, MAX(numbeer) FROM Table_Name GROUP BY Country
PFB updated query to include Name:
SELECT t1.* FROM table1 t1 INNER JOIN
(SELECT country, max(numbeer) as numbeer FROM table1 GROUP BY country) t2
ON t1.country=t2.country AND t1.numbeer=t2.numbeer;

Use row_number():
select t.Country, t.Name, t.Number
from (select t.*,
row_number() over (partition by country order by number desc) as seqnum
from table t
) t
where seqnum = 1;

Related

Reconciliation Automation Query

I have one database and time to time i change some part of query as per requirement.
i want to keep record of results of both before and after result of these queries in one table and want to show queries which generate difference.
For Example,
Consider following table
emp_id country salary
---------------------
1 usa 1000
2 uk 2500
3 uk 1200
4 usa 3500
5 usa 4000
6 uk 1100
Now, my before query is :
Before Query:
select count(emp_id) as count,country from table where salary>2000 group by country;
Before Result:
count country
2 usa
1 uk
After Query:
select count(emp_id) as count,country from table where salary<2000 group by country;
After Query Result:
count country
2 uk
1 usa
My Final Result or Table I want is:
column 1 | column 2 | column 3 | column 4 |
2 usa 2 uk
1 uk 1 usa
...... but if query results are same than it shouldn't show in this table.
Thanks in advance.
I believe that you can use the same approach as here.
select t1.*, t2.* -- if you need specific columns without rn than you have to list them here
from
(
select t.*, row_number() over (order by count) rn
from
(
-- query #1
select count(emp_id) as count,country from table where salary>2000 group by country;
) t
) t1
full join
(
select t.*, row_number() over (order by count) rn
from
(
-- query #2
select count(emp_id) as count,country from table where salary<2000 group by country;
) t
) t2 on t1.rn = t2.rn

How to remove duplicate entries in SQL using selected columns only?

My data is as shown below:
id | name | date | country | vendor
1717 | CUST A | 8-Aug-1978 | INDIA | VENDOR 1
1972 | CUST B | 1-Jan-1965 | INDIA | VENDOR 2
2083 | CUST C | 1-Jan-1936 | AUSTRALIA | VENDOR 1
2189 | CUST D | 27-May-2000 | USA | VENDOR 4
2189 | CUST D | 27-May-2000 | USA | VENDOR 5
2189 | CUST D | 27-May-2000 | USA | VENDOR 6
Question:
I want to remove the duplicate rows based on Columns
id, name, date, gender and country only (hence excluding Vendor)
In the above example, the 5th and 6th entries are duplicate except for their vendors.
Using Select Query how can I get rid of the 5th and 6th entry and keep on 4th entry?
By Keeping the 4th Entry, I mean the first Entry that comes up by select in the sequence of rows.
One method is group by:
select id, name, date, gender, country, min(vendor) as vendor
from t
group by id, name, date, gender, country;
This returns an "arbitrary" value of vendor. Tables in SQL represent unordered sets. There is no concept of 4th or 5th or 6th row. So, if you want one of the particular vendor values, you need to specify how that value is determined.
SELECT count(vendor) as count, id, name, date, gender, country
FROM TABLENAME
GROUP BY id, name, date, gender, country
WHERE Count > 1
sqlcsa
You can use Row_Number()
select * from (
select *, RowN= Row_Number() over(partition by id, name, date, gender, country order by id, name, date, gender, country)
from YourTable ) a where a.RowN = 1
If you are not interested in preserving the vendor information, you can use the distinct keyword
select distinct id, name, date, gender, country
from yourTable
This way the rows that are different for the undesired column only, will result as identical and the distinct will have the query return only one of them
Edit
If you want to preserve only the rows that are not duplicate, you can first select the combinations of id, name, date, gender and country that are available only once
select id, name, date, gender, country, count(*)
from yourTable
group by id, name, date, gender, country
having count(*) = 1
Then you use this table to filter the original one, by joining them together
select t1.*
from yourTable t1
join (
select id, name, date, gender, country, count(*)
from yourTable
group by id, name, date, gender, country
having count(*) = 1
) t2
on t1.id = t2.id and
t1.name = t2.name and
t1.date = t2.date and
t1.gender = t2.gender and
t1.country = t2.country

SQL:How to get min Quantity?

I got this problem. When i tried to summarize the min quatity of nations's products and it did not work.
I have 2 tables below
PRODUCT:
ID|NAME |NaID|Qty
-------------------
01|Fruit|JP |50
02|MEAT |AUS |10
03|MANGA|JP |80
04|BOOK |AUS |8
NATION:
NaID |NAME
-------------------
AUS |Australia
JP |Japan
I want my result like this:
ID|NAME |Name|minQty
-------------------
01|Fruit|JP |50
04|BOOK |AUS |8
and i used:
select p.id,p.name, p.NaID,n.name,min(P.Qty)as minQty
from Product p,Nation n
where p.NaID=n.NaID
group by p.id,p.name, p.NaID,n.name,p.Qty
and i got this (T_T):
ID|NAME |NaID|minQty
-------------------
01|Fruit|JP |50
02|MEAT |AUS |10
03|MANGA|JP |80
04|BOOK |AUS |8
Please,Could soneone help me? I am thinking that i am bad at SQL now.
SQL Server 2005 supports window functions, so you can do something like this:
select id,
name,
NaID,
name,
qty
from (
select p.id,
p.name,
p.NaID,
n.name,
min(P.Qty) over (partition by n.naid) as min_qty,
p.qty
from Product p
join Nation n on p.NaID=n.NaID
) t
where qty = min_qty;
If there is more than one nation with the same minimum value, you will get each of them. If you don't want that, you need to use row_number()
select id,
name,
NaID,
name,
qty
from (
select p.id,
p.name,
p.NaID,
n.name,
row_number() over (partition by n.naid order by p.qty) as rn,
p.qty
from Product p
join Nation n on p.NaID = n.NaID
) t
where rn = 1;
As your example output with only includes the NaID but not the nation's name you don't really need the the join between product and nation.
(There is no DBMS product called "SQL 2005". SQL is just a (standard) for a query language. The DBMS product you mean is called Microsoft SQL Server 2005. Or just SQL Server 2005).
In Oracle, you can use several techniques. You can use subqueries and analytic functions, but the most efficient one is to use aggregate functions MIN and FIRST.
Your tables:
SQL> create table nation (naid,name)
2 as
3 select 'AUS', 'Australia' from dual union all
4 select 'JP', 'Japan' from dual
5 /
Table created.
SQL> create table product (id,name,naid,qty)
2 as
3 select '01', 'Fruit', 'JP', 50 from dual union all
4 select '02', 'MEAT', 'AUS', 10 from dual union all
5 select '03', 'MANGA', 'JP', 80 from dual union all
6 select '04', 'BOOK', 'AUS', 8 from dual
7 /
Table created.
The query:
SQL> select max(p.id) keep (dense_rank first order by p.qty) id
2 , max(p.name) keep (dense_rank first order by p.qty) name
3 , p.naid "NaID"
4 , n.name "Nation"
5 , min(p.qty) "minQty"
6 from product p
7 inner join nation n on (p.naid = n.naid)
8 group by p.naid
9 , n.name
10 /
ID NAME NaID Nation minQty
-- ----- ---- --------- ----------
01 Fruit JP Japan 50
04 BOOK AUS Australia 8
2 rows selected.
Since you're not using Oracle, a less efficient query, but probably working in all RDBMS:
SQL> select p.id
2 , p.name
3 , p.naid
4 , n.name
5 , p.qty
6 from product p
7 inner join nation n on (p.naid = n.naid)
8 where ( p.naid, p.qty )
9 in
10 ( select p2.naid
11 , min(p2.qty)
12 from product p2
13 group by p2.naid
14 )
15 /
ID NAME NAID NAME QTY
-- ----- ---- --------- ----------
01 Fruit JP Japan 50
04 BOOK AUS Australia 8
2 rows selected.
Note that if you have several rows with the same minimum quantity per nation, all those rows will be returned, instead of just one as in the previous "Oracle"-query.
with cte as (
select *,
row_number() over (partition by Nation order by qty) as [rn]
from product
)
select * from cte where [rn] = 1

Oracle SQL : Getting the first row with filters

How do I get the first row filtering some values out? I used row_number() over(partition by Name ORDER BY Date) to get the order (See example below). But I need, the rank will start at the last occurrence of Type = B (See expected output)
SELECT Name, Age, Type, Date,
row_number() over(partition by Name ORDER BY Date) as Rank
FROM TableA;
For example :
Name Age Type Date Rank
Ben 12 A 2013/02/01 1
Rod 14 A 2013/02/05 2
Zed 13 B 2013/03/09 3
Ken 12 A 2013/04/02 4
Jed 14 B 2013/05/01 5
Mar 13 A 2013/05/04 6
Nic 12 A 2013/06/02 7
Jen 15 A 2013/06/09 8
Expected Output :
Name Age Type Date Rank
Mar 13 A 2013/05/04 1
Nic 12 A 2013/06/02 2
Jen 15 A 2013/06/09 3
Try
WITH qry AS
(
SELECT "Name", "Age", "Type", "Date",
ROW_NUMBER() OVER (PARTITION BY "Type" ORDER BY "Date") rank
FROM TableA
)
SELECT "Name", "Age", "Type", "Date"
FROM qry
WHERE rank = 1
Output:
| NAME | AGE | TYPE | DATE |
-------------------------------------------------------
| Ben | 12 | A | February, 01 2013 00:00:00+0000 |
| Zed | 13 | B | March, 09 2013 00:00:00+0000 |
Here is SQLFiddle demo
There is another possibility: You could wrap it in a subquery:
select
t.*
from
(SELECT "Name", "Age", "Type", "Date",
ROW_NUMBER() OVER (PARTITION BY "Type" ORDER BY "Date") rank
FROM TableA
) t
where
rank = 1
"Sub queries" and "common table expressions (with)" behave differently, so have a look at these approaches. It depends on your case, if one is mandatory (dealing with side-effects) or one is faster. In case of Oracle, there is even a materialize hint. "Per SQL standard, CTEs offer an optimization fence feature" (this is an adapted quote from here).
Try this:
SELECT Name, Age, Type, Date,
row_number() over(partition by Name ORDER BY Date) as Rank
FROM TableA
WHERE type <> 'B'
AND Date > (
SELECT max(Date)
FROM TableA
WHERE type = 'B'
);
Please note that this assumes that there are no A's on the exact same date/time as the last B.
this should also do what you asked for:
select t.*,
row_number() over(partition by "Name" ORDER BY "Date") as Rank
from TableA t
where "Date" >= (select max("Date") from TableA where "Type" = 'B')

select n rows in sql

I have a table
Country | Capital
----------------------
France | Paris
Germany | Berlin
USA | Washington
Russia | Moscow.
I need to select all rows except the first one.The table is having no primary key.
How should i do this?
SELECT *
FROM (
SELECT country, capitol, rownum as rn
FROM your_table
ORDER BY country
)
WHERE rn > 1
If the "first one" is not defined through sorting by country, then you need to apply a different ORDER BY in the inner query.
Edit
For completeness, the ANSI SQL solution to this would be:
SELECT *
FROM (
SELECT country,
capitol,
row_number() over (order by country) as rn
FROM your_table
)
WHERE rn > 1
That is a portable solution that works on almost all major DBMS
The way to do it with Oracle is the following:
SELECT country, capital FROM
( SELECT rownum rn, country, capital
FROM table
)
WHERE rn > 1
You cannot put a direct >N condition on rownum, because ROWNUMs are assigned when rows are fetched and your condition will never evaluate to TRUE.
Alternative is:
SELECT country, capital FROM table
MINUS
SELECT country, capital FROM table WHERE rownum <= 1