SQL left joins returning weird IDs - sql

I am running this SQL query in a SQL Server database,
SELECT staff_list.id AS staff_id,
staff_list.status,
core_teams.workbase,
core_teams.service_user,
core_teams.staff_name,
core_teams.role,
service_users.id AS su_id
FROM staff_list
LEFT JOIN core_teams
ON staff_list.workbase = core_teams.workbase
LEFT JOIN service_users
ON service_users.name = core_teams.service_user
WHERE staff_list.status <> 'Left'
AND staff_list.status <> 'Name Changed';
The join and results are being returned, however the attribute staff_id is being repeated multiple times over many rows for users that have relevance to the staff_id. Am I doing something incorrectly?

You may be missing a link between staff_list and core_teams.
SELECT staff_list.id AS staff_id, staff_list.status,
core_teams.workbase, core_teams.service_user, core_teams.staff_name,
core_teams.role, service_users.id AS su_id
FROM staff_list
LEFT JOIN core_teams
ON staff_list.workbase = core_teams.workbase
--missing link
AND staff_list.name = core_teams.staff_name
LEFT JOIN service_users
ON service_users.name = core_teams.service_user
WHERE staff_list.status <> 'Left'
AND staff_list.status <> 'Name Changed';

Related

SQL join with null values not returning all rows

I've got a query with two joins. One is a standard inner join, and the other is used to translate a country code to the full country name.
Query:
SELECT Company.Comp_CompanyId, Company.comp_bigchange_contactid, Company.Comp_Type, Company.Comp_Name, Address.Addr_Address1, Address.Addr_Address2, Address.Addr_Address3, Address.Addr_Address4, Address.Addr_City, Address.Addr_PostCode, Custom_Captions.Capt_UK AS Addr_Country, Company.Comp_UpdatedDate
FROM Company
INNER JOIN Address ON Company.Comp_PrimaryAddressId = Address.Addr_AddressId
INNER JOIN Custom_Captions ON Address.Addr_Country = Custom_Captions.Capt_Code
WHERE (Custom_Captions.Capt_Family = 'addr_country') AND Company.comp_bigchange_sync = 'Y'
ORDER BY Company.Comp_UpdatedDate ASC
As an example Address.Addr_Country could be 'GB', and the Custom_Captions table would have a row to translate that into 'United Kingdom' (Custom_Captions.Capt_UK)
This works fine except for when the Address.Addr_Country field is Null (not selected). When that field is Null, the whole row is not returned.
Can anyone suggest a way to make this show even when the Addr_Country field is Null? It would just need to return a null value for the country.
Make the join a LEFT OUTER JOIN and move the WHERE clause associated with it to the join - simpler than making the where clause also check for null there perhaps. Related question on this type of a change: SQL JOIN Condition moved to with where clause produces differences
SELECT Company.Comp_CompanyId, Company.comp_bigchange_contactid, Company.Comp_Type, Company.Comp_Name, Address.Addr_Address1, Address.Addr_Address2, Address.Addr_Address3, Address.Addr_Address4, Address.Addr_City, Address.Addr_PostCode, Custom_Captions.Capt_UK AS Addr_Country, Company.Comp_UpdatedDate
FROM Company
INNER JOIN Address ON Company.Comp_PrimaryAddressId = Address.Addr_AddressId
LEFT OUTER JOIN Custom_Captions ON Address.Addr_Country = Custom_Captions.Capt_Code
AND Custom_Captions.Capt_Family = 'addr_country'
WHERE Company.comp_bigchange_sync = 'Y'
ORDER BY Company.Comp_UpdatedDate ASC
You would need to use a LEFT or RIGHT JOIN on your query but since you're filtering on that row in the WHERE, it would be best to add that filter as a subquery:
SELECT Company.Comp_CompanyId, Company.comp_bigchange_contactid,
Company.Comp_Type, Company.Comp_Name, Address.Addr_Address1,
Address.Addr_Address2, Address.Addr_Address3, Address.Addr_Address4,
Address.Addr_City, Address.Addr_PostCode, Custom_Captions.Capt_UK AS
Addr_Country, Company.Comp_UpdatedDate
FROM Company
INNER JOIN Address ON Company.Comp_PrimaryAddressId =
Address.Addr_AddressId
LEFT JOIN (SELECT * FROM Custom_Captions WHERE
Custom_Captions.Capt_Family = 'addr_country') Custom_Captions ON
Address.Addr_Country = Custom_Captions.Capt_Code
WHERE Company.comp_bigchange_sync = 'Y'
ORDER BY Company.Comp_UpdatedDate ASC

Rows which appeared only once in query results

