distinct on with case when - sql

I need your help with conditional "distinct on".
For example I have a table "users".
id
name
age
1
John
17
2
Sam
18
3
John
12
4
Sam
19
And I want to do something like:
select case when (u.age > 17) then (distinct on u.name u.*) else (u.*) from users u order by u.name
And I want to do "distinct on" BUT with exclusion, if age is less than 17, then display them as well, if age is greater then take random one.
The output I want is:
id
name
age
1
John
17
2
Sam
18
3
John
12
There are two users with name Sam, and both have age > 17, then I want to apply distinct on in this case.
But there's at least 1 John with age less than 17, therefore I want to have all users with name John in the output.

It might be possible to put this in a condition, but in my opinion, that leads to bad readability.
I would prefer to use UNION ALL here to clearly split these two parts of your query:
SELECT DISTINCT ON (name) id, name, age
FROM users
WHERE age > 17
UNION ALL
SELECT id, name, age
FROM users
WHERE age <= 17
ORDER BY id;
And yes, I know this will select twice from the table, but I think this better in this case unless you observe a very poor performance using this query.

Related

Display DISTINCT value on SQL statement by column condition

i'm introducing you the problem with DISTINCT values by column condition i have dealt with and can't provide
any idea how i can solve it.
So. The problem is i have two Stephen here declared , but i don't want duplicates:
**
The problem:
**
id vehicle_id worker_id user_type user_fullname
9 1 NULL external_users John Dalton
10 1 16 employees Mike
11 1 1 employees Stephen
12 2 173 employee Nicholas
13 2 1 employee Stephen
14 1 NULL external_users Peter
**
The desired output:**
id vehicle_id worker_id user_type user_fullname
9 1 NULL external_users John Dalton
10 1 16 employees Mike
12 2 173 employee Nicholas
13 2 1 employee Stephen
14 1 NULL external_users Peter
I have tried CASE statements but without success. When i group by it by worker_id,
it removes another duplicates, so i figured out it needs to be grouped by some special condition?
If anyone can provide me some hint how i can solve this problem , i will be very grateful.
Thank's!
There are no duplicate rows in this table. Just because Stephen appears twice doesn't make them duplicates because the ID, VEHICLE_ID, and USER_TYPE are different.
What you need to do is decide how you want to identify the Stephen record you wish to see in the output. Is it the one with the highest VEHICLE_ID? The "latest" record, i.e. the one with the highest ID?
You will use that rule in a window function to order the rows within your criteria, and then use that row number to filter down to the results you want. Something like this:
select id, vehicle_id, worker_id, user_type, user_fullname
from (
select id, vehicle_id, worker_id, user_type, user_fullname,
row_number() over (partition by worker_id, user_fullname order by id desc) n
from user_vehicle
) t
where t.n = 1

How to find rows where the same column has only the same another column

Suppose following table:
Name Age Occupation
Alex 20 Student
Alex 20 Seller
Alex 20 Minister
Liza 19 Student
Liza 20 Volunteer
Liza 21 HR partner
I want to find names which have only (and only) 20 in age column. So from this table I want to get all "Alex" rows and no "Liza" rows at all.
Thanks!
You need to use Group By and Having clause. Try this way
select Name
from table
group by Name
having count(case when Age = 20 then 1 end) = count(*)
count(case when Age = 20 then 1 end) counts only when age = 20 if it is equal to total count then the name has only 20 as age.
Just one another way:
select Name
from table
group by Name
having min(Age) = 20 and max(Age) = 20
One way is using NOT IN():
SELECT Name, Age, Occupation
FROM YourTable
WHERE Age = 20
AND Name NOT IN (SELECT Name FROM YourTable WHERE Age <> 20)

Is it possible to SELECT N first rows from database using ROWNUM if some rows have the same value?

I'm trying to select N first rows from a database using ROWNUM. The problem is that if I want to select 6 first values of age with names of people with this age and some of the have the same value of age not every person will be shown.
For example
name age
Peter 15
Mark 22
Kelly 17
Mike 17
George 17
If I want to show people with 2 biggest values of age ROWNUM will show Mark and Kelly or Mike or George. Not all three of them. The expected result is:
Name age
Mark 22
Kelly 17
Mike 17
George 17
Is it possible to get this result using ROWNUM? Not RANK, DENSE_RANK, JOIN, correlated subquery but ROWNUM?
You can try something like this:
select *
from test
where age in (
select age
from (
select age
from test
group by age
order by age desc
)
where rownum <=2
)
The right solution dense_rank(), but you can do it with just row_number() and some subqueries:
select t.*
from t
where t.age in (select age
from (select age
from t t2
order by age desc
) x
where rownum <= 2
);
In Oracle 12+, you can simplify this:
select t.*
from t
where t.age in (select age
from t t2
order by age desc
fetch first 2 rows only
);

Comparison with results from sub-query in sqlite

Here is the code:
SELECT * FROM COMPANY WHERE SALARY > 40000;
4 Mark 25 Rich-Mond 65000.0
5 David 27 Texas 85000.0
6 Kim 22 South-Hall 45000.0
8 Kitos 31 90000.0
SELECT * FROM COMPANY
WHERE AGE < (SELECT AGE FROM COMPANY WHERE SALARY > 40000);
3 Teddy 23 Norway 20000.0
6 Kim 22 South-Hall 45000.0
7 James 24 Houston 10000.0
How does this work when there are multiple row returned from the sub-query? In this example I would expect the last query to produce employees younger than 22 (minimum from the sub-query), apparently it doesn't work that way.
Most databases will raise an error if the subquery does not return exactly one result. SQLite doesn't, but just uses the first returned row (or NULL) (there is an implied LIMIT 1).
The order of SELECT results is not guaranteed without an ORDER BY, so the result will be random.
If you want to use some specific record, you must ensure that you SELECT returns exactly that record, typically using MIN/MAX, or with ORDER BY:
SELECT ...
FROM Company
WHERE Age < (SELECT MIN(Age)
FROM Company
WHERE Salary > 40000);
SELECT ...
FROM Company
WHERE Age < (SELECT Age
FROM Company
WHERE Salary > 40000
ORDER BY Age
LIMIT 1);
It is also possible to use a correlated subquery, which can return a different result for each row in the outer query:
SELECT ...
FROM Company
WHERE Age < (SELECT Age
FROM Company AS C2
WHERE C2.ID = Company.ManagerID);

SQL Group By Help Required

I have a table named People in the following format:
Date | Name.
When I count the people by Grouping By Name with
Select Date, Name, count(*)
From People
Group By Date, Name;
Will give the following
Date Name count(*)
10 Peter 25
10 John 30
10 Mark 25
11 Peter 15
11 John 10
11 Mark 5
But I would like the following result:
Date Peter John Mark
10 25 30 25
11 15 10 5
Is this possible? This is a simple example of a more complicated database. If someone helps me in solving this problem I will use the concept to implement it in my table
Thanks!
Select Date
, count(case when Name = 'Peter' then 1 else null end)
, count(case when Name = 'John' then 1 else null end)
, count(case when Name = 'Mark' then 1 else null end)
From People
Group By Date;
another option different from turbanoff's if, for some reason, you find yourself in a situation that you cant apply a group by:
Select distinct(P.Date),
(select count(*) from People where date=p.date and name='Peter') as Peter,
(select count(*) from People where date=p.date and name='John') as John,
(select count(*) from People where date=p.date and name='Mark') as Mark
From People P