Comparison with results from sub-query in sqlite - sql

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);

Related

distinct on with case when

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.

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)

calculate highest nth salary using sql

I want to calculate highest nth salary in Oracle. I've already done with my solution but on google I found one query doing the same thing.
Here is the query
SELECT *
FROM Employee Emp1
WHERE (N - 1) = (SELECT COUNT(DISTINCT(Emp2.orig_salary))
FROM Employee Emp2
WHERE emp2.orig_salary > emp1.orig_salary)
Data
ID Name Birth Orig_Salary
2 John 15-JUL-97 2341
3 Joe 25-JAN-86 4321
4 Tom 13-SEP-06 2413
5 Jane 17-APR-05 7654
6 James 18-JUL-04 5679
7 Jodd 20-JUL-03 5438
8 Joke 01-JAN-02 8765
9 Jack 29-AUG-01 7896
I'm not not able to understand this query.
After running inner query it will always gives me count of 8 after doing this it will go to where clause where it will pick salary which is higher than the outer query salary. How equal operator is working here in between inner and outer query and how comparison is happening.
Could any help me to understand how this query is working technically in back-end..?
SELECT *
FROM Employee Emp1
WHERE (N - 1) = (SELECT COUNT(DISTINCT(Emp2.orig_salary))
FROM Employee Emp2 <--- cartesian product with same table
WHERE emp2.orig_salary > emp1.orig_salary) <---- but do the cartesian product only if line of salary of emp 2 is greater than the current line of Emp1 'salary
e.g assume there are only 3 lines in the table:
ID Name Birth Orig_Salary
2 John 15-JUL-97 2341
3 Joe 25-JAN-86 4321
4 Tom 13-SEP-06 5413
the main query will look at the first line --> 2 John 15-JUL-97 2341 <---, and subquery will return 2 because the salaries 4321 (emp2.orig_salary) and 5413 (emp2.orig_salary) are greater than 2341 (emp1.orig_salary)
the main query will then look at the second line --> 3 Joe 25-JAN-86 4321 <---, and subquery will return 1 because the salaries 5413 (emp2.orig_salary) is greater than 2341 (emp1.orig_salary)
when i say subquery, it is the
=(SELECT COUNT(DISTINCT(Emp2.orig_salary))
FROM Employee Emp2 <--- cartesian product with same table
WHERE emp2.orig_salary > emp1.orig_salary)
and the main query is
SELECT *
FROM Employee Emp1
WHERE
the returned value from the subquery is then compare to the where condition n-1, if the condition is satisfied, then it retrieves the line.
There is no need to understand that query. The correct formulation is:
SELECT Emp1.*
FROM (SELECT Emp1.*, DENSE_RANK() OVER (ORDER BY Emp2.orig_salary) as seqnum
FROM Employee Emp1
) Emp1
WHERE seqnum = <n>;
This gives the details for the employees. If you just want the salary:
SELECT orig_salary
FROM (SELECT Emp1.*, DENSE_RANK() OVER (ORDER BY Emp2.orig_salary) as seqnum
FROM Employee Emp1
) Emp1
WHERE seqnum = <n> AND rownum = 1;
I should note that a simpler version of this is:
select distinct orig_salary
from employees
order by orig_salary desc
offset <n - 1>
fetch first 1 row only;
The use of a correlated subquery for this is a pleasant anachronism from the days when relational databases were not as powerful as they are now. It is of historical interest.

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
);