I want to have rows which appeared in 25th of February and did not appear in 6th of March. I've tried to do such query:
SELECT favfd.day, dv.title, array_to_string(array_agg(distinct "host_name"), ',') AS affected_hosts , htmltoText(dsol.fix),
count(dv.title) FROM fact_asset_vulnerability_finding_date favfd
INNER JOIN dim_vulnerability dv USING(vulnerability_id)
INNER JOIN dim_asset USING(asset_id)
INNER JOIN dim_vulnerability_solution USING(vulnerability_id)
INNER JOIN dim_solution dsol USING(solution_id)
INNER JOIN dim_solution_highest_supercedence dshs USING (solution_id)
WHERE (favfd.day='2018-02-25' OR favfd.day='2018-03-06') AND
dsol.solution_type='PATCH' AND dshs.solution_id=dshs.superceding_solution_id
GROUP BY favfd.day, dv.title, host_name, dsol.fix
ORDER BY favfd.day, dv.title
which gave me following results:
results
I've read that I need to add something like "HAVING COUNT(*)=1" but like you can see in query results my count columns looks quite weird. Here is my results with that line added:
results with having
Can you advice me what I am doing wrong?
One way is to use a HAVING clause to assert your date criteria:
SELECT
dv.title,
array_to_string(array_agg(distinct "host_name"), ',') AS affected_hosts,
htmltoText(dsol.fix),
count(dv.title)
FROM fact_asset_vulnerability_finding_date favfd
INNER JOIN dim_vulnerability dv USING(vulnerability_id)
INNER JOIN dim_asset USING(asset_id)
INNER JOIN dim_vulnerability_solution USING(vulnerability_id)
INNER JOIN dim_solution dsol USING(solution_id)
INNER JOIN dim_solution_highest_supercedence dshs USING (solution_id)
WHERE
dsol.solution_type = 'PATCH' AND
dshs.solution_id = dshs.superceding_solution_id
GROUP BY dv.title, dsol.fix
HAVING
SUM(CASE WHEN favfd.day = '2018-02-25' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN favfd.day = '2018-03-06' THEN 1 ELSE 0 END) = 0
ORDER BY favfd.day, dv.title
The only major changes I made were to remove host_name from the GROUP BY clause, because you use this column in aggregate, in the select clause. And I added the HAVING clause to check your logic.
The change you should make is to replace your implicit join syntax with explicit joins. Putting join criteria into the WHERE clause is considered bad practice nowadays.

SQL using table from join in subqueries

I am writing a query to be used as databases view, it looks now like this:
SELECT
contact.*,
contact_users.names AS user_names,
contact_status.status_id AS status_id,
status_translation.name AS status_name,
status_translation.lang_id AS lang_id
FROM contacts as contact
LEFT JOIN contact_status AS contact_status ON contact_status.status_id = contact.status
LEFT JOIN contact_status_translation AS status_translation ON status_translation.id = contact.status
LEFT JOIN (
SELECT
contacts_users.contact_id,
string_agg(users.fullname || ', ' || users.id, ' | ') as names
FROM v_contacts_users as contacts_users
LEFT JOIN v_users as users on users.id = contacts_users.user_id
WHERE users.lang_id = status_translation.lang_id
GROUP BY contacts_users.contact_id
) AS contact_users ON contact_users.contact_id = contact.id
WHERE contact.deleted = FALSE
Everything works as expected except the WHERE condition in the last LEFT JOIN - the WHERE users.lang_id = status_translation.lang_id part says that status_translation cannot be referenced in this part of the query? Why is that? I tried to reference this table with various always but the result is still the same.Thing is that v_users is translated as well so I need to have only one result from this table.
Insert LATERAL between LEFT JOIN and the opening parenthesis if you want to reference previous FROM list entries.

SQL select results not appearing if a value is null

I am building a complex select statement, and when one of my values (pcf_auto_key) is null it will not disipaly any values for that header entry.
select c.company_name, h.prj_number, h.description, s.status_code, h.header_notes, h.cm_udf_001, h.cm_udf_002, h.cm_udf_008, l.classification_code
from project_header h, companies c, project_status s, project_classification l
where exists
(select company_name from companies where h.cmp_auto_key = c.cmp_auto_key)
and exists
(select status_code from project_status s where s.pjs_auto_key = h.pjs_auto_key)
and exists
(select classification_code from project_classification where h.pcf_auto_key = l.pcf_auto_key)
and pjm_auto_key = 11
--and pjt_auto_key = 10
and c.cmp_auto_key = h.cmp_auto_key
and h.pjs_auto_key = s.pjs_auto_key
and l.pcf_auto_key = h.pcf_auto_key
and s.status_type = 'O'
How does my select statement look? Is this an appropriate way of pulling info from other tables?
This is an oracle database, and I am using SQL Developer.
Assuming you want to show all the data that you can find but display the classification as blank when there is no match in that table, you can use a left outer join; which is much clearer with explicit join syntax:
select c.company_name, h.prj_number, h.description, s.status_code, h.header_notes,
h.cm_udf_001, h.cm_udf_002, h.cm_udf_008, l.classification_code
from project_header h
join companies c on c.cmp_auto_key = h.cmp_auto_key
join project_status s on s.pjs_auto_key = h.pjs_auto_key
left join project_classification l on l.pcf_auto_key = h.pcf_auto_key
where pjm_auto_key = 11
and s.status_type = 'O'
I've taken out the exists conditions as they just seem to be replicating the join conditions.
If you might not have matching data in any of the other tables you can make the other inner joins into outer joins in the same way, but be aware that if you outer join to project_status you will need to move the statatus_type check into the join condition as well, or Oracle will convert that back into an inner join.
Read more about the different kinds of joins.

