Skipping an item from being checked in where clause - sql

In the code below, in the WHERE clause, all the items are checked against that condition "bcp.value = cm.id". Hence if item has no value in bcp table it wont show up, how do i make it show up?
I have tried my best using the likes of sub query without success.
Please help
SELECT DISTINCT
u.firstname,
u.lastname,
u.id,
c.shortname,
c.fullname,
cs.name AS 'Units Code/s Covered',
m.name,
MIN(FROM_UNIXTIME(cmc.timemodified)) AS 'Start Time',
MAX(FROM_UNIXTIME(bgi.dateissued))AS 'Time Completed'
FROM
mdl_course_modules_completion cmc
LEFT JOIN mdl_course_modules cm
ON (cmc.coursemoduleid =cm.id)
LEFT JOIN mdl_course_sections cs
ON (cs.id = cm .section)
LEFT JOIN mdl_modules m
ON (m.id= cm.module )
LEFT JOIN mdl_course c
ON c.id = cm.course AND c.shortname = 'DOM_2015_1'
LEFT JOIN mdl_user u
ON u.id = cmc.userid AND (u.firstname = 'bambo' OR u.firstname = 'Test bambo')
LEFT JOIN mdl_badge_issued bgi
ON bgi.userid = cmc.userid
LEFT JOIN mdl_badge bg
ON bg.id = bgi.badgeid
LEFT JOIN mdl_badge_criteria bc
ON bc.badgeid = bg.id
LEFT JOIN mdl_badge_criteria_param bcp
ON bcp.critid = bc.id AND bcp.value = cm.id
GROUP BY u.firstname, cs.name, FROM_UNIXTIME(bgi.dateissued)

Try this:
AND (bcp.value is null or bcp.value = cm.id)

Just add OR Inside your Where Clause like this
WHERE c.shortname = 'DOM_2015_1' AND (bcp.value = cm.id OR bcp.value IS NULL)
or if it string bcp.value = ""
Update
Before to adding this in Where clause you have to get correct Join maybe it will be better use FULL OUTER JOIN or RIGHT JOIN(Is it possible? It depends on your logic) instead of using LEFT JOIN

WHERE conditions are being applied to entire resultset, thus discarding any of those rows from table mdl_course_modules_completion that don't meet this criteria.
If you only need to check for NULL value that could come from mdl_badge_criteria_param bcp table, then adding additional (or bcp.value is null) would suffice.
This is because your mdl_course_modules cm table is being joined to mdl_badge_criteria_param bcp, and not the main table. If we had the case of joining directly with the main table, making bcp independent from cm, query could sometimes yield wrong results, when cm.id would be holding NULL, and bcp.value was a non-null value.
Keeping that in mind, your solution would be
WHERE c.shortname = 'DOM_2015_1' AND ( bcp.value = cm.id OR bcp.value is null )
Edit You may also consider dropping the entire condition considering bcp.value = cm.id since it seems like you want to allow every record, that has a matching cm.id with the main table to appear and instead applying a cm.id is not null to your where condition.

Related

Trying to get the right SQL Query (Joins)

