Get records based on column max value - sql

I have cars table with data
country | car | price
---------------------
Germany | Mercedes | 30000
Germany | BMW | 20000
Germany | Opel | 15000
Japan | Honda | 20000
Japan | Toyota | 15000
I need get country, car and price from table, with highest price
for each country
Germany Mercedes 30000
Japan Honda 20000

try
select cars.* FROM cars
INNER JOIN (
select country, max(price) AS maxprice from cars
GROUP BY country
) m
ON cars.country = m.country AND cars.price = m.maxprice

Use ROW_NUMBER()
SELECT *
FROM ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY country
ORDER BY price DESC) as rn
FROM cars ) as T
WHERE T.rn = 1
If you allow ties, use RANK instead
SELECT *
FROM ( SELECT *,
RANK() OVER (PARTITION BY country
ORDER BY price DESC) as rn
FROM cars ) as T
WHERE T.rn = 1

Related

Sum with group by in postgres

I've a table countries_percent like following:
country_name
group_name
percent_value
US
EMEA
10.00
US
25.00
GB
EMEA
15.00
AP
Domestic
20.00
AK
Domestic
12.00
ES
5.00
Now I would like to select the highest sum of percent_value for each group by country e.g:
country_name
group_name
sum_percent
US
EMEA
35.00
AP
Domestic
20.00
so here US is selected because the sum of its percentage was highest for EMEA group. Please note that NULL group_name is counted towards EMEA because of country_name match. Also note that ES didn't appear as it didn't have any group_name against it and there is no other group with value ES.
Is it possible to query the above with Postgres?
From your Result you bSkip Spain, because it has no group.
So you can do it like this
WITH SUMCTE AS (SELECT "country_name", MAX("group_name") as group_name, SUM("percent_value") AS percent_value
FROM tab1
GROUP BY "country_name"),
CTE_ROW_NUM AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY "group_name" ORDER BY "percent_value" DESC ) rn
FROM SUMCTE
WHERE "group_name" IS NOT NULL)
SELECT "country_name", "group_name", "percent_value"
FROM CTE_ROW_NUM
WHERE rn = 1
ORDER BY "percent_value" DESC
country_name | group_name | percent_value
:----------- | :--------- | ------------:
US | EMEA | 35.00
AP | Domestic | 20.00
db<>fiddle here
SAMPLE DATA RESULTS
WITH country_level_info AS (
SELECT
country_name,
MAX(group_name) AS group_name, -- MAXIMUM ON GROUP NAME WILL CHOOSE THE NON NULL VALUE WHICH IS WHAT WE WANT
SUM(percent_value) AS sum_percent
FROM data
GROUP BY 1
HAVING MAX(group_name) != '' OR MAX(group_name) != NULL
),
ranking AS (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY group_name ORDER BY sum_percent DESC) AS rank_
FROM country_level_info
)
SELECT country_name,
group_name,
sum_percent
FROM ranking WHERE rank_ = 1
ORDER BY sum_percent DESC

Row Number by Certain Column in SQL

I have a table that contains customer transactions. It looks like this:
Tha data is sorted by Total Transaction. I want to create a column that contains number by City. For Example, the first row shows City is London so the values is 1, second row becaus it's from London too, the value is also 1. When the Next Row is not London, the value is 2. So it looks like this:
Is there a way to create that row number in SQL Server?
You can try using dense_rank()
select *,dense_rank() over(order by city) as cityNumber
from tablename
order by total_transaction desc
I believe the question is valid and as per my understanding on the requirement , you need a two level of sub query to get to the final result,
Here I have used max as the data first has to be sorted by Total Transaction and then we can use dense_rank to give a row number using the max value and city.
select t.city as "City"
,dense_rank() over (order by max_total_per_city desc,city) as "City Number"
,t.customer as "Customer"
,t.total_transaction as "Total Transaction"
from
(
select *
,max(total_transaction) over (partition by city) as max_total_per_city
from tableName t
) t
order by total_transaction desc
You can get the CityNumbers with ROW_NUMBER() window function:
select City, row_number() over (order by max(TotalTransaction) desc) CityNumber
from tablename
group by City
so you can join the above query to the table:
select t.City, c.CityNumber, t.Customer, t.Totaltransaction
from tablename t inner join (
select City, row_number() over (order by max(TotalTransaction) desc) CityNumber
from tablename
group by City
) c on c.City = t.City
order by t.TotalTransaction desc
Or with DENSE_RANK() window function:
select t.City,
dense_rank() over (order by (select max(TotalTransaction) from tablename where City = t.City) desc) as cityNumber,
t.Customer,
t.TotalTransaction
from tablename t
order by t.TotalTransaction desc
See the demo.
Results:
> City | CityNumber | Customer | Totaltransaction
> :--------- | ---------: | :------- | ---------------:
> London | 1 | Michael | 250
> London | 1 | Edward | 180
> Paris | 2 | Michael | 160
> Madrid | 3 | Luis | 153
> London | 1 | Serena | 146
> Madrid | 3 | Lionel | 133
> Manchester | 4 | Frank | 96

Unique values with string_agg on more than 1 column