Why is this SQL query returning this result?

Can anyone tell me why this sql query is returning this result.
SELECT
M.MatterNumber_Id, M.MatterNumber, M.MatterName,
ISNULL(MP.Role_Cd, 'No Primary') AS PrimaryRole,
ISNULL(E.Name, 'No Primary') AS PrimaryName,
ISNULL(C.CommNumber, 'l.w#abc.ca;m.j#abc.com') AS Email
FROM
Matter M
LEFT OUTER JOIN
MatterPlayer MP ON M.MatterNumber_Id = MP.MatterNumber_Id AND
MP.Role_Cd IN ('Primary Lawyer', 'Primary Staff Member') AND
MP.EndDate IS NULL
LEFT OUTER JOIN
Entity E on MP.Entity_EID = E.Entity_EID
LEFT OUTER JOIN Communication C on MP.Entity_EID = C.Entity_EID AND
C.CommunicationType_Cd = 'Email'
LEFT OUTER JOIN
MatterExposure ME on M.MatterNumber_Id = ME.MatterNumber_Id AND
ME.AssessedDate > '7/10/2014' AND
ME.Currency_CD IS NOT NULL
WHERE
M.MatterStatus_Cd = 'Active' AND
ME.AssessedDate IS NULL AND
M.Matter_Cd in
(SELECT
rpl.Type_CD
FROM
RuleProfile_Tabs rpt
INNER JOIN
RuleProfile_LookupCode rpl ON rpt.RuleProfile_ID = rpl.RuleProfile_ID
WHERE
tab_id = 1034 AND Caption LIKE 'Reportable Matter%')
ORDER BY
Email, M.MatterName
But When i see the results I check one of the records returned and it should not have been returned.
One of the results returned:
Bailey, Richard
In the db i check the values in the tables it should not have been returned because Currency_CD is null and in the sql it states currency_cd is not null. Also the assessed date is AFTER 7/10/2014
Table Values:
MatterStatus_CD:
Active
AssessedDate:
7/24/2014
Currency_CD:
NULL
EndDate:
NULL
Where clauses are used for filtering, not the join clause. Every JOIN should have an ON. Here's a head start.
Select M.MatterNumber_Id, M.MatterNumber, M.MatterName,
IsNull(MP.Role_Cd, 'No Primary') as PrimaryRole,
IsNull(E.Name, 'No Primary') as PrimaryName,
IsNull(C.CommNumber, 'l.w#abc.ca;m.j#abc.com') as Email
From Matter M
Left Outer Join MatterPlayer MP on M.MatterNumber_Id = MP.MatterNumber_Id
Left Outer Join Entity E on MP.Entity_EID = E.Entity_EID
Left Outer Join Communication C on MP.Entity_EID = C.Entity_EID
Left Outer Join MatterExposure ME on M.MatterNumber_Id = ME.MatterNumber_Id
Where M.MatterStatus_Cd = 'Active'
AND ME.AssessedDate is Null
and M.Matter_Cd in (select rpl.Type_CD
from RuleProfile_Tabs rpt
inner join RuleProfile_LookupCode rpl on rpt.RuleProfile_ID = rpl.RuleProfile_ID
where tab_id = 1034 AND Caption like 'Reportable Matter%')
and MP.Role_Cd In ('Primary Lawyer', 'Primary Staff Member')
and MP.EndDate is Null
and ME.AssessedDate > '7/10/2014'
and ME.Currency_CD IS NOT NULL
and C.CommunicationType_Cd = 'Email'
Order by Email, M.MatterName
You are LEFT JOINing MatterExposure onto Matter. Your Me.Currency_CD IS NOT NULL criteria in the LEFT JOIN's ON statement is only saying, "don't join to records where Currency_CD is null..." but because this is a left join, none of your Matter table's records are lost in the join. So when you ask for Currency_CD you will get NULL.
To remove records from your query where Currency_CD is null in the MatterExposure table you will either need to specify Currency_CD IS NOT NULL in your where statement (instead of your join) or will need to use an INNER JOIN to your MatterExposure table.
It is because this join is a LEFT OUTER JOIN. If you want to enforce this join make it an inner join. Or move that check to the WHERE clause.