I've been trying to join a couple tables to get the right result.
The problem is its returning some NULL values where this shouldn't be the case.
EP.ProductId, CreateDate, IsproductActivated and Subject are returning a Null Value.I've noticed it That DossierID and dossier will return a value when I make a right join as stated below.
Could anyone help me with my case?
SELECT
EP.ProductId AS [ProductId]
,MAX(EP.CreateDate) AS [CreateDate]
,EP.IsProductActivated AS [IsProductActivated]
,PC.EntityId AS [DossierID]
,PC.EntityDiscriminator AS [EntityDiscriminator]
,PDCP.Name AS [Subject]
,D.Name AS [Dossier]
FROM
[tblEntityProduct] AS EP
LEFT JOIN .tblPDCProduct AS PDCP
ON PDCP.id = EP.Productid
LEFT JOIN
[tblProductContainerContent] AS PCC
ON PCC.EntityProductId = EP.ProductId
RIGHT JOIN
[tblProductContainer] AS PC
ON PC.Id = PCC.Productcontainerid
LEFT JOIN
[tblDossier] AS D
ON D.Id = PC.Entityid
WHERE PC.EntityId = 2803
GROUP BY EP.Productid
,IsProductActivated
,EntityId
,EntityDiscriminator
,PDCP.name
,D.name
The picture below shows the result of the above query. I want the values it suppose to have instead of the NULL
use inner join to avoid getting null values, because The LEFT JOIN keyword returns all records from the left table (table1), and the matched records from the right table (table2). The result is NULL from the right side, if there is no match. for right join opposite is true of left join
SELECT
EP.ProductId AS [ProductId]
,MAX(EP.CreateDate) AS [CreateDate]
,EP.IsProductActivated AS [IsProductActivated]
,PC.EntityId AS [DossierID]
,PC.EntityDiscriminator AS [EntityDiscriminator]
,PDCP.Name AS [Subject]
,D.Name AS [Dossier]
FROM
[tblEntityProduct] AS EP
JOIN .tblPDCProduct AS PDCP
ON PDCP.id = EP.Productid
JOIN
[tblProductContainerContent] AS PCC
ON PCC.EntityProductId = EP.ProductId
JOIN
[tblProductContainer] AS PC
ON PC.Id = PCC.Productcontainerid
JOIN
[tblDossier] AS D
ON D.Id = PC.Entityid
WHERE PC.EntityId = 2803
GROUP BY EP.Productid
,IsProductActivated
,EntityId
,EntityDiscriminator
,PDCP.name
,D.name
I hate right-joins. They are basically a REQUIRE THIS TABLE regardless of the others leading up to it. Instead, I reversed the query to put the REQUIRED Table in the first position, then LEFT-JOIN to the underlying. Now, based on the structure / join, I would THINK that you start with a product container -- will always exist. Each product container has stuff in it (ProductContainerContent). Then, each content item IS an entity product to get its description. Each container has its own Dossier which may (or not) be assigned at the time the container is being prepared. If I am accurate, most of the LEFT-JOINs should just be standard (inner) JOINs, but lets see what this does against your data.
SELECT
EP.ProductId,
MAX(EP.CreateDate) CreateDate,
EP.IsProductActivated,
PC.EntityId DossierID,
PC.EntityDiscriminator,
PDCP.Name Subject,
D.Name Dossier
FROM
tblProductContainer PC
LEFT JOIN tblProductContainerContent PCC
ON PC.Id = PCC.Productcontainerid
LEFT JOIN tblEntityProduct EP
ON PCC.EntityProductId = EP.ProductId
LEFT JOIN tblPDCProduct PDCP
ON EP.Productid = PDCP.id
LEFT JOIN tblDossier D
ON PC.Entityid = D.Id
WHERE
PC.EntityId = 2803
GROUP BY
EP.Productid,
IsProductActivated,
EntityId,
EntityDiscriminator,
PDCP.name,
D.name

Including 0 in count does not work with extra where clause

