SQL Left Outer Join acting like Inner Join - sql

I am trying to do a left outer join on two tables (well, an inline view and a table).
What I want to happen is to list all the grads (I know there are 3815 DISTINCT Grads) with any of their enrolments (there could be 0 or n enrolments). What I'm getting is only a list of the grads that have enrolments (3649 DISTINCT students). I'm not sure where I'm going wrong with not getting all the rows from the grad 'view' (I don't have create view privs so this is my workaround).
This is my code:
SELECT C.*, D.FREEZE_EVENT, D.ACADEMIC_PERIOD, D.CAMPUS, D.COLLEGE, D.COLLEGE_DESC,D.MAJOR, D.MAJOR_DESC , D.STUDENT_RATE
FROM
(SELECT A.STUDENT_LEVEL_DESC, A.CAMPUS, A.CAMPUS_DESC, A.COLLEGE, A.COLLEGE_DESC, A.MAJOR_DESC, A.MAJOR, A.DEGREE_DESC, A.PERSON_UID, A.ID, A.NAME,
A.OUTCOME_GRADUATION_DATE, A.STATUS, A.GRAD_YEAR, A.TRAINING_LOCATION, B.CITIZENSHIP_TYPE
FROM ACAD_OUTOCME A, PERSON_DETAIL B
WHERE A.STUDENT_LEVEL IN ('02','03') AND A.GRAD_YEAR = '2015' AND A.FREEZE_EVENT = '10TH_SEP2016' AND B.FREEZE_EVENT = '10TH_SEP2016'
AND A.ID = B.ID) C
LEFT OUTER JOIN ACAD_STUDY D ON
C.CAMPUS = D.CAMPUS
AND C.COLLEGE = D.COLLEGE
AND C.MAJOR = D.MAJOR
AND C.PERSON_UID = D.PERSON_UID
WHERE D.FREEZE_EVENT = '10TH_SEP2016'
ORDER BY C.NAME
Any suggestions? I'm using Toad Data Point. I'm also the loan developer at work, so I don't have anyone I can ask to help out with this, and google has failed me.
Thanks!

Move your WHERE condition to the ON condition:
Select C.*
, D.FREEZE_EVENT
, D.ACADEMIC_PERIOD
, D.CAMPUS
, D.COLLEGE
, D.COLLEGE_DESC
, D.MAJOR
, D.MAJOR_DESC
, D.STUDENT_RATE
From (Select A.STUDENT_LEVEL_DESC
, A.CAMPUS
, A.CAMPUS_DESC
, A.COLLEGE
, A.COLLEGE_DESC
, A.MAJOR_DESC
, A.MAJOR
, A.DEGREE_DESC
, A.PERSON_UID
, A.ID
, A.NAME
, A.OUTCOME_GRADUATION_DATE
, A.STATUS
, A.GRAD_YEAR
, A.TRAINING_LOCATION
, B.CITIZENSHIP_TYPE
From ACAD_OUTOCME A
Join PERSON_DETAIL B On A.ID = B.ID
Where A.STUDENT_LEVEL In ('02', '03')
And A.GRAD_YEAR = '2015'
And A.FREEZE_EVENT = '10TH_SEP2016'
And B.FREEZE_EVENT = '10TH_SEP2016'
) C
Left Outer Join ACAD_STUDY D
On C.CAMPUS = D.CAMPUS
And C.COLLEGE = D.COLLEGE
And C.MAJOR = D.MAJOR
And C.PERSON_UID = D.PERSON_UID
And D.FREEZE_EVENT = '10TH_SEP2016'
Order By C.NAME;
The WHERE clause is evaluated after the OUTER JOIN, which would cause it to filter out the NULL records from the LEFT JOIN. So, having the right-hand table of a LEFT JOIN in the WHERE clause will effectively transform the OUTER JOIN into an INNER JOIN.

Related

Where Clause Using Conditional Statement

