SQL QUERY USING POSTGRESQL - sql

I have the following query:
SELECT DISTINCT "stylists".* FROM "stylists"
INNER JOIN "category_stylists" ON "category_stylists"."stylist_id" = "stylists"."id"
WHERE category_stylists.category_id IN (1,2)
But I want to order the stylists by categories.
For example:
Stylists
id | Name
1 Sebastian
2 Jhon
Categories
id | Name
1 Wedding
2 Office
Stylist_Categories
id | stylist_id | category_id
1 1 1
2 2 1
3 2 2
So If I apply my query, I got both Stylists, but I need always order by how many categories has the stylist. I mean in this example Jhon will be the first row because has the category_id 1 and category_id 2.
Result expected:
Stylists
id | Name
2 Jhon
1 Sebastian
Thanks in advance!

Try counting categories for each stylist and order by it:
SELECT "id","name" from(
SELECT "stylists"."id", "stylists"."Name",count(stylists.id) as cnt FROM "stylists"
INNER JOIN "category_stylists" ON "category_stylists"."stylist_id" = "stylists"."id"
WHERE category_stylists.category_id IN (1,2)
group by "stylists"."id", "stylists"."Name")
order by cnt desc

Related

How to filter by all FK coincidence

I have 2 tables, with this structure:
Table: Matches
MatchId | MatchResult
Table: Player_Matches
MatchId | PlayerId
And data currently looks like this:
Matches:
MatchId | MatchResult
1 | 4-2
2 | 2-1
3 | 3-1
Player_Matches:
MatchId | PlayerId
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
3 | 1
4 | 2
4 | 3
The idea is that every match can have between 1 and 5 players.
How can I create a select statement recieving 1 to 5 parameters, and only get the id of those matches played by all the players passed as parameters?
For example these would be the expected output for these values:
input1 = 1 results would be:
1
2
3
input1 = 1 and input2=2 results would be:
1
2
input1= 1 and input2= 3 results would be:
1
Thanks in advance for the assistance
One simple approach uses aggregation:
SELECT MatchId
FROM Player_Matches
WHERE PlayerId IN (1, 2, 3) -- players 1, 2, 3
GROUP BY MatchId
HAVING COUNT(DISTINCT PlayerId) = 3;
The above query would only return the first match. It should be clear how to modify it as needed.
This is surprisingly tricky but can be broken down with the help of a cte to simplify matters.
Given a list of 5 parameters, any or all of which can have a specified PlayerId
Using a set of these PlayerIds built from values of the parameters, get a list of matches played by all the passed Id values and a count of matches played by each Id
Then filter this list where each player's Matchcount is the count of how many unique matches exist for the player in the list.
declare #p1 tinyint,#p2 tinyint,#p3 tinyint,#p4 tinyint,#p5 tinyint
select #p1=1;
with l as (
select MatchId, PlayerId, Count(*) over(partition by matchid) MatchCount
from t
where exists(
select * from (values (#p1),(#p2),(#p3),(#p4),(#p5))x(id) where x.id=t.playerid
)
);
select l.MatchId
from l
where MatchCount=(select Count(distinct playerid) from l)
group by l.Matchid;
See DB Fiddle for examples.

Selecting only if on column value is distinct by other column

I have a linking table with rule_id to sub_rule_id as such:
rule_id | sub_rule_id
---------------------
1 | 1
2 | 1
2 | 2
2 | 3
3 | 3
3 | 4
I want to be able to get all the sub_rule_ids which are linked to only one rule_is by rule_id. So if my rule_id = 1 then I expected no rows. And if rule_id = 2 then I should get just one. Tried to play with distinct and having and would not trouble you with a bad query.. I am sure there is an easy elegant way to do it.
Thanks in advance
You can group by sub_rule_id amd set the condition in the having clause:
select sub_rule_id
from tablename
group by sub_rule_id
having count(distinct rule_id) = 1
Or with NOT EXISTS if you want full rows:
select t.* from tablename t
where not exists (
select 1 from tablename
where sub_rule_id = t.sub_rule_id
and rule_id <> t.rule_id
)

Count how many times a value appears in tables SQL

Here's the situation:
So, in my database, a person is "responsible" for job X and "linked" to job Y. What I want is a query that returns: name of person, his ID and he number of jobs it's linked/responsible. So far I got this:
select id_job, count(id_job) number_jobs
from
(
select responsible.id
from responsible
union all
select linked.id
from linked
GROUP BY id
) id_job
GROUP BY id_job
And it returns a table with id in the first column and number of occurrences in the second. Now, what I can't do is associate the name of person to the table. When i put that in the "select" from beginning it gives me all the possible combinations... How can I solve this? Thanks in advance!
Example data and desirable output:
| Person |
id | name
1 | John
2 | Francis
3 | Chuck
4 | Anthony
| Responsible |
process_no | id
100 | 2
200 | 2
300 | 1
400 | 4
| Linked |
process_no | id
101 | 4
201 | 1
301 | 1
401 | 2
OUTPUT:
| OUTPUT |
id | name | number_jobs
1 | John | 3
2 | Francis | 3
3 | Chuck | 0
4 | Anthony | 2
Try this way
select prs.id, prs.name, count(*) from Person prs
join(select process_no, id
from Responsible res
Union all
select process_no, id
from Linked lin ) a on a.id=prs.id
group by prs.id, prs.name
I would recommend aggregating each of the tables by the person and then joining the results back to the person table:
select p.*, coalesce(r.cnt, 0) + coalesce(l.cnt, 0) as numjobs
from person p left join
(select id, count(*) as cnt
from responsible
group by id
) r
on r.id = p.id left join
(select id, count(*) as cnt
from linked
group by id
) l
on l.id = p.id;
select id, name, count(process_no) FROM (
select pr.id, pr.name, res.process_no from Person pr
LEFT JOIN Responsible res on pr.id = res.id
UNION
select pr.id, pr.name, lin.process_no from Person pr
LEFT JOIN Linked lin on pr.id = lin.id) src
group by id, name
order by id
Query ain't tested, give it a shot, but this is the way you want to go

INNER JOIN and COUNT in the same query

I am having trouble with putting together INNER JOIN and COUNT in the same query.
Tables are:
TABLE STREETS
ID | STREET_NAME
------------------------
1 | Elm street
2 | Some other street
3 | Unknown street
4 | Killer street
5 | Dead-end street
TABLE ACCIDENTS_STREETS
STREET_ID | ACCIDENT_ID
-----------------------
2 | 4
2 | 7
2 | 2
2 | 1
5 | 3
I would like to get the street name where most accidents have occured.
This is for COUNT:
SELECT TOP 1 COUNT(STREET_ID) AS dangerous_street FROM ACCIDENTS_STREETS GROUP BY STREET_ID ORDER BY dangerous_street DESC
How to add INNER JOIN there to get only the name of the street?
Any advice is appreciated!
The Following should work
SELECT TOP 1 S.STREET_NAME,COUNT(a.*) AS dangerous_street
FROM ACCIDENTS_STREETS A
inner Join STREET S on S.ID = A.STREET_ID
GROUP BY S.STREET_NAME ORDER BY dangerous_street DESC
try this...
After joining the streets table, you would have to use an aggregation function to get the name in the resultset
SELECT
TOP 1 COUNT(STREET_ID) AS dangerous_street
, min( STREET_NAME) dangerous_STREET_NAME
FROM ACCIDENTS_STREETS acc
inner join STREETS str
on acc.STREET_ID=str.id
GROUP BY STREET_ID
ORDER BY dangerous_street DESC

PostgreSQL Order By SubQuery

I have this database structure:
Tables:
Category
id | category
1 | fruit
2 | cars
3 | tables
Product
id | product | category_id
1 | banana | 1
2 | apple | 1
3 | orange | 1
4 | example 1 | 2
5 | example 2 | 3
User_List
id | product_id | user_id | bought_date
1 | 1 | 1 | 2012-06-21 11:00:00
2 | 2 | 1 | 2012-06-21 06:00:00
3 | 4 | 1 | 2012-06-21 08:00:00
4 | 5 | 1 | 2012-06-21 01:00:00
what i want is create a query that "order by bought_date (desc) by category".
In that case the expected result is:
banana
apple
example 1
example 2
My query:
SELECT c.id, u.bought_date
FROM categry as c
left join product p on (c.id=p.category_id)
left join user_list u on (p.id=u.product_id)
WHERE u.user_id=3
ORDER BY u.bought_date DESC NULLS LAST
But this only does a simple sort by bought date...
with this result:
banana
example 1
apple
example 2
I thought of one ordering. You want to order by the earliest or latest date for each category. For that, use window functions.
SELECT c.id, u.bought_date, max(u.bought_date) over (partition by c.id) as category_bd
FROM categry c left join
product p
on (c.id=p.category_id) left join
user_list u
on (p.id=u.product_id)
WHERE u.user_id = 3
ORDER BY category_bd DESC NULLS LAST, u.bought_date DESC NULLS LAST
It sounds as though you just need two columns in your order by clause:
SELECT c.id, u.bought_date
FROM categry as c
left join product p on (c.id=p.category_id)
left join user_list u on (p.id=u.product_id)
WHERE u.user_id=3
ORDER BY category_id, u.bought_date DESC NULLS LAST
Assuming missing information:
bought_date is defined NOT NULL.
There can be multiple rows in user_list per product, and they can have a different bought_date.
Order products by the latest bought_date of their category first,
and by their latest bought_date next.
SELECT p.product
FROM product p
JOIN user_list u ON u.product_id = p.id
GROUP BY 1
ORDER BY max(max(u.bought_date)) OVER (PARTITION BY p.category_id) DESC
, max(u.bought_date) DESC;
You don't need the table categry in the query at all.
The window function to get the latest bought_date per category can go into the ORDER BY clause.
Yes, that's a window function over the aggregate max(u.bought_date).
Obviously you don't want oranges in the result, since nobody "brought" them.
Use JOIN, not LEFT JOIN.