How do i get max salary row , group by country - sql

------------------------------------------------
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.

Related

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

There is a table having country and city columns as shown in the below table input. I need the output as mentioned below

I need a SQL query to get the desired output from the input table
You can do this with a UNION query, first selecting the distinct country names, and then each of the cities for that country. The output is then ordered by the country; whether the value is a country or a city; and then by the value:
SELECT DISTINCT country AS data, country, 1 AS ctry
FROM cities
UNION ALL
SELECT city, country, 0
FROM cities
ORDER BY country, ctry DESC, data
Output:
data country ctry
India India 1
BNG India 0
CHN India 0
HYD India 0
Sweden Sweden 1
GOTH Sweden 0
STOCK Sweden 0
VAXO Sweden 0
Demo on dbfiddle
It really looks like you are willing to interleave the records, with each country followed by its related countries.
The actual solution heavily depends on your datase, so let me assume that yours supports window functions, row constructor values() and lateral joins (SQL Server and Postgres are two candidates).
In SQL Server, you could do:
select distinct rn, idx, val
from (
select t.*, dense_rank() over(order by country) rn
from mytable t
) t
cross apply (values (t.country, 1), (t.city, 2)) as v(val, idx)
order by rn, idx, val
Demo on DB Fiddle:
rn | idx | val
-: | --: | :-----
1 | 1 | INDIA
1 | 2 | BNG
1 | 2 | CHN
1 | 2 | HYD
2 | 1 | SWEDEN
2 | 2 | STOCK
2 | 2 | VAXO
In Postgres, you would just replace outer apply with cross join lateral: Demo.

Get records based on column max value

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

plsql append rows together from select statements (similar to rbind)

Say I have a table:
CREATE TABLE Test_Scores
( last_name VARCHAR2(40),
score number(10));
And I want to select the top and bottom scores into a table. The two queries would be:
select last_name,score, 'MAX' as Score_Type from Test_Scores
where score = (select max(score) from Test_Scores)
-> Last_name | Score | Score_Type
Smith | 15 | Max
select last_name,score, 'MIN' as Score_Type from Test_Scores
where score = (select min(score) from Test_Scores)
-> Last_name | Score | Score_Type
Jones | 5 | Min
How do I set these two rows together? In SAS I would use a set statement, and in R I would use rbind. Is there a pl sql equivalent (Oracle 11g)?
The final output would be:
Last_name | Score | Score_Type
Smith | 15 | Max
Jones | 5 | Min
Thanks in advance.
This can be done using plain SQL and a window (aka "analytic") function. There is no need for PL/SQL here (PL/SQL is only used for stored procedures)
select last_name,
score
from (
select last_name,
score,
dense_rank() over (order by score desc) as ranked_first,
dense_rank() over (order by score asc) as ranked_last
from test_scores
)
where ranked_first = 1
or ranked_last = 1;

SQL group by with a count

I have a table (simplified below)
|company|name |age|
| 1 | a | 3 |
| 1 | a | 3 |
| 1 | a | 2 |
| 2 | b | 8 |
| 3 | c | 1 |
| 3 | c | 1 |
For various reason the age column should be the same for each company. I have another process that is updating this table and sometimes it put an incorrect age in. For company 1 the age should always be 3
I want to find out which companies have a mismatch of age.
Ive done this
select company, name age from table group by company, name, age
but dont know how to get the rows where the age is different. this table is a lot wider and has loads of columns so I cannot really eyeball it.
Can anyone help?
Thanks
You should not be including age in the group by clause.
SELECT company
FROM tableName
GROUP BY company, name
HAVING COUNT(DISTINCT age) <> 1
SQLFiddle Demo
If you want to find the row(s) with a different age than the max-count age of each company/name group:
WITH CTE AS
(
select company, name, age,
maxAge=(select top 1 age
from dbo.table1 t2
group by company,name, age
having( t1.company=t2.company and t1.name=t2.name)
order by count(*) desc)
from dbo.table1 t1
)
select * from cte
where age <> maxAge
Demontration
If you want to update the incorrect with the correct ages you just need to replace the SELECT with UPDATE:
WITH CTE AS
(
select company, name, age,
maxAge=(select top 1 age
from dbo.table1 t2
group by company,name, age
having( t1.company=t2.company and t1.name=t2.name)
order by count(*) desc)
from dbo.table1 t1
)
UPDATE cte SET AGE = maxAge
WHERE age <> maxAge
Demonstration
Since you mentioned "how to get the rows where the age is different" and not just the comapnies:
Add a unique row id (a primary key) if there isn't already one. Let's call it id.
Then, do
select id from table
where company in
(select company from table
group by company
having count(distinct age)>1)