SQL Table Joining - sql

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
)

Related

Select the first row of a LEFT JOIN

I'm trying to do a left join. But I only want the first row of the joined table.
When I do :
SELECT DISTINCT
c.reference
FROM contracts as c
output : 7400 rows
But when I try to do the left join I have a lot of duplicates.
I already tried to only get the first row but it does not work. Here is my code :
SELECT DISTINCT
c.reference,
contract_premiums.start_date
FROM contracts as c
LEFT OUTER JOIN contract_premiums ON contract_premiums.contract_id=(
SELECT contract_id FROM contract_premiums
WHERE contract_premiums.contract_id = c.id
ORDER BY contract_premiums.created_at ASC
LIMIT 1)
output : 11500 rows
Note the database in Postgresql and I'm using this request in klipfolio.
If you just want the latest start_date per reference, you can use aggregation:
select c.reference, max(cp.start_date) max_start_date
from contracts c
left join contracts_premiums cp on cp.contract_id = c.id
group by c.reference
This guarantees that you will only get one row per reference.
If you want more columns from contracts_premiums, or if you want to sort by a column other than start_date (possibly, you want created_at instead), then another option is distinct on:
select distinct on (c.reference) c.reference, cp.start_date, cp.created_at
from contracts c
left join contracts_premiums cp on cp.contract_id = c.cid
order by c.reference, cp.created_at desc

SQL inner join duplicates

The query returns duplicates on columns when adding the third table to the query
TABLE A
A.AccId
A.AccNr
A.EntId
TABLE B
B.EntName
B.EntId
TABLE C
C.AccNr
C.CustomerNr
SELECT A.AccID, A.AccNr, A.EntId, B.EntName, B.EntId, C.AccNr,C.CustomerNr, C.EntName
FROM ((Cat.dbo.A
INNER JOIN Cat.dbo.B ON A.EntId = B.EntId)
INNER JOIN Dog.dbo.C ON B.EntName = C.EntName
Your query needs to be updated first. I dont see EntName column in Table C.
There is a possibility of duplicates, if there are many rows matching in Table C , rows from Table A & Table B are repeated while joining with Table C.
If there are many records matching for EntName in C, then you will have duplicates. if you don't want duplicate, you have to apply DISTINCT
SELECT DISTINCT A.AccID, A.AccNr, A.EntId, B.EntName, B.EntId, C.AccNr,C.CustomerNr
FROM Cat.dbo.Accounts
INNER JOIN Cat.dbo.B ON A.EntId = B.EntId
INNER JOIN Dog.dbo.C ON B.EntName = C.EntName
Or if duplicates are due to duplicate records in Table C, you can filter them out before JOIN.
SELECT A.AccID, A.AccNr, A.EntId, B.EntName, B.EntId, C.AccNr,C.CustomerNr
FROM Cat.dbo.Accounts
INNER JOIN Cat.dbo.B ON A.EntId = B.EntId
INNER JOIN (SELECT DISTINCT CustomerNr, EntName FROM Dog.dbo.C) AS C ON B.EntName = C.EntName;

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

SQL Select rows with value that appears x times

SELECT Opponent, JerseyNumber, A
FROM (SELECT * FROM Games_t INNER JOIN GameStats_t ON Games_t.GameID=GameStats_t.GameID)
WHERE A >=1 AND COUNT(Opponent) >3;
I'm trying to return games where there were at least three players who recorded one assist or more. If I don't have AND COUNT(Opponent) >3, the query returns almost what I want, but there are a few games where only three players recorded an assist.
Try this :
SELECT Opponent,
JerseyNumber,
A,
COUNT(Opponent) FROM
(
SELECT *
FROM Games_t INNER JOIN GameStats_t
ON Games_t.GameID=GameStats_t.GameID
)
WHERE A >=1
GROUP BY Opponent, JerseyNumber, A
HAVING COUNT(Opponent) >3
Use the following Query.
SELECT G_TEMP.GAME_ID, GT.OPPONENT, GT.JERSEYNUMBER, G.A FROM
(
SELECT GAME_ID, COUNT(OPPONENT) OPP_COUNT FROM GAMESTATS_T
HAVING COUNT(OPPONENT) > 3
GROUP BY GAME_ID
) G_TEMP
LEFT OUTER JOIN
GAMES_T G
ON
G.GAME_ID = G_TEMP.GAME_ID
AND G.A > 1
INNER JOIN
GAMESTATS_T GT
ON
G.GAME_ID = GT.GAME_ID
Working SQL Fiddle HERE
Note 1: When there are more than one table, it is always better to specify fields using tablename_alias.field_name syntax. This is a good practice, however it is optional.
For example, if Table TABLEA has fields FIELDA1, FIELDA2, FIELDA3 and if Table TABLEB has fields FIELDB1, FIELDB2
Then you can use query as:
SELECT A.FIELDA1, A.FIELDA3, B.FIELDB2
FROM TABLEA A JOIN TABLEB B ON A.FIELDA2 = B.FIELDB2
Use HAVING part in query to use some parameters after query completion:
SELECT Opponent, JerseyNumber, A
FROM (SELECT * FROM Games_t INNER JOIN GameStats_t ON Games_t.GameID = GameStats_t.GameID)
WHERE A >=1 HAVING COUNT(Opponent) > 3;

SQL Join question

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.