SELECT Top 1 ID, DISTINCT Field - sql

I have a table sample table as follows:
ID | City
--------------
1 | New York
2 | San Francisco
3 | New York
4 | Los Angeles
5 | Atlanta
I would like to select the distinct City AND the TOP ID for each. E.g., conceptually I would like to do the following
SELECT TOP 1 ID, DISTINCT City
FROM Cities
Should give me:
ID | City
--------------
1 | New York
2 | San Francisco
4 | Los Angeles
5 | Atlanta
Because New York appears twice, it's taken the first ID 1 in this instance.
But I get the error:
Column 'Cities.ID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

Try this way:
SELECT min(ID), City
FROM Cities
Group by City
MIN function is used for choose one of the ID from two New York cities.

You need to have your city in a GROUP BY
SELECT MIN(ID), City
FROM Cities
GROUP BY City

More general solution is to use row_number in order to get other details of table:
select * from
(select *, row_number() over(partition by City order by ID) as rn from Cities)
where rn = 1
But for this particular table just grouping will do the work:
select City, Min(ID) as ID
from Cities
group by City

If you have a complex scenario where Group By cannot use, You could use Row_Number() function with Common Table Expression.
;WITH CTE AS
(
SELECT ID, City, ROW_NUMBER() OVER (PARTITION BY City ORDER BY Id) rn
FROM YourTable
)
SELECT Id, City
FROM CTE
WHERE rn = 1

Related

How to check how many times some values are duplicated?

I have table like below:
city | segment
------------------
London | A
London | B
New York | A
Berlin | B
Barcelona | C
Barcelona | H
Barcelona | E
Each city should have only one segment, but as you can see there are two cities (London and Barcelona) that have more than one segment.
It is essential that in result table I need only these cities which have > 1 segmnet
As a result I need somethig like below:
city - city based on table above
no_segments - number of segments which have defined city based on table above
segments - segments of defined city based on table above
city
no_segments
segments
London
2
A
B
Barcelona
3
C
H
E
How can I do that in Oracle?
You can use COUNT(*) OVER ()(in order to get number of segments) and ROW_NUMBER()(in order to prepare the results those will be conditionally displayed) analytic functions such as
WITH t1 AS
(
SELECT city,
segment,
COUNT(*) OVER (PARTITION BY city) AS no_segments,
ROW_NUMBER() OVER (PARTITION BY city ORDER BY segment) rn
FROM t
)
SELECT DECODE(rn,1,city) AS city,
DECODE(rn,1,no_segments) AS no_segments,
segment
FROM t1
WHERE no_segments > 1
ORDER BY t1.city, segment
Demo
Another way to do this is:
SELECT NULLIF(CITY, PREV_CITY) AS CITY,
SEGMENT
FROM (SELECT CITY,
LAG(CITY) OVER (ORDER BY CITY DESC) AS PREV_CITY,
SEGMENT,
COUNT(SEGMENT) OVER (PARTITION BY CITY) AS CITY_SEGMENT_COUNT
FROM CITY_SEGMENTS)
WHERE CITY_SEGMENT_COUNT > 1
Using LAG() to determine the "previous" CITY allows us to directly compare the CITY values, which in my mind is clearer that using ROW_NUMBER = 1.
db<>fiddle here
;with cte as (
Select city, count(seg) as cntseg
From table1
Group by city having count(seg) > 1
)
Select a.city, b.cntseg, a.seg
From table1 as a join cte as b
On a.city = b.city

Display Value From Another Row

I have to write a query to display the value for ID2 when ID=ID. Currently the table looks like the below.
ID
ID2
fname
lname
address1
address2
city
state
123
123
joe
smith
12 main st
los angeles
CA
122
122
james
jones
13 main st
new york
NY
123
3210
joe
smith
14 main st
los angeles
CA
124
124
mary
jones
15 main st
new york
NY
The desired output would look like this. Where I can do some sort of a self join to get the ID2 value.
ID
ID2
fname
lname
address1
address2
city
state
other ID
123
123
joe
smith
12 main st
los angeles
CA
3210
122
122
james
jones
13 main st
new york
NY
124
124
mary
jones
15 main st
new york
NY
Any ideas/suggestions greatly appreciated!
Update:
I was able to get the result with the additional columns using the below script. Thanks all for your assistance on this one!
with cte (id, id2, fname, lname, address1, address2, city, state)
as (select *
from (select *,row_number() over(partition by id order by id desc) as rn from your_table
) x
where rn = 2)
select x.id, x.id2, x.fname, x.lname, x.address1, x.address2, x.city, x.state, c.id2, c.address1
from (select *,row_number() over(partition by id order by id desc) as rn from your_table
) x
left join
cte c on x.id=c.id
where x.rn = 1
dbfiddle below
https://dbfiddle.uk/?rdbms=postgres_9.5&fiddle=4010ab08c5e32d9293d10e985adbfd7a
You could do something like the following, using a correlated subquery; using max ensures it only returns a single row should you have an Id with more than one different ID2:
select *,
(select max(id2) from t t2 where t2.id = t.id and t2.id2 != t.id) OtherId
from t
where id = id2
If you have two rows max with the same id, then you can try this :
SELECT (array_agg(t.*) FILTER (WHERE id = id2))[1].*
, (array_agg(t.id2) FILTER (WHERE id <> id2))[1] AS "other ID"
FROM your_table AS t
GROUP BY id
If you may have more than two rows with the same id then you can try this :
SELECT (array_agg(t.*) FILTER (WHERE id = id2))[1].*
, array_agg(t.id2) FILTER (WHERE id <> id2) AS "other IDs"
FROM your_table AS t
GROUP BY id
see the test result in dbfiddle

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

How to select only details of min value only in SQL?

I could get the minimum percentage of two values, but I need only the name, and ID in the select.
ID NAME CITY ONE TWO
--------------------------------------------------
2 Morales Los Angeles 40 10
1 John New York 60 20
4 Mary San Diego 10 10
I need to get the min value of one/two, and to only appear this as a result:
ID NAME
---------
4 Mary
Select ID, NAME
from MYTABLE
where least(ONE,TWO) = (select min(least(ONE,TWO)) from MYTABLE);
If you don't want Morales, then you can do this :
Select ID, NAME
from MYTABLE
where id =
(select id from
(select id from MYTABLE order by least(ONE,TWO), ONE*TWO)
where rownum <= 1);

SQL: select values in table A which are not in table B (and the other way around)

I have a hypothetical Census information by areas (Districts) collected in two different years:
Year 1 Table
District | Name
----------------
1 | Paul
1 | John
2 | Max
2 | Aaron
3 | Michael
Year 2 Table
District | Name
1 | Paul
1 | John
1 | Michael
3 | Michael
I'd like two queries:
Query 1: List new people by District. Should return Michael in District 1
Query 2: List missed people by District. Should return Max and Aaron in District 2
I would do this using union all and aggregation. The following query returns the name/district combinations that appear only in one year, along with the specific year:
select district, name, min(yyyy)
from ((select 1 as yyyy, district, name from table1) union all
(select 2, district, name from table2)
) tt
group by district, name
having count(distinct yyyy) = 1;
Query 1 : List new people by District. Should return Michael in District 1
SELECT Name, District
FROM year2
WHERE (Name NOT IN
(SELECT Name
FROM year1
WHERE (District = year2.District)))
GROUP BY Name, District
Query 2: List missed people by District. Should return Max and Aaron in District 2
SELECT Name, District
FROM year1
WHERE (Name NOT IN
(SELECT Name
FROM year2
WHERE (District = District)))
GROUP BY Name, District
If further you need combined result just use union all