i have query below
SELECT #RoleUser = MR.Code FROM MasterRole MR INNER JOIN MasterUsersRole MUR ON MR.Id = MUR.RoleId
INNER JOIN MasterUsers MU ON Mu.UserCode = MUR.UserCode
WHERE MU.UserCode = #UserLoginID
select 1 Num
, MyHistory.ID
, MyHistory.RequestNumber
, MyHistory.FlowID
, MyHistory.FlowProcessStatusID
from
(
select *
from Requests R
inner join
(
--DECLARE #UserLoginID nvarchar(200) = 'dum.testing.3'
select distinct
RequestID
from dbo.RequestTrackingHistory RTH
where IIF(#UserLoginID = 'admin', #UserLoginID, RTH.CreatedBy) = #UserLoginID
OR ( CreatedBy IN
SELECT Mu.UserCode from MasterUsers MU
INNER JOIN MasterUsersRole MUR ON MU.UserCode = MUR.UserCode
INNER JOIN MasterRole MR ON MUR.RoleId = MR.Id
WHERE MR.Code = #RoleUser
)
)
) RT on R.ID = RT.RequestID
) as MyHistory
inner join MasterFlow F on MyHistory.FlowID = F.ID
inner join
(
select FP.ID
, FP.Name
, FP.AssignType
, FP.AssignTo
, FP.IsStart
, case FP.AssignType
when 'GROUP' then
G.Name
end as 'AssignToName'
from MasterFlowProcess FP
left join dbo.MasterRole G on FP.AssignTo = G.ID and FP.AssignType = 'GROUP'
) FP on MyHistory.FlowProcessID = FP.ID
inner join MasterFlowProcessStatus FPS on MyHistory.FlowProcessStatusID = FPS.ID
left join MasterFlowProcessStatusNext FPSN on FPS.ID = FPSN.ProcessStatusFlowID
left join MasterFlowProcess FPN on FPSN.NextProcessFlowID = FPN.ID
left JOIN MasterRole MR ON MR.Id = FPN.AssignTo
left join MasterUsersRole MUR on MR.Id = MUR.RoleId
left join MasterUsers MURO on MUR.UserCode = MURO.UserCode
inner join MasterUsers UC on MyHistory.CreatedBy = UC.UserCode
left join MasterUsers UU on MyHistory.UpdatedBy = UU.UserCode
LEFT JOIN RequestMT RMT ON MyHistory.ID = RMT.RequestID
LEFT JOIN RequestGT RGT ON MyHistory.ID = RGT.RequestID
left join (SELECT sum(QtyCU) countQty , RequestId from dbo.RequestGTDetail where IsActive = 1 group by RequestId) RGTD on RGTD.RequestId = RGT.RequestId
left join (SELECT sum(QtyPCS) countQty , RequestId from dbo.RequestMTDetail where IsActive = 1 group by RequestId) RMTD on RMTD.RequestId = RMT.RequestId
left join (SELECT COUNT(IIF(returnable = 0, returnable, null)) countReturnable , RequestId from dbo.RequestMTDetail group by RequestId) RMTR on RMTR.RequestId = RMT.RequestId
left JOIN dbo.MasterDistributor md ON md.Code = RGT.CustId or md.Code = RMT.CustId
left JOIN dbo.MasterUsersDistributor MUD ON MUD.UserCode = MURO.UserCode AND md.Code = MUD.DistributorCode
LEFT JOIN dbo.MasterReason MRMT ON RMT.ReasonId = MRMT.Id
LEFT JOIN dbo.MasterReason MRGT ON RGT.ReasonId = MRGT.Id
LEFT JOIN dbo.MasterDistributorGroup MDG ON MDG.Id = MD.GroupId
OUTER APPLY dbo.FnGetHistoryApproveDate(MyHistory.Id) AS x
where REPLACE(FPS.Name, '#Requestor', uc.Name) <> 'DRAFT'
AND MUD.DistributorCode IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = #UserLoginID)
i want to add some logic in where clause
this line
==> AND MUD.DistributorCode IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = #UserLoginID)
it depend on the #RoleUser variable, if #RoleUser IN ('A','B') then where clause above is executed, but if #RoleUser Not IN ('A','B') where clause not executed
i,m trying this where clause
AND IIF(#RoleUser IN ('A','B'), MUD.DistributorCode, #RoleUser) IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = IIF(#RoleUser IN ('A','B'), #UserLoginID, NULL))
it didn't work, only executed if #RoleUser IS ('A','B') other than that it return 0 record
any help or advice is really appreciated
thank you
The cleanest way I'm implemented these kind of crazy rules is a
holderTable
and a countVariable against the holder table.
I'll give a generic examples.
This is a "approach" and "philosophy", not a specific answer....with complex WHERE clauses.
DECLARE #customerCountryCount int
DECLARE #customerCountry TABLE ( CountryName varchar(15) )
if ( "the moon is blue on tuesday" ) /* << whatever rules you have */
BEGIN
INSERT INTO #customerCountry SELECT "Honduras" UNION ALL SELECT "Malaysia"
END
if ( "favorite color = green" ) /* << whatever rules you have */
BEGIN
INSERT INTO #customerCountry SELECT "Greenland" UNION ALL SELECT "Peru"
END
SELECT #customerCountryCount = COUNT(*) FROM #customerCountry
Select * from dbo.Customers c
WHERE
(#customerCountryCount = 0)
OR
( exists (select null from #customerCountry innerVariableTable where UPPER(innerVariableTable.CountryName) = UPPER(c.Country) ))
)
This way, instead of putting all the "twisted logic" in an overly complex WHERE statement..... you have "separation of concerns"...
Your inserts into #customerCountry are separated from your use of #customerCountry.
And you have the #customerCountryCount "trick" to distinguish when nothing was used.
You can add a #customerCountryNotExists table as well, and code it to where not exists.
As a side note, you may want to try using a #temp table (instead of a #variabletable (#customerCountry above)... and performance test these 2 options.
There is no "single answer". You have to "try it out".
And many many variables go into #temp table performance (from a sql-server SETUP, not "how you code a stored procedure". That is way outside the scope of this question.
Here is a SOF link to "safe" #temp table usage.
Temporary table in SQL server causing ' There is already an object named' error

Trying a subquery for my first time and not sure how to within an aggregate statement

I am trying to execute the below script but a, unsure how to combine the aggregate select statement.
I am trying to add the subquery to sum the amount of parts within the fsl. I am getting the following errors:
Msg 8120, Level 16, State 1, Line 17 Column 'Customer.CustInvId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Msg 8120, Level 16, State 1, Line 16 Column 'dbo.FSLMaster.FSLId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Can someone please help me be able to execute this? Thank you so much!
P.S.Feel free to tell me I am horrible at explaining and I will do my best to give more details to what I am doing
SELECT c.CustCode
, na.NatName
, c.[Name]
, fm.Code
, fm.FSLName
, cfs.SquareFeet AS 'SQFT'
, CONCAT(a.AddrLine1,',', a.City,',', a.StateAbbr,',', a.ZipCode,',', a.CountryId) AS 'Full Adrress'
, a.AddrLine1
, a.City
, a.StateAbbr
, a.ZipCode
, a.CountryId
, a.Longitude
, a.Latitude
,
(
SELECT ISNULL(SUM(ISNULL(OnHandQty,0) - (ISNULL(ReservedQty,0) )),0) FROM dbo.FSLStock WITH (NOLOCK) WHERE FSLStock.FSLId = FM.FSLId
AND PartMasterId IN ( SELECT PartMasterId FROM dbo.PartMaster P WITH (NOLOCK) WHERE P.CustInvId=C.CustInvId)) AS IOH
FROM Customer C WITH(NOLOCK)
INNER JOIN dbo.CustInvProfile CP WITH(NOLOCK) ON C.CustCode = CP.CustCode AND C.ValidTo IS NULL
INNER JOIN dbo.CustFSLAssociation CF WITH(NOLOCK) ON CF.CustInvId = CP.CustInvId AND CF.ValidTo IS NULL
INNER JOIN dbo.FSLMaster FM WITH(NOLOCK) ON FM.FSLId = CF.FSLId AND (COALESCE(FM.ValidTo,getutcdate()) >= getutcdate())
LEFT JOIN CustFSLStrgDtl CFS WITH(NOLOCK) ON cfs.CustInvId = CF.CustInvId and cfs.FSLId = CF.FSLId
LEFT JOIN [Address] a ON fm.AddrId = a.AddrId
LEFT JOIN NationalAccount na ON c.NatAccountId = na.NatAccountId
LEFT JOIN FSLStock fs ON fm.FSLId = fs.FSLId
GROUP BY c.CustCode, na.NatName, c.[Name], fm.Code, fm.FSLName, cfs.SquareFeet, a.AddrLine1, a.City, a.StateAbbr, a.ZipCode, a.CountryId, a.Longitude, a.Latitude
You can use distinct instead to remove duplicates :
select distinct c.CustCode, na.NatName, c.[Name], fm.Code, fm.FSLName,
. . .
from Customer C inner join
dbo.CustInvProfile CP
on . . .
Note : NOLOCK reads dirty data (uncommitted). Please be aware.
You should be using Group by only when you need some kind of aggregation done at the grouped up levels. You don't actually need the subquery, you can convert it into a join:
SELECT c.CustCode
, na.NatName
, c.[Name]
, fm.Code
, fm.FSLName
, cfs.SquareFeet AS 'SQFT'
, CONCAT(a.AddrLine1,',', a.City,',', a.StateAbbr,',', a.ZipCode,',', a.CountryId) AS 'Full Adrress'
, a.AddrLine1
, a.City
, a.StateAbbr
, a.ZipCode
, a.CountryId
, a.Longitude
, a.Latitude
, SUM(CASE WHEN P.Partmasterid is not null then ISNULL(SUM(ISNULL(OnHandQty,0) - (ISNULL(ReservedQty,0) )),0) else 0 end) as IOH
FROM Customer C WITH(NOLOCK)
INNER JOIN dbo.CustInvProfile CP WITH(NOLOCK) ON C.CustCode = CP.CustCode AND C.ValidTo IS NULL
INNER JOIN dbo.CustFSLAssociation CF WITH(NOLOCK) ON CF.CustInvId = CP.CustInvId AND CF.ValidTo IS NULL
INNER JOIN dbo.FSLMaster FM WITH(NOLOCK) ON FM.FSLId = CF.FSLId AND (COALESCE(FM.ValidTo,getutcdate()) >= getutcdate())
LEFT JOIN CustFSLStrgDtl CFS WITH(NOLOCK) ON cfs.CustInvId = CF.CustInvId and cfs.FSLId = CF.FSLId
LEFT JOIN [Address] a ON fm.AddrId = a.AddrId
LEFT JOIN NationalAccount na ON c.NatAccountId = na.NatAccountId
LEFT JOIN FSLStock fs ON fm.FSLId = fs.FSLId
LEFT JOIN PartMaster P ON P.CustInvId=C.CustInvId
GROUP BY c.CustCode, na.NatName, c.[Name], fm.Code, fm.FSLName, cfs.SquareFeet, a.AddrLine1, a.City,
a.StateAbbr, a.ZipCode, a.CountryId, a.Longitude, a.Latitude
If you think, no aggregation is needed, you can simply remove the group by statement. Hope this helps.

An SQL query with an 'IN' within the where clause is very slow to run

I have created an SQL query which runs but it takes about 17 seconds to complete. I have narrowed down the problem to the IN clause within the Where section of the query. This section is responsible for also finding all records within the same table where the kitref of one record matches the partofkit field of multiple other records.
Could anyone help with suggestions on how I may be able to make this more efficient?
The full SQL is below:
SELECT
tblProductions.ProductionName, tblEquipment.KitRef, tblEquipment.PartOfKit, tblEquipment.Description,
tblCollection.HireID, tblCollection.CollectedBy, Format(tblCollection.DueBack,'dd/MM/yyyy') AS DueBack, Format(tblCollection.CollectionDate,'dd/MM/yyyy') AS CollectionDate, Format(tblCollection.CollectionTime,'HH:mm') AS CollectionTime, tblCollection.DiscountPC,
tblCollectionItemized.HireLine, tblCollectionItemized.Notes, tblCollectionItemized.BookingActive, tblCollectionItemized.DepositReturned, tblTariff.Tariff
FROM tblTariff
INNER JOIN (
tblProductions INNER JOIN (
tblCollection INNER JOIN (
tblEquipment
INNER JOIN tblCollectionItemized ON tblEquipment.KitKey = tblCollectionItemized.KitKey
) ON tblCollection.HireID = tblCollectionItemized.HireID)
ON tblProductions.ProductionIDKey = tblCollection.ProductionName
) ON tblTariff.TariffKey = tblCollection.Tarriff
WHERE (
tblCollectionItemized.BookingActive='TRUE'
AND tblEquipment.PartOfKit IN (
SELECT tblEquipment.KitRef
FROM tblEquipment
INNER JOIN tblCollectionItemized ON tblEquipment.KitKey = tblCollectionItemized.KitKey
WHERE tblCollectionItemized.ReturnsNumber =43
)
)
OR (
tblCollectionItemized.BookingActive='TRUE'
AND tblCollectionItemized.ReturnsNumber =43
)
Not a complete answer here but using some aliases and joins in a logical order make this nightmarish query into something a lot easier to see what is going on.
SELECT
p.ProductionName
, e.KitRef
, e.PartOfKit
, e.Description
, c.HireID
, c.CollectedBy
, Format(c.DueBack,'dd/MM/yyyy') AS DueBack
, Format(c.CollectionDate,'dd/MM/yyyy') AS CollectionDate
, Format(c.CollectionTime,'HH:mm') AS CollectionTime
, c.DiscountPC
, ci.HireLine
, ci.Notes
, ci.BookingActive
, ci.DepositReturned
, t.Tariff
FROM tblTariff t
INNER JOIN tblCollection c ON t.TariffKey = c.Tarriff
INNER JOIN tblProductions p ON p.ProductionIDKey = c.ProductionName
INNER JOIN tblCollectionItemized ci ON c.HireID = ci.HireID
INNER JOIN tblEquipment e ON e.KitKey = ci.KitKey
WHERE ci.BookingActive = 'TRUE'
AND e.PartOfKit IN
(
SELECT e2.KitRef
FROM tblEquipment e2
INNER JOIN tblCollectionItemized ci2 ON e2.KitKey = ci2.KitKey
WHERE ci2.ReturnsNumber = 43
)
OR
(
ci.ReturnsNumber = 43
)
you can try EXISTS instead of IN and add (nolock) hint to tables
SELECT
P.ProductionName,
E.KitRef,
E.PartOfKit,
E.Description,
C.HireID,
C.CollectedBy,
Format(C.DueBack,'dd/MM/yyyy') AS DueBack,
Format(C.CollectionDate,'dd/MM/yyyy') AS CollectionDate,
Format(C.CollectionTime,'HH:mm') AS CollectionTime,
C.DiscountPC,
CI.HireLine,
CI.Notes,
CI.BookingActive,
CI.DepositReturned,
T.Tariff
FROM tblTariff T
INNER JOIN tblCollection C (nolock) ON T.TariffKey = C.Tarriff
INNER JOIN tblProductions P (nolock) ON P.ProductionIDKey = C.ProductionName
INNER JOIN tblCollectionItemized CI (nolock) ON C.HireID = CI.HireID
INNER JOIN tblEquipment E (nolock) ON E.KitKey = CI.KitKey
WHERE (
tblCollectionItemized.BookingActive='TRUE'
AND EXISTS (
SELECT *
FROM tblEquipment E2 (nolock)
INNER JOIN tblCollectionItemized CI2 (nolock) ON E2.KitKey = CI2.KitKey
WHERE CI2.ReturnsNumber =43 AND E.PartOfKit = E2.KitRef )
)
OR (
CI.BookingActive='TRUE'
AND CI.ReturnsNumber =43
)

Using a group by to group a select statement

Using a group by to group a select stament
SELECT
k.Ivalue, k.JOBDESCRIPTION ,
count( k.Ivalue) as TOTAL
FROM
(SELECT
a."ID" as Ivalue, b."JOBDESCRIPTION", rq."CURRENTSTATUS"
FROM
tblG2o_Requests a
INNER JOIN
tblG2o_JOBS b ON a."JOBPOSTID" = b."ID"
INNER JOIN
(SELECT
r.REQUESTID, ir."CURRENTSTATUS"
FROM
TBLG2O_RESULTSPOOL r
INNER JOIN
tblG2o_Requests ir ON r.RequestID = ir."ID"
WHERE
r.ShortListed = '1') rq ON rq.REQUESTID = a."ID"
WHERE
"ACTIVE" = '1'
AND "DATECOMPLETED" IS NULL
ORDER BY
"REQUESTDATE" DESC) k
GROUP BY
k.JOBDESCRIPTION
What is the question? You seem to be missing the group by clause, and you do not need double quotes around field names unless you have spaces in them, and even then, if TSQL for example, you would use [] in preference.
I had to remove an ORDER BY in the subquery, that isn't allowed unless other conditions demand it (like TOP n in TSQL)
SELECT
k.Ivalue
, k.JOBDESCRIPTION
, COUNT(k.Ivalue) AS TOTAL
FROM (
SELECT
a.ID AS Ivalue
, b.JOBDESCRIPTION
, rq.CURRENTSTATUS
FROM tblG2o_Requests a
INNER JOIN tblG2o_JOBS b
ON a.JOBPOSTID = b.ID
INNER JOIN (
SELECT
r.REQUESTID
, ir.CURRENTSTATUS
FROM TBLG2O_RESULTSPOOL r
INNER JOIN tblG2o_Requests ir
ON r.RequestID = ir.ID
WHERE r.ShortListed = '1'
) rqenter
ON rq.REQUESTID = a.ID
WHERE ACTIVE = '1'
AND DATECOMPLETED IS NULL
) k
GROUP BY
k.Ivalue
, k.JOBDESCRIPTION
Finally worked
SELECT
k.Ivalue
, l.JOBDESCRIPTION
, k.TOTAL,
k.CURRENTSTATUS
FROM (
SELECT
a.ID AS Ivalue
,b.ID as JobPostID
, rq."CURRENTSTATUS"
,COUNT(a.ID) AS TOTAL
FROM tblG2o_Requests a
INNER JOIN tblG2o_JOBS b
ON a."JOBPOSTID" = b.ID
INNER JOIN (
SELECT
r."REQUESTID"
, ir."CURRENTSTATUS"
FROM TBLG2O_RESULTSPOOL r
INNER JOIN tblG2o_Requests ir
ON r."REQUESTID" = ir.ID
WHERE r."SHORTLISTED" = 1
) rq
ON rq."REQUESTID" = a.ID
WHERE ACTIVE = '1'
AND DATECOMPLETED IS NULL
GROUP BY
a.ID ,b.ID
, rq."CURRENTSTATUS" ) k
inner join tblG2o_JOBS l on k.JobPostID =l.ID
enter code here

SQL - left join generate duplicates

I have code to select some applications but LEFT JOIN is creating duplicates.
Question: How to get rid of duplicates generated by
LEFT JOIN
attachments as att
ON
(a.ssn = att.ssn AND att.doc_type = 'id_copy' AND att.source = 'cpt3')
My Work:
I am considering SELECT DISTINCT or GROUP BY but, can't figure out where to put them.
This is the query:
SELECT
a.*, c.code, l.who, l.locked_time, att.id AS attnew,
( SELECT
COUNT(*)
FROM
applications as a2
WHERE
a2.approved = 'Y'
AND
a2.paid = 'Y'
AND
a2.paid_back = 'Y'
AND
a2.ssn = a.ssn
) AS previous_apps
FROM
applications as a
LEFT JOIN
locked_by as l
USING(id)
LEFT JOIN
campaign_codes as c
ON
c.id = a.campaign
LEFT JOIN
attachments as att
ON
(a.ssn = att.ssn AND att.doc_type = 'id_copy' AND att.source = 'cpt3')
WHERE
a.closed='N'
AND
a.paid = 'N'
ORDER BY
a.arrived_date
DESC
Use GROUP BY
to avoid duplicates. In your case:
...
WHERE
a.closed='N'
AND
a.paid = 'N'
GROUP BY
a.id
ORDER BY
a.arrived_date
DESC
If you want to make an one to one relation to avoid duplicates and att.id is the same for each att.ssn or you need only one (MAX,MIN,..ANY?) att.id. Try this:
LEFT JOIN
(SELECT ssn,
MAX(id) as id,
FROM attachments
WHERE doc_type = 'id_copy' AND source = 'cpt3'
GROUP BY ssn
)as att
ON
(a.ssn = att.ssn)