In my current project, I have user groups with facilities assigned to them. What I need is to show the user group's name, it's department, it's description and number of facilities assigned to it. I somewhat have been able to achieve this as shown below:
Current "successful" result
The problem that I am facing now is when I add a where clause stating that facilities that have been deleted should NOT be included. Below is my SQL statement that produces the result above:
Statement to achieve result above
SELECT ug.usergroupname UserGroupName, dpt.deptname Department, ug.usergroupdesc UserGroupDesc, count(ugf.usergroupid) NumOFFacilities
FROM tblusergroup ug
LEFT JOIN tblusergroupfacilities ugf
ON ug.usergroupid = ugf.usergroupid
LEFT JOIN tbldepartment dpt
ON dpt.deptcode = ug.deptcode
LEFT JOIN tblfacility f
ON ugf.facilityid = f.facilityid
WHERE ug.isdeleted = 0
GROUP BY ug.usergroupname, dpt.deptname, ug.usergroupdesc
However, when I add the where clause to not include deleted facilities, it produces a whole other result:
Edited statement
SELECT ug.usergroupname UserGroupName, dpt.deptname Department, ug.usergroupdesc UserGroupDesc, count(ugf.usergroupid) NumOFFacilities
FROM tblusergroup ug
LEFT JOIN tblusergroupfacilities ugf
ON ug.usergroupid = ugf.usergroupid
LEFT JOIN tbldepartment dpt
ON dpt.deptcode = ug.deptcode
LEFT JOIN tblfacility f
ON ugf.facilityid = f.facilityid
WHERE ug.isdeleted = 0
AND f.isdeleted = 0 //<-------added clause
GROUP BY ug.usergroupname, dpt.deptname, ug.usergroupdesc
Changed result
Is there any way at all to achieve the results above excluding the deleted facilities? Any help would really be appreciated.
Nothing off the top of my head on how to do it in one query, but with a UNION it seems straightforward. Run each alone to understand what they provide:
-- Your "changed" query.
UNION -- or UNION ALL, look up the difference, cuz I forgot it.
-- Your original query.
HAVING count(ugf.usergroupid) = 0
A LEFT JOIN keeps all rows in the first table plus matching rows in the second. When there is no match, then the values returned are NULL. Your condition on f.isdeleted removes all NULL values, turning the LEFT JOIN into an INNER JOIN.
The solution is to put the condition in the ON clause:
SELECT ug.usergroupname UserGroupName, dpt.deptname Department, ug.usergroupdesc UserGroupDesc, count(ugf.usergroupid) NumOFFacilities
FROM tblusergroup ug LEFT JOIN
tblusergroupfacilities ugf
ON ug.usergroupid = ugf.usergroupid LEFT JOIN
tbldepartment dpt
ON dpt.deptcode = ug.deptcode LEFT JOIN
tblfacility f
ON ugf.facilityid = f.facilityid AND f.isdeleted = 0
WHERE ug.isdeleted = 0
GROUP BY ug.usergroupname, dpt.deptname, ug.usergroupdesc;
Note that conditions on the first table do belong in the WHERE clause.

LEFT JOIN ON COALESCE(a, b, c) - very strange behavior

