SQL Join question - sql

I have 3 tables
Links
Link ID
Link Name
GroupID (FK into Groups)
SubGroupID (FK into Subgroups)
Groups
GroupID
GroupName
SubGroup
SubGroupID
SubGroupName
GroupID (FK into Groups)
Every link needs to have a GroupID but teh SubGroupID is optional. How do i write a SQL query to show:
Links.LinkName, Groups.GroupName, SubGroup.SubGroupName
For the records with no subgroup just put a blank entry in that field. If i have 250 link rows, i should get back 250 reecords from this query.
Is there a way to do this in one query or do i need to do multiple queries?

This assumes that there is at most only 1 subgroup per group. if there are more, then you have the potential to get additional records.
select links.linkname, groups.groupname, subgroup.subgroupname
from links
inner join groups on (links.groupid = groups.groupid)
left outer join subgroup on (links.subgroupid = subgroup.subgroupid)

SELECT Links.LinkName, Groups.GroupName, SubGroup.SubGroupName -- Will potentially be NULL
FROM Links
INNER JOIN Groups
ON Group.GroupID = Links.GroupID
LEFT JOIN SubGroup
ON SubGroup.SubGroupID = Links.SubGroupID

You would use an Outer Join:
select Links.LinkName, Groups.GroupName, SubGroup.SubGroupName
from Links
inner join Groups on Groups.GroupID = Links.GroupID
left outer join SubGroup on Links.SubGroupID = SubGroup.SubGroupID

