2
\$\begingroup\$

I've the following tables

Candidates
Id  Name  PicId
 1  Tom   NULL
 2  James 1
 3  Bob   NULL
 4  Manny 2

Attachments
Id  Name
 1  One.jpg
 2  Two.doc
 3  Three.rtf

The join table AttachmentCandidate
CandidateId  AttachmentId
    3             1
    3             2
    4             3

So from the above tables it shows that a candidate's 'PicId' is nullable & one candidate can have 0 or many attachments.

Now the query what I wish to create is the one which should return me Candidate's Name, PicId, & Attachment Name, but I don't want the candidates who neither have a PicId nor any Attachments.

I was able to achieve it with a subquery. Is there any better way it can be done??

SELECT r.cname,r.picId,r.attName FROM 
(SELECT Candidates.Name as cname,
Candidates.PicId as picId, 
Attachments.Name as attName
FROM Candidates LEFT JOIN AttachmentCandidate
ON Candidates.Id = AttachmentCandidate.CandidateId
LEFT JOIN Attachments
ON AttachmentCandidate.AttachmentId = Attachments.Id) as r
WHERE (r.picId IS NOT NULL OR r.attName IS NOT NULL)

Result
cname   picId  attName
James   1      NULL
Bob     NULL   One.jpg
Bob     NULL   Two.doc
Manny   3      Three.rtf

And the candidate 'Tom' not is present in the result since he doesn't have a picId nor any attachments

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

My only real comment to make is that the subquery is irrelevant. You can achieve the same result without it.


When specifying your column aliases in the outer query. There is no need for you to use AS picid as the column is already called picid.


You could also use table aliases as well to shorten some of the statements. I personally prefer to see aliases than fully qualified table names. Assuming everyone sticks to a naming convention of course. My normal rule of thumb is that each capital letter of the table becomes the alias.


Also, as we can remove the subquery, we can change the WHERE clause so it is no longer filtering out the attachment name but instead the PK (WHERE a.Id IS NOT NULL).


For the columns being returned, can we make them more explicit? i.e. CandidateName instead of CName, etc.

Here is the modified version of your query using the changes above:

SELECT
     c.Name     AS CandidateName
    ,c.picid    AS PicId
    ,a.Name     AS AttachmentName
FROM Candidates c
LEFT JOIN AttachmentCandidate ac ON c.Id = ac.CandidateId
LEFT JOIN Attachments a ON ac.AttachmentId = a.Id
WHERE c.picid IS NOT NULL
OR a.Id IS NOT NULL

I'm not sure whether the formatting is just bad when you created your question or whether your SQL query is like that? If so, it's badly formatted and difficult to read. I try and break up each part of the query into separate segments so it's easy to read and maintain by others:

SELECT
     r.cname
    ,r.PicId
    ,r.attName
FROM
(
    SELECT
        Candidates.Name     AS cname
        ,Candidates.PicId   AS picid
        ,Attachments.Name   AS attname
    FROM Candidates
    LEFT JOIN AttachmentCandidate ON Candidates.Id = AttachmentCandidate.CandidateId
    LEFT JOIN Attachments ON AttachmentCandidate.AttachmentId = Attachments.Id
) AS r
WHERE (r.PicId IS NOT NULL OR r.attName IS NOT NULL)

As you requested an alternative, i'll give you one. Essentially it's pretty much the reverse of what you're doing. I'm gathering all of the candidates with attachments first and then I RIGHT JOIN back onto the candidates table and filter out the ones without a PicId.

SELECT
    c.Name      AS CandidateName
    ,c.PicId    AS PicId
    ,a.Name     AS AttachmentName
FROM AttachmentCandidate ac
INNER JOIN Attachments a ON a.Id = ac.AttachmentId
RIGHT JOIN Candidates c ON c.Id = ac.CandidateId
WHERE c.picid IS NOT NULL
OR a.Id IS NOT NULL
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.