I have encountered very strange behavior of my query and I wasted a lot of time to understand what causes it, in vane. So I am asking for your help.
SELECT count(*) FROM main_table
LEFT JOIN front_table ON front_table.pk = main_table.fk_front_table
LEFT JOIN info_table ON info_table.pk = front_table.fk_info_table
LEFT JOIN key_table ON key_table.pk = COALESCE(info_table.fk_key_table, front_table.fk_key_table_1, front_table.fk_key_table_2)
LEFT JOIN side_table ON side_table.fk_front_table = front_table.pk
WHERE side_table.pk = (SELECT MAX(pk) FROM side_table WHERE fk_front_table = front_table.pk)
OR side_table.pk IS NULL
Seems like a simple join query, with coalesce, I've used this technique before(not too many times) and it worked right.
In this query I don't ever get nulls for side_table.pk. If I remove coalesce or just don't use key_table, then the query returns rows with many null side_table.pk, but if I add coalesce join, I can't get those nulls.
It seems key_table and side_table don't have anything in common, but the result is so weird.
Also, when I don't use side_table and WHERE clause, the count(*) result with coalesce and without differs, but I can't see any pattern in rows missing, it seems random!
Real query:
SELECT ECHANGE.EXC_AUTO_KEY, STOCK_RESERVATIONS.STR_AUTO_KEY FROM EXCHANGE
LEFT JOIN WO_BOM ON WO_BOM.WOB_AUTO_KEY = EXCHANGE.WOB_AUTO_KEY
LEFT JOIN VIEW_WO_SUB ON VIEW_WO_SUB.WOO_AUTO_KEY = WO_BOM.WOO_AUTO_KEY
LEFT JOIN STOCK stock3 ON stock3.STM_AUTO_KEY = EXCHANGE.STM_AUTO_KEY
LEFT JOIN STOCK stock2 ON stock2.STM_AUTO_KEY = EXCHANGE.ORIG_STM
LEFT JOIN CONSIGNMENT_CODES con2 ON con2.CNC_AUTO_KEY = stock2.CNC_AUTO_KEY
LEFT JOIN CONSIGNMENT_CODES con3 ON con3.CNC_AUTO_KEY = stock3.CNC_AUTO_KEY
LEFT JOIN CI_UTL ON CI_UTL.CUT_AUTO_KEY = EXCHANGE.CUT_AUTO_KEY
LEFT JOIN PART_CONDITION_CODES pcc2 ON pcc2.PCC_AUTO_KEY = stock2.PCC_AUTO_KEY
LEFT JOIN PART_CONDITION_CODES pcc3 ON pcc3.PCC_AUTO_KEY = stock3.PCC_AUTO_KEY
LEFT JOIN STOCK_RESERVATIONS ON STOCK_RESERVATIONS.STM_AUTO_KEY = stock3.STM_AUTO_KEY
LEFT JOIN WAREHOUSE wh2 ON wh2.WHS_AUTO_KEY = stock2.WHS_ORIGINAL
LEFT JOIN SM_HISTORY ON (SM_HISTORY.STM_AUTO_KEY = EXCHANGE.ORIG_STM AND SM_HISTORY.WOB_REF = EXCHANGE.WOB_AUTO_KEY)
LEFT JOIN RC_DETAIL ON stock3.RCD_AUTO_KEY = RC_DETAIL.RCD_AUTO_KEY
LEFT JOIN RC_HEADER ON RC_HEADER.RCH_AUTO_KEY = RC_DETAIL.RCH_AUTO_KEY
LEFT JOIN WAREHOUSE wh3 ON wh3.WHS_AUTO_KEY = COALESCE(RC_DETAIL.WHS_AUTO_KEY, stock3.WHS_ORIGINAL, stock3.WHS_AUTO_KEY)
WHERE STOCK_RESERVATIONS.STR_AUTO_KEY = (SELECT MAX(STR_AUTO_KEY) FROM STOCK_RESERVATIONS WHERE STM_AUTO_KEY = stock3.STM_AUTO_KEY)
OR STOCK_RESERVATIONS.STR_AUTO_KEY IS NULL
Removing LEFT JOIN WAREHOUSE wh3 gives me about unique EXC_AUTO_KEY values with a lot of NULL STR_AUTO_KEY, while leaving this row removes all NULL STR_AUTO_KEY.
I recreated simple tables with numbers with the same structure and query works without any problems o.0
I have a feeling COALESCE is acting as a REQUIRED flag for the joined table, hence shooting the LEFT JOIN to become an INNER JOIN.
Try this:
SELECT COUNT(*)
FROM main_table
LEFT JOIN front_table ON front_table.pk = main_table.fk_front_table
LEFT JOIN info_table ON info_table.pk = front_table.fk_info_table
LEFT JOIN key_table ON key_table.pk = NVL(info_table.fk_key_table, NVL(front_table.fk_key_table_1, front_table.fk_key_table_2))
LEFT JOIN (SELECT fk_, MAX(pk) as pk FROM side_table GROUP BY fk_) st ON st.fk_ = front_table.pk
NVL might behave just the same though...
I undertood what was the problem (not entirely though): there is a LEFT JOIN VIEW_WO_SUB in original query, 3rd line. It causes this query to act in a weird way.
When I replaced the view with the other table which contained the information I needed, the query started returning right results.
Basically, with this view join, NVL, COALESCE or CASE join with combination of certain arguments did not work along with OR clause in WHERE subquery, all rest was fine. ALthough, I could get the query to work with this view join, by changing the order of joined tables, I had to place table participating in where subquery to the bottom.

SQL JOIN WIth null

What can I do do make my statement return Null when there is no row with D.MetaDataID = '580c215f-54cb-4449-8368-7d740be71973' and the Data Table?
I've tried Left JOIN But nothing changes... :(
SELECT
D.Value
FROM dbo.Item I
JOIN dbo.Data D ON D.ItemID = I.ID
WHERE I.ItemTypeID = '14ea6709-1bf8-4d5c-9090-3ace3cc42874' --Instance.album
AND D.MetaDataID = '580c215f-54cb-4449-8368-7d740be71973' --freepacks
You need a LEFT JOIN and to move the condition on D into the ON clause rather than WHERE. In the WHERE clause this converts your query back into an inner join.
SELECT D.Value
FROM dbo.Item I
LEFT JOIN dbo.Data D
ON D.ItemID = I.ID
AND D.MetaDataID = '580c215f-54cb-4449-8368-7d740be71973'
--freepacks
WHERE I.ItemTypeID = '14ea6709-1bf8-4d5c-9090-3ace3cc42874' --Instance.album
Make it a LEFT JOIN. Left joins will preserve every row from the original table (dbo.Item), even if the table being joined (dbo.Data) would be NULL. Of course, then you also can't require dbo.Data.anything to be nonzero in your on clause so you should probably remove that criterion.

