Обсуждение: Extend inner join to fetch not yet connected rows also
I have craftsmanships table which has (id, name) and users table (id, email, ..). When a user has some craftsmanships,
theyare stored inside the contractor_skills(user_id, craftsmanship_id, id) table.
What I want here is that to list all the available craftsmanships with id, name and has column. I can get now only
thosecraftsmanships that a specific user has,
SELECT
craftsmanships.id,
craftsmanships.name,
TRUE as has
FROM
"craftsmanships"
INNER JOIN "contractor_skills" ON "contractor_skills"."craftsmanship_id" = "craftsmanships"."id"
INNER JOIN "users" ON "users"."id" = "contractor_skills"."user_id"
WHERE (contractor_skills.user_id = 8)
ORDER BY
"craftsmanships".”id"
——————
id | name | has
----+---------+-----
1 | paint | t
2 | drywall | t
(2 rows)
But I want to list all craftsmanships and has column should have `t` when user_id #8 has it, else `f`. How can I extend
this query?
Thanks,
Arup Rakshit
ar@zeit.io
Hey,
Am Sonntag, 22. September 2019, 13:21:46 CEST schrieb Arup Rakshit:
> I have craftsmanships table which has (id, name) and users table (id, email,
> ..). When a user has some craftsmanships, they are stored inside the
> contractor_skills(user_id, craftsmanship_id, id) table.
[...]
> But I want to list all craftsmanships and has column should have `t` when
> user_id #8 has it, else `f`. How can I extend this query?
maybe something like
select
c.id,
c.name,
case when cs.user_id = 8 then true else false end as has
from craftsmanships c
left join contractor_skills cs
on cs.craftsmanship_id = c.craftmanship_id;
--
MfG Jan
Hi Jan,
I was close and came up with:
SELECT
craftsmanships.id,
craftsmanships.name,
CASE WHEN contractor_skills.user_id IS NULL THEN
FALSE
ELSE
TRUE
END AS has
FROM
"craftsmanships"
LEFT JOIN "contractor_skills" ON "contractor_skills"."craftsmanship_id" = "craftsmanships"."id"
LEFT JOIN "users" ON "users"."id" = "contractor_skills"."user_id"
WHERE (contractor_skills.user_id = 3
OR contractor_skills.user_id IS NULL)
ORDER BY
"craftsmanships"."id”;
But after I read yours I found mine is doing lot of unnecessary joins. Thank you.
Thanks,
Arup Rakshit
ar@zeit.io
> On 22-Sep-2019, at 5:38 PM, Jan Kohnert <nospam001-lists@jan-kohnert.de> wrote:
>
> Hey,
>
> Am Sonntag, 22. September 2019, 13:21:46 CEST schrieb Arup Rakshit:
>> I have craftsmanships table which has (id, name) and users table (id, email,
>> ..). When a user has some craftsmanships, they are stored inside the
>> contractor_skills(user_id, craftsmanship_id, id) table.
>
> [...]
>
>> But I want to list all craftsmanships and has column should have `t` when
>> user_id #8 has it, else `f`. How can I extend this query?
>
> maybe something like
>
> select
> c.id,
> c.name,
> case when cs.user_id = 8 then true else false end as has
> from craftsmanships c
> left join contractor_skills cs
> on cs.craftsmanship_id = c.craftmanship_id;
>
> --
> MfG Jan
>
>
>
>
Hi Jan, > On 22-Sep-2019, at 5:38 PM, Jan Kohnert <nospam001-lists@jan-kohnert.de> wrote: > > Hey, > > Am Sonntag, 22. September 2019, 13:21:46 CEST schrieb Arup Rakshit: >> I have craftsmanships table which has (id, name) and users table (id, email, >> ..). When a user has some craftsmanships, they are stored inside the >> contractor_skills(user_id, craftsmanship_id, id) table. > > [...] > >> But I want to list all craftsmanships and has column should have `t` when >> user_id #8 has it, else `f`. How can I extend this query? > > maybe something like > > select > c.id, > c.name, > case when cs.user_id = 8 then true else false end as has > from craftsmanships c > left join contractor_skills cs > on cs.craftsmanship_id = c.craftmanship_id; > > -- > MfG Jan > But this query fetched duplicate data: id | name | has ----+---------------------------------------+----- 2 | drywall | t 1 | paint | t 1 | paint | f 11 | landscaping | f 12 | electrical | f 10 | countertops | f 13 | plumbing | f 5 | flooring | f 8 | decks (displayed as decks and patios) | f 6 | basements | f 4 | kitchens | f 3 | bathrooms | f 14 | handyman | f 9 | windows (windows and doors) | f 7 | carpentry | f (15 rows) >
Hi Arup,
Am Sonntag, 22. September 2019, 14:48:20 CEST schrieb Arup Rakshit:
> Hi Jan,
>
> > On 22-Sep-2019, at 5:38 PM, Jan Kohnert <nospam001-lists@jan-kohnert.de>
> > wrote:
> > maybe something like
> >
> > select
> >
> > c.id,
> > c.name,
> > case when cs.user_id = 8 then true else false end as has
> >
> > from craftsmanships c
> > left join contractor_skills cs
> >
> > on cs.craftsmanship_id = c.craftmanship_id;
>
> But this query fetched duplicate data:
yeah, that's possible, since I don't exactly know your data model. If only the
values above are required, you could simply use distinct:
select distinct
c.id,
c.name,
case when cs.user_id = 8 then true else false end as has
from craftsmanships c
left join contractor_skills cs
on cs.craftsmanship_id = c.id
order by
c.id;
--
MfG Jan
> On 22-Sep-2019, at 6:55 PM, Jan Kohnert <nospam001-lists@jan-kohnert.de> wrote:
>
> Hi Arup,
>
> Am Sonntag, 22. September 2019, 14:48:20 CEST schrieb Arup Rakshit:
>> Hi Jan,
>>
>>> On 22-Sep-2019, at 5:38 PM, Jan Kohnert <nospam001-lists@jan-kohnert.de>
>>> wrote:
>>> maybe something like
>>>
>>> select
>>>
>>> c.id,
>>> c.name,
>>> case when cs.user_id = 8 then true else false end as has
>>>
>>> from craftsmanships c
>>> left join contractor_skills cs
>>>
>>> on cs.craftsmanship_id = c.craftmanship_id;
>>
>> But this query fetched duplicate data:
>
> yeah, that's possible, since I don't exactly know your data model. If only the
> values above are required, you could simply use distinct:
When someone adds a craftsmanship to their skill set, the contractor_skills table holds that relationship. I don’t
thinkdistinct is the correct tool, as it will eliminate the correct data. users and craftsmanship has m:n relationship
viathe join table contractor_skills.
SELECT
craftsmanships.id,
craftsmanships.name,
CASE WHEN contractor_skills.user_id IS NULL THEN
FALSE
ELSE
TRUE
END AS has
FROM
"craftsmanships"
LEFT JOIN "contractor_skills" ON "contractor_skills"."craftsmanship_id" = "craftsmanships"."id"
LEFT JOIN "users" ON "users"."id" = "contractor_skills"."user_id"
WHERE (contractor_skills.user_id = 8
OR contractor_skills.user_id IS NULL)
ORDER BY
"craftsmanships"."id”;
Gives correct result. Not sure if still this query has bug in it.
id | name | has
----+---------------------------------------+-----
1 | paint | t
2 | drywall | t
3 | bathrooms | f
4 | kitchens | f
5 | flooring | f
6 | basements | f
7 | carpentry | f
8 | decks (displayed as decks and patios) | f
9 | windows (windows and doors) | f
10 | countertops | f
11 | landscaping | f
12 | electrical | f
13 | plumbing | f
14 | handyman | f
(14 rows)
>
> select distinct
> c.id,
> c.name,
> case when cs.user_id = 8 then true else false end as has
> from craftsmanships c
> left join contractor_skills cs
> on cs.craftsmanship_id = c.id
> order by
> c.id;
>
> --
> MfG Jan
>
>
>
>
On 9/22/19 6:30 AM, Arup Rakshit wrote: > >> On 22-Sep-2019, at 6:55 PM, Jan Kohnert <nospam001-lists@jan-kohnert.de> wrote: >> >> Hi Arup, >> >> Am Sonntag, 22. September 2019, 14:48:20 CEST schrieb Arup Rakshit: >>> Hi Jan, >>> >>>> On 22-Sep-2019, at 5:38 PM, Jan Kohnert <nospam001-lists@jan-kohnert.de> >>>> wrote: >>>> maybe something like >>>> >>>> select >>>> >>>> c.id, >>>> c.name, >>>> case when cs.user_id = 8 then true else false end as has >>>> >>>> from craftsmanships c >>>> left join contractor_skills cs >>>> >>>> on cs.craftsmanship_id = c.craftmanship_id; >>> >>> But this query fetched duplicate data: >> >> yeah, that's possible, since I don't exactly know your data model. If only the >> values above are required, you could simply use distinct: > > When someone adds a craftsmanship to their skill set, the contractor_skills table holds that relationship. I don’t thinkdistinct is the correct tool, as it will eliminate the correct data. users and craftsmanship has m:n relationship viathe join table contractor_skills. > > SELECT > craftsmanships.id, > craftsmanships.name, > CASE WHEN contractor_skills.user_id IS NULL THEN > FALSE > ELSE > TRUE > END AS has > FROM > "craftsmanships" > LEFT JOIN "contractor_skills" ON "contractor_skills"."craftsmanship_id" = "craftsmanships"."id" > LEFT JOIN "users" ON "users"."id" = "contractor_skills"."user_id" > WHERE (contractor_skills.user_id = 8 > OR contractor_skills.user_id IS NULL) > ORDER BY > "craftsmanships"."id”; > > Gives correct result. Not sure if still this query has bug in it. What I see is that the rows below with 'has' = 'f' will not have a user_id(implied). So I am not sure how you plan to associate that data with a user? > > id | name | has > ----+---------------------------------------+----- > 1 | paint | t > 2 | drywall | t > 3 | bathrooms | f > 4 | kitchens | f > 5 | flooring | f > 6 | basements | f > 7 | carpentry | f > 8 | decks (displayed as decks and patios) | f > 9 | windows (windows and doors) | f > 10 | countertops | f > 11 | landscaping | f > 12 | electrical | f > 13 | plumbing | f > 14 | handyman | f > (14 rows) > > > > >> >> select distinct >> c.id, >> c.name, >> case when cs.user_id = 8 then true else false end as has >> from craftsmanships c >> left join contractor_skills cs >> on cs.craftsmanship_id = c.id >> order by >> c.id; >> >> -- >> MfG Jan >> >> >> >> > > > > -- Adrian Klaver adrian.klaver@aklaver.com
On Sun, Sep 22, 2019 at 6:30 AM Arup Rakshit <ar@zeit.io> wrote:
SELECT
craftsmanships.id,
craftsmanships.name,
CASE WHEN contractor_skills.user_id IS NULL THEN
FALSE
ELSE
TRUE
END AS has
FROM
"craftsmanships"
LEFT JOIN "contractor_skills" ON "contractor_skills"."craftsmanship_id" = "craftsmanships"."id"
LEFT JOIN "users" ON "users"."id" = "contractor_skills"."user_id"
WHERE (contractor_skills.user_id = 8
OR contractor_skills.user_id IS NULL)
ORDER BY
"craftsmanships"."id”;
Gives correct result. Not sure if still this query has bug in it.
If you do not understand the query - then it's wrong on its face. You should never run something which you do not understand.
So one should take a step back - make smaller pieces and then combine smaller pieces of logic together to form an answer. If at some point in the future there is a performance issue - then deal with that then - but do not make some fancy multi join query that you do not fully understand.
So in that vein,
Piece 1 = A list of craftsmanship_id for a particular user
Piece 2 - Take piece 1 and compare to the full list of craftsmanship_id
Putting piece 1 into a CTE you end up with something like this.
with UserSkills as (
SELECT
craftsmanship_id
FROM
contractor_skills
WHERE
user_id = 3
)
SELECT
CASE WHEN UserSkills.ctraftsmanship_id IS NULL THEN FALSE
ELSE TRUE as has
FROM
craftsmanships
LEFT JOIN
UserSkills
ON
craftsmanships.id = UserSkills.craftsmanship_id
So you take the two pieces and combine then. Yes you can drop the CTE into the main body - but unless you are certain you are doing it correctly - there is no point doing that. The query parser will do the work for you - so why bother making your life more difficult then it need be.
John W Higgins
Hi Arup, Am Sonntag, 22. September 2019, 15:30:38 CEST schrieb Arup Rakshit: > When someone adds a craftsmanship to their skill set, the contractor_skills > table holds that relationship. I don’t think distinct is the correct tool, > as it will eliminate the correct data. users and craftsmanship has m:n > relationship via the join table contractor_skills. depending on the definition of table "contractor_skills" it can give you a n:m relationship between user_id and craftmanship_id, that is true. > SELECT > craftsmanships.id, > craftsmanships.name, > CASE WHEN contractor_skills.user_id IS NULL THEN > FALSE > ELSE > TRUE > END AS has > FROM > "craftsmanships" > LEFT JOIN "contractor_skills" ON "contractor_skills"."craftsmanship_id" = > "craftsmanships"."id" LEFT JOIN "users" ON "users"."id" = > "contractor_skills"."user_id" WHERE (contractor_skills.user_id = 8 > OR contractor_skills.user_id IS NULL) > ORDER BY > "craftsmanships"."id”; BUT: you don't use any of users' columns in select, where, or order by. And since users is in a left join it is just a table which is neither used nor relevant in that particular statement. In the end, it depends on how data is structured in your database and what you want to achieve. -- MfG Jan
Just a side note, that the below can be written more simply-
CASE WHEN UserSkills.craftsmanship_id IS NULL THEN FALSE
ELSE TRUE as has
If you want to be direct-
UserSkills.craftsmanship_id IS NOT NULL as has