SQL tables joined with max - sql

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

Related

Why is the SQL full outer join is not presenting unmatched customers (avc_id)?

I appreciate your help in advance!
The right table avc_enr has 108K customers (b.avc_id) in it. In the 2nd table (alias a), we have about 97K customers (a.avc_id).
I tried to use right, left and full outer join but every time the count of customers shows 97K rather than 108K customers (under Total_users)... any idea why with full outer join the count function is not counting all customers even if no common match is found between two tables?
with avc_enr as
(
select
dt, avc_id, service_template_name
from
hive.thor_satellite.v_nms_inventory_nmsdb_avc_service
where
current_status = 'ACTIVE' and dt = 20220809
)
select
a.dt, a.metrics_date,
avg(a.vsat_fl_byte_count_kbps) as AUPU_Kbps,
count(b.avc_id) as Total_users
from
hive.thor_satellite.vda_satellite_nms_performance_smts_avc_pm_throughput a
full outer join
avc_enr b on a.avc_id = b.avc_id and a.dt = b.dt
where
a.dt = 20220809
group by
a.dt, a.metrics_date

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
)

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 sum data from multiple tables

I have 2 tables AP and INV where both have the columns [PROJECT] and [Value].
I want a query to return something like this :
PROJECT | SUM_AP | SUM_INV
I came up with the code below but it's returning the wrong results ( sum is wrong ).
SELECT AP.[PROJECT],
SUM(AP.Value) AS SUM_AP,
SUM(INV.Value) AS SUM_INV
FROM AP INNER JOIN INV ON (AP.[PROJECT] =INV.[PROJECT])
WHERE AP.[PROJECT] = 'XXXXX'
GROUP BY AP.[PROJECT]
The results from your query are wrong because the values you are trying to summarize are being grouped, which causes duplicate values to be included in the SUM.
You could solve it with a couple of sub-selects:
SELECT
AP1.[PROJECT],
(SELECT SUM(AP2.Value) FROM AP AS AP2 WHERE AP2.PROJECT = AP1.PROJECT) AS SUM_AP,
(SELECT SUM(INV2.Value) FROM INV AS INV2 WHERE INV2.PROJECT = AP1.PROJECT) AS SUM_INV
FROM AP AS AP1
INNER JOIN INV AS INV1
ON (AP1.[PROJECT] =INV1.[PROJECT])
WHERE AP1.[PROJECT] = 'XXXXX'
GROUP BY AP1.[PROJECT]
If you have N rows in AP with a given project ID, and M rows in INV with that ID, then the join between the two tables on the project ID will have a total of N*M rows for that project, because the same row in AP will be repeated for every row in INV that has that project ID, and vice versa. Hence why your counts are most likely off (because it's counting the same row in a given table multiple times due to repetition from the join).
Instead, you might want to try doing a join between the results of two subqueries, one which groups the first table by project ID and does that its sum, and the second which groups the other table by project ID and does that sum - then joining once you only have 1 row with sum for each project ID.
If PROJECT is the parent table, you should select FROM the project table, and do a left outer join on the two child tables:
SELECT PROJECT.PROJECT_ID, SUM(AP.Value) AS SUM_AP, SUM(INV.Value) AS SUM_INV
FROM PROJECT
LEFT OUTER JOIN AP ON (AP.[PROJECT] = PROJECT.[PROJECT_ID])
LEFT OUTER JOIN INV ON (INV.[PROJECT] = PROJECT.[PROJECT_ID])
WHERE PROJECT.[PROJECT_ID] = 'XXXXX'
GROUP BY PROJECT.[PROJECT_ID]
You could separate the two sum calculations. One way I can think of is to move the inventory calculation to a subquery, like:
SELECT
AP.[PROJECT]
, SUM(AP.Value) AS SUM_AP
, SummedInv as SUM_INV
FROM AP
LEFT JOIN (
SELECT PROJECT, SUM(Value) AS SUM_INV
FROM INV
GROUP BY PROJECT
) SummedInv ON SummedInv.Project = AP.Project
GROUP BY AP.PROJECT, SummedInv.SUM_INV
Because the SummedInv subquery is grouped on project, it's safe to group on SummedInv.SUM_INV in the outer query as well.
how about this query :
select SUM(gpCutBody.actualQty) as cutQty , SUM(gpSewBody.quantity) as sewQty
from jobOrder
inner join gpCutHead on gpCutHead.joNum = jobOrder.joNum
inner join gpSewHead on gpSewHead.joNum = jobOrder.joNum
inner join gpCutBody on gpCutBody.gpCutID = gpCutHead.gpCutID
inner join gpSewBody on gpSewBody.gpSewID = gpSewHead.gpSewID
where jobOrder.joNum = '36'
here is the link to the ERD: http://dl.dropbox.com/u/18794525/AUG%207%20DUMP%20STAN.png
Try:
SELECT AP.[PROJECT] AS PROJECT, SUM(AP.[Value]) AS SUM_AP, SUM(INV.[Value]) AS SUM_INV
FROM AP, INV
WHERE AP.[PROJECT] = INV.[PROJECT]
AND AP.[PROJECT] = 'XXXXX'
GROUP BY AP.[PROJECT]

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.