Super Slow Query - sped up, but not perfect... Please help

I posted a query yesterday (see here) that was horrible (took over a minute to run, resulting in 18,215 records):
SELECT DISTINCT
dbo.contacts_link_emails.Email, dbo.contacts.ContactID, dbo.contacts.First AS ContactFirstName, dbo.contacts.Last AS ContactLastName, dbo.contacts.InstitutionID,
dbo.institutionswithzipcodesadditional.CountyID, dbo.institutionswithzipcodesadditional.StateID, dbo.institutionswithzipcodesadditional.DistrictID
FROM
dbo.contacts_def_jobfunctions AS contacts_def_jobfunctions_3
INNER JOIN
dbo.contacts
INNER JOIN
dbo.contacts_link_emails
ON dbo.contacts.ContactID = dbo.contacts_link_emails.ContactID
ON contacts_def_jobfunctions_3.JobID = dbo.contacts.JobTitle
INNER JOIN
dbo.institutionswithzipcodesadditional
ON dbo.contacts.InstitutionID = dbo.institutionswithzipcodesadditional.InstitutionID
LEFT OUTER JOIN
dbo.contacts_def_jobfunctions
INNER JOIN
dbo.contacts_link_jobfunctions
ON dbo.contacts_def_jobfunctions.JobID = dbo.contacts_link_jobfunctions.JobID
ON dbo.contacts.ContactID = dbo.contacts_link_jobfunctions.ContactID
WHERE
(dbo.contacts.JobTitle IN
(SELECT JobID
FROM dbo.contacts_def_jobfunctions AS contacts_def_jobfunctions_1
WHERE (ParentJobID <> '1841')))
AND
(dbo.contacts_link_emails.Email NOT IN
(SELECT EmailAddress
FROM dbo.newsletterremovelist))
OR
(dbo.contacts_link_jobfunctions.JobID IN
(SELECT JobID
FROM dbo.contacts_def_jobfunctions AS contacts_def_jobfunctions_2
WHERE (ParentJobID <> '1841')))
AND
(dbo.contacts_link_emails.Email NOT IN
(SELECT EmailAddress
FROM dbo.newsletterremovelist AS newsletterremovelist))
ORDER BY EMAIL
With a lot of coaching and research, I've tuned it up to the following:
SELECT contacts.ContactID,
contacts.InstitutionID,
contacts.First,
contacts.Last,
institutionswithzipcodesadditional.CountyID,
institutionswithzipcodesadditional.StateID,
institutionswithzipcodesadditional.DistrictID
FROM contacts
INNER JOIN contacts_link_emails ON
contacts.ContactID = contacts_link_emails.ContactID
INNER JOIN institutionswithzipcodesadditional ON
contacts.InstitutionID = institutionswithzipcodesadditional.InstitutionID
WHERE
(contacts.ContactID IN
(SELECT contacts_2.ContactID
FROM contacts AS contacts_2
INNER JOIN contacts_link_emails AS contacts_link_emails_2 ON
contacts_2.ContactID = contacts_link_emails_2.ContactID
LEFT OUTER JOIN contacts_def_jobfunctions ON
contacts_2.JobTitle = contacts_def_jobfunctions.JobID
RIGHT OUTER JOIN newsletterremovelist ON
contacts_link_emails_2.Email = newsletterremovelist.EmailAddress
WHERE (contacts_def_jobfunctions.ParentJobID <> 1841)
GROUP BY contacts_2.ContactID
UNION
SELECT contacts_1.ContactID
FROM contacts_link_jobfunctions
INNER JOIN contacts_def_jobfunctions AS contacts_def_jobfunctions_1 ON
contacts_link_jobfunctions.JobID = contacts_def_jobfunctions_1.JobID
AND contacts_def_jobfunctions_1.ParentJobID <> 1841
INNER JOIN contacts AS contacts_1 ON
contacts_link_jobfunctions.ContactID = contacts_1.ContactID
INNER JOIN contacts_link_emails AS contacts_link_emails_1 ON
contacts_link_emails_1.ContactID = contacts_1.ContactID
LEFT OUTER JOIN newsletterremovelist AS newsletterremovelist_1 ON
contacts_link_emails_1.Email = newsletterremovelist_1.EmailAddress
GROUP BY contacts_1.ContactID))
While this query is now super fast (about 3 seconds), I've blown part of the logic somewhere - it only returns 14,863 rows (instead of the 18,215 rows that I believe is accurate).
The results seem near correct. I'm working to discover what data might be missing in the result set.
Can you please coach me through whatever I've done wrong here?
Thanks,
Russell Schutte
The main problem with your original query was that you had two extra joins just to introduce duplicates and then a DISTINCT to get rid of them.
Use this:
SELECT cle.Email,
c.ContactID,
c.First AS ContactFirstName,
c.Last AS ContactLastName,
c.InstitutionID,
izip.CountyID,
izip.StateID,
izip.DistrictID
FROM dbo.contacts c
INNER JOIN
dbo.institutionswithzipcodesadditional izip
ON izip.InstitutionID = c.InstitutionID
INNER JOIN
dbo.contacts_link_emails cle
ON cle.ContactID = c.ContactID
WHERE cle.Email NOT IN
(
SELECT EmailAddress
FROM dbo.newsletterremovelist
)
AND EXISTS
(
SELECT NULL
FROM dbo.contacts_def_jobfunctions cdj
WHERE cdj.JobId = c.JobTitle
AND cdj.ParentJobId <> '1841'
UNION ALL
SELECT NULL
FROM dbo.contacts_link_jobfunctions clj
JOIN dbo.contacts_def_jobfunctions cdj
ON cdj.JobID = clj.JobID
WHERE clj.ContactID = c.ContactID
AND cdj.ParentJobId <> '1841'
)
ORDER BY
email
Create the following indexes:
newsletterremovelist (EmailAddress)
contacts_link_jobfunctions (ContactID, JobID)
contacts_def_jobfunctions (JobID)
Do you get the same results when you do:
SELECT count(*)
FROM
dbo.contacts_def_jobfunctions AS contacts_def_jobfunctions_3
INNER JOIN
dbo.contacts
INNER JOIN
dbo.contacts_link_emails
ON dbo.contacts.ContactID = dbo.contacts_link_emails.ContactID
ON contacts_def_jobfunctions_3.JobID = dbo.contacts.JobTitle
SELECT COUNT(*)
FROM
contacts
INNER JOIN contacts_link_jobfunctions
ON contacts.ContactID = contacts_link_jobfunctions.ContactID
INNER JOIN contacts_link_emails
ON contacts.ContactID = contacts_link_emails.ContactID
If so keep adding each join conditon on until you don't get the same results and you will see where your mistake was. If all the joins are the same, then look at the where clauses. But I will be surprised if it isn't in the first join because the syntax you have orginally won't even work on SQL Server and it is pretty nonstandard SQL and may have been incorrect all along but no one knew.
Alternatively, pick a few of the records that are returned in the orginal but not the revised. Track them through the tables one at a time to see if you can find why the second query filters them out.
I'm not directly sure what is wrong, but when I run in to this situation, the first thing I do is start removing variables.
So, comment out the where clause. How many rows are returned?
If you get back the 11,604 rows then you've isolated the problems to the joins. Work though the joins, commenting each one out (remove the associated columns too) and figure out how many rows are eliminated.
As you do this, aim to find what is causing the desired rows to be eliminated. Once isolated, consider the join differences between the first query and the second query.
In looking at the first query, you could probably just modify that to eliminate any INs and instead do a EXISTS instead.
Consider your indexes as well. Any thing in the where or join clauses should probably be indexed.