SELECT
links.linkname
, groups.groupname
, subgroup.groupname
FROM
links
JOIN groups ON links.groupid = groups.groupid
LEFT OUTER JOIN subgroups ON links.subgroupid = subgroup.subgroupid
(re-added to address OP's question)
incidentally, why not keep groups and subgroups in the same table, and use a self-referential join?
Akantro:
You'd have something like this:
create table groups(
groupid integer primary key,
parentgroupid integer foreign key references groups (groupid),
groupname varchar(50))
your query would then be
SELECT
links.linkname
, groups.groupname
, SUBGROUPS.groupname
FROM
links
JOIN groups ON links.groupid = groups.groupid
LEFT OUTER JOIN groups SUBGROUPS ON links.subgroupid = subgroup.groupid
there's no functional difference to keeping the tables like this, but the benefit is you only have to go to one place to edit the groups/subgroups

You're not too clear, but I think you want to get all rows including those that don't have a correspondent in the SubGroup table.
For this you can use LEFT JOIN, it will fetch NULLs if there are no matching rows.

SELECT LinkName, GroupName, SubGroupNamne
FROM Links INNER JOIN Groups ON LInks.GroupID = Groups.GroupID
LEFT JOIN SubGroup ON Links.SubGroupID = SubGroup.SubGroupID
This will include rows that do not have a sub group. That column will simply be NULL.

select L1.LinkName, G1.GroupName, NVL(S1.SubGroupName,' ')
from Links L1, Groups G1, SubGroup S1
where L1.GroupID = G1.GroupID and
L1.GroupID = S1.GroupID

Okay, try:
select a.linkname, b.groupname, c.subgroupname
from links a, groups b, subgroup c
where a.groupid = b.groupid
and a.subgroupid = c.subgroupid
and a.subgroupid is not null
union all
select a.linkname, b.groupname, ' '
from links a, groups b
where a.groupid = b.groupid
and a.subgroupid is null
I think that should work (it does in DB2 which is the DBMS I use most) - you'll need to adjust the spaces in the second select to match the subgroup.subgroupname size.

Use a LEFT OUTER JOIN on the SubGroup table will give you all rows from the Links table and where a SubGroup exists will return that otherwise you see a NULL value.
SELECT L.LinkName, G.GroupName, S.SubGroupName
FROM Links As L
INNER JOIN Groups As G ON L.GroupID=G.GroupID
LEFT OUTER JOIN SubGroup S ON L.SubGroupID=S.SubGroupID
This does not check that your SubGroups.LinkID matches the Links.LinkID which should never happen but if you need to check this then add in another clause to the join:
SELECT L.LinkName, G.GroupName, S.SubGroupName
FROM Links As L
INNER JOIN Groups As G ON L.GroupID=G.GroupID
LEFT OUTER JOIN SubGroup S ON L.SubGroupID=S.SubGroupID AND L.GroupID=S.GroupID

Just use a LEFT OUTER JOIN on the SubGroup table like:
select
l.LinkName,
g.GroupName,
s.SubGroupName
from
Links l
'
JOIN Group g
on ( g.GroupId = l.GroupId)
'
LEFT OUTER JOIN SubGroup s
on ( s.SubGroupId = l.SubGroupId )
That should do it.

Related

SQL Table Joining

I'm joining these three tables, but the same information gets displayed 3 times ... Any idea how to have only the unique rows to be displayed, as determined by unique shipment id's?
SELECT S.SHIPMENT_ID, S.CREATION_DATE, S.BUSINESS_ID, B.BUS_ID, S.SHIPMENT_STATUS, S.BUSINESS_NAME, S.SHIPMENT_MODES, S.CUSTOMER_NAME
FROM "SHIPMENT" S
INNER JOIN "BUSINESS" B ON S.BUSINESS_ID=B.BUS_ID
INNER JOIN "SHIPMENT_GROUP" SG ON S.SHIPMENT_ID=SG.SHIPMENT_ID
INNER JOIN "DATA_GROUP" DG ON DG.ID=SG.GROUP_ID
try select distinct
SELECT DISTINCT column1, column2, ...
FROM table_name;
w3schools
You are selecting rows from the first table only, so this suggests that you are using the joins for filtering.
If so, you can rewrite this with exists, which will avoid duplicates if there are multiple matches. Starting from your existing query, the logic would be:
select s.*
from shipment s
where
exists (
select 1
from business b
where b.bus_id = s.business_id
) and exists (
select 1
from shipment_group sg
inner join data_group dg on dg.id = sg.group_id
where sg.shipment_id = s.shipment_id
)

Get all the values from the first left table but when two left joins used its restricting the values from first table

I am trying to get all the values from the first left table but when I use two left joins its restricting the values from first table.
I used the below query
SELECT P.person_id, TS.Task_Id, TS.skill
FROM Person P
LEFT JOIN Person_Skill PS ON P.person_id = PS.person_id
LEFT JOIN Task_Skill TS ON PS.Skill = TS.Skill
WHERE ts.task_id = 245
I need all the person id from person table.
Just move the condition on the left joined table from the where clause to the on clause of the join:
select p.person_id, ts.task_id, ts.skill
from person p
left join person_skill ps
on p.person_id = ps.person_id
left join task_skill ts
on ps.skill = ts.skill
and ts.task_id = 245 --> here
Rationale: conditions in the where clause are mandatory. If there is no match in ts, then condition ts.task_id = 245 cannot be satisfied, since ts.task_id is null.
Use the filter condition in a sub query instead of using it as a global filter outside. This should give you the output that you desire.
SELECT P.person_id,TS.Task_Id,TS.skill FROM Person P
LEFT JOIN Person_Skill PS
ON P.person_id=PS.person_id
LEFT JOIN
(Select * from Task_Skill where task_id = 245) TS
ON PS.Skill=TS.Skill;

SQL tables joined with max

I'm working with two tables. I have a full list of groups in table A, and a list of each group member that has been reviewed in table B. So table B is a log of all review records for those members for each group.
select a.Group_Name, Max(b.Request_Review_Date)
From GroupTable a
Left Outer Join GroupReviews b ON a.Group_Name = b.Group_Name
Group By a.Group_Name
What I am trying to return is the full list of groups from table A, and find the latest review date from table B for each of those groups.
I have researched and tried all or most of the inner & outer joins, apply methods....but its just not giving me the results. Can anyone point me in the right direction? Or am I having to bring back two result sets and compare in my ASP code-behind?
Try a CTE then join back to it
WITH Recent AS
(
select group_name, max(Request_Review_Date) AS 'MaxReviewDate'
from GroupReviews
group by group_name
)
select a.group_name, MaxReviewDate
from GroupTable a left join Recent
on group_name = a.group_name
if you need the value for max for all the a.group name rows the ypu should join the subquery for max date
select a.Group_Name, t.max_date
left join (
select b.Group_Name, Max(b.Request_Review_Date) max_date
from GroupReviews b
Group By b.Group_Name
) t on t.Group_Name = a.Group_Name

Left outer join with count, on 3 tables not returning all rows from left table

I have these 3 tables:
Areas - id, name
Persons - id, area_id
Special_Persons - id_person, date
I'd like to produce a list of all Areas, followed by a count of Special Persons in each area, including Areas with no Special Persons.
If I do a left join of Areas and Persons, like this:
select a.id as idArea, count(p.id) as count
from areas a
left join persons p on p.area_id = a.id
group by a.id;
This works just fine; Areas that have no Persons show up, and have a count of 0.
What I am not clear on is how to do the same thing with the special_persons table, which currently only has 2 entries, both in the same Area.
I have tried the following:
select a.id as idArea, count(sp.id_person) as count
from special_persons sp, areas a
left join persons p on p.area_id = a.id
where p.area_id = a.id
and sp.id_person = p.id
group by a.id;
And it only returns 1 row, with the Area that happens to have 2 Special Persons in it, and a count of 2.
To continue getting a list of all areas, do I need to use a sub-query? Another join? I'm not sure how to go about it.
You can add another left join to the Special_Persons table:
select a.id as idArea, count(p.id), count(sp.id_person)
from areas a
left join persons p on p.area_id = a.id
left join special_persons sp on sp.id_person = p.id
group by a.id;

Unable to Group on MSAccess SQL multiple search query

please can you help me before I go out of my mind. I've spent a while on this now and resorted to asking you helpful wonderful people. I have a search query:
SELECT Groups.GroupID,
Groups.GroupName,
( SELECT Sum(SiteRates.SiteMonthlySalesValue)
FROM SiteRates
WHERE InvoiceSites.SiteID = SiteRates.SiteID
) AS SumOfSiteRates,
( SELECT Count(InvoiceSites.SiteID)
FROM InvoiceSites
WHERE SiteRates.SiteID = InvoiceSites.SiteID
) AS CountOfSites
FROM (InvoiceSites
INNER JOIN (Groups
INNER JOIN SitesAndGroups
ON Groups.GroupID = SitesAndGroups.GroupID
) ON InvoiceSites.SiteID = SitesAndGroups.SiteID)
INNER JOIN SiteRates
ON InvoiceSites.SiteID = SiteRates.SiteID
GROUP BY Groups.GroupID
With the following table relationship
http://m-ls.co.uk/ExtFiles/SQL-Relationship.jpg
Without the GROUP BY entry I can get a list of the entries I want but it drills the results down by SiteID where instead I want to GROUP BY the GroupID. I know this is possible but lack the expertise to complete this.
Any help would be massively appreciated.
I think all you need to do is add groups.Name to the GROUP BY clause, however I would adopt for a slightly different approach and try to avoid the subqueries if possible. Since you have already joined to all the required tables you can just use normal aggregate functions:
SELECT Groups.GroupID,
Groups.GroupName,
SUM(SiteRates.SiteMonthlySalesValue) AS SumOfSiteRates,
COUNT(InvoiceSites.SiteID) AS CountOfSites
FROM (InvoiceSites
INNER JOIN (Groups
INNER JOIN SitesAndGroups
ON Groups.GroupID = SitesAndGroups.GroupID
) ON InvoiceSites.SiteID = SitesAndGroups.SiteID)
INNER JOIN SiteRates
ON InvoiceSites.SiteID = SiteRates.SiteID
GROUP BY Groups.GroupID, Groups.GroupName;
I think what you are looking for is something like the following:
SELECT Groups.GroupID, Groups.GroupName, SumResults.SiteID, SumResults.SumOfSiteRates, SumResults.CountOfSites
FROM Groups INNER JOIN
(
SELECT SitesAndGroups.SiteID, Sum(SiteRates.SiteMonthlySalesValue) AS SumOfSiteRates, Count(InvoiceSites.SiteID) AS CountOfSites
FROM SitesAndGroups INNER JOIN (InvoiceSites INNER JOIN SiteRates ON InvoiceSites.SiteID = SiteRates.SiteID) ON SitesAndGroups.SiteID = InvoiceSites.SiteID
GROUP BY SitesAndGroups.SiteID
) AS SumResults ON Groups.SiteID = SumResults.SiteID
This query will group your information based on the SiteID like you want. That query is referenced in the from statement linking to the Groups table to pull the group information that you want.