I am trying to group by and get list of values for multiple columns. Here is an example:
City | State | Income
-------+-------+--------
Salem | OH | 40000
Salem | OH | 45000
Mason | OH | 50000
Dayton | OH | 60000
Salem | MA | 40000
Mason | MA | 45000
Mason | MA | 50000
Dayton | MA | 70000
Salem | PA | 45000
Mason | PA | 50000
Dayton | PA | 60000
The result I am looking for is:
City | States | Income
-------+------------+--------------
Salem | OH,MA,PA | 40000,45000
Mason | OH,MA,PA | 50000,45000
Dayton | OH,MA,PA | 60000,70000
I managed to get this far:
City | States | Income
-------+------------+-------------------------
Salem | OH,MA,PA | 40000,40000,45000,45000
Mason | OH,MA,PA | 50000,50000,50000,45000
Dayton | OH,MA,PA | 60000,70000,60000
How do I go from here to the result set?
City | States | Income
-------+------------+-------------------------
Salem | OH,MA,PA | 40000,45000,50000
Mason | OH,MA,PA | 50000,45000
Dayton | OH,MA,PA | 60000,70000
Alas, you cannot use string_agg() with distinct. But you can use conditional aggregation:
select city,
string_agg(case when seqnum_state = 1 then state end, ',') as states,
string_agg(case when seqnum_income = 1 then income end, ',') as incomes
from (select t.*,
row_number() over (partition by city, state order by state) as seqnum_state,
row_number() over (partition by city, income order by income) as seqnum_income
from t
) t
group by city;
Here is a db<>fiddle.
You can perform separate group by on (City, state) and (City, Income) to remove duplicates, then you can separately build the (States) and (Incomes) aggregated strings and finally you can join the results in a single table:
DECLARE #tmp TABLE (City VARCHAR(100), State VARCHAR(100), Income int);
INSERT INTO #tmp
VALUES ('Salem' ,'OH', 40000) ,('Salem' ,'OH', 45000) ,('Mason' ,'OH', 50000)
,('Dayton','OH', 60000) ,('Salem' ,'MA', 40000) ,('Mason' ,'MA', 45000)
,('Mason' ,'MA', 50000) ,('Dayton','MA', 70000) ,('Salem' ,'PA', 45000)
,('Mason' ,'PA', 50000) ,('Dayton','PA', 60000)
;with States as(
select City, state
from #tmp
group by City, state
),
incomes as(
select City, Income
from #tmp
group by City, Income
)
, states_g as (
select city, STRING_AGG(state,',') as States
from states
group by city
)
, incomes_g as (
select city, STRING_AGG(Income,',') as Incomes
from incomes
group by city
)
select
s.City, s.States, i.Incomes
from
states_g as s
inner join
incomes_g as i
on i.City = s.City
Results:
Here is one more way of doing it (db fiddle):
select city,
(select string_agg(value,', ') from (select distinct value from string_split(string_agg(state, ','),',')) t) as states,
(select string_agg(value,', ') from (select distinct value from string_split(string_agg(income, ','),',')) t) as incomes
from t
group by city;
You may easily convert the splitting and merging part into a reusable scalar valued function.
Experts are welcome to comment on performance.

How do i get max salary row , group by country

------------------------------------------------
country | salary | name | adress
------------------------------------------------
India | 10000 | ch1 | something
japan | 20000 | ch2 | nothing
india | 25000 | ch3 | hjsdj
japan | 30000 | ch4 | hjhjhj
I need to get max salary in japan and also in india with adress, name.
Have a sub-query that returns each country's max salary. Join with that result to get the users with that max salary.
select t1.*
from tablename t1
join (select country, max(salary) as max_salary
from tablename group by country) t2
on t1.country = t2.country and t1.salary = t2.max_salary
Will return both users if it's a tie (i.e. several users with same maximum salary.)
If by chance your DBMS supports ROW_NUMBER() OVER() function you may try
select * from
(select ROW_NUMBER() OVER(PARTITION BY country ORDER BY salary DESC) as rn
, country, salary as max_salary, name, adress
from tablename
) t
where rn = 1
Note, contrary to jarlh's query it will return only one arbitrary row of equal top salaries rows for the country.

Select all data with no duplicate data

I have some data in database:
Name | Country | Status
Mary | USA | Pending
Jane| Japan | Pending
Jane | Korea | Pending
Adrian | China | Pending
Peter | Singapore | Pending
Jack | Malaysia | Pending
Adrian | China | Updated
Jane | Japan | Updated
May I know how to use the SELECT query to select all the data with no duplicate data? (If the duplicates data exist, select only the Status with Updated)
Try:
SELECT Name, Country, MAX(Status) as Status FROM (
SELECT TOP 100 PERCENT *
FROM NameCountry
ORDER BY Name ASC, Country ASC, Status DESC
) G
GROUP BY G.Name, G.Country
ORDER BY G.Name, G.Country
Check my Demo
From your comment, you seem to mean only data where the first two columns are duplicated. The easiest way, I think, is to use row_number(), which is available in most databases:
select name, country, status
from (select t.*,
row_number() over (partition by name, country
order by (case when status = 'Pending' then 0 else 1 end)
) as seqnum
from t
) t
where seqnum = 1