Left Join is not working - sql

I wrote following query
SELECT
us.Id as Id, us.Name as Name,
SUM(CASE WHEN c.isPublish = 0 THEN 1 ELSE 0 END) AS PendingCoupons,
SUM(CASE WHEN c.isPublish = 1 and convert(date,c.PublishedDate,101) >= convert(date, GETDATE(), 101) THEN 1 ELSE 0 END) AS ApprovedCouponsToday,
SUM(CASE WHEN c.isPublish = 0 and convert(date,c. CreateDate, 101) = convert(date, GETDATE(), 101) THEN 1 ELSE 0 END) AS PendingCouponsToday,
SUM(CASE WHEN c.isPublish = 1 THEN 1 ELSE 0 END) AS ApprovedCoupons,
SUM(CASE WHEN c.isPublish = 1 and c.Userid = us.Id and convert(date, c.PublishedDate, 101) >= convert(date, GETDATE(), 101) THEN 1 ELSE 0 END) AS ApprovedByUserToday,
SUM(CASE WHEN c.isPublish = 1 and c.Userid = us.Id THEN 1 ELSE 0 END) AS ApprovedByUser,
SUM(CASE WHEN c.ReviewVerify = 1 and convert(date, c.PublishedDate, 101) >= convert(date, GETDATE(), 101) THEN 1 ELSE 0 END) AS ProcessToday,
COUNT(*) AS Total
FROM
Users AS us
LEFT JOIN
Coupon c ON Userid = us.Id
GROUP BY
us.Name , us.Id
and I have following two tables
and after running above query the result is always this
Is there any error in this query , because its always returning me count " 0 " and I have almost 100 coupons on every user but its not showing

It would seem that since RIGHT JOIN works and gives correct answers (and probably a bogus user id) while a LEFT JOIN doesn't give any results related to coupons at all, the coupons are registered on a non existing user.
LEFT JOIN demands that data exists in the left part of the table that possibly exists in the right side, while RIGHT JOIN does the reverse. In other words, data exists in your rightmost table (coupon) that doesn't have any connection to the left side (user)

I think you may need to use a RIGHT JOIN to get all records form the Coupon table.

You not using the correct join.
You want to get coupon for a user id?
Use this join.
FROM
Users AS us
LEFT JOIN
Coupon c ON us.Id = c.Userid
if this does not work then use:
FROM
Users AS us
LEFT OUTER JOIN
Coupon c ON us.Id = c.Userid

Related

Group by clause not returning the desired result

I have the following query that will pull the status of the courses enrolled for each user. I need to display a single record for that user that will represent the overall status of the courses. As you can see in the query below, it uses case statement to decide the prioritisation of the status. I have tried to use a group by clause but it still shows all the courses in the resultset. Could someone let me know what am I doing wrong in the query
DECLARE #Rep1 INT;
SET #Rep1 = 13119;
SELECT
cr.[CourseID]
, cr.[UserID]
,u.[Code]
,u.[DisplayName]
,t.[Name]
,cr.[CourseResultStatusID] AS [CourseResultStatusID]
,case
when min(case when crs.[Description] = 'Complete' then 1 else 0 end) = 1
then 'Complete'
when max(case when crs.[Description] = 'Fail' then 1 else 0 end) = 1
then 'Fail'
when max(case when crs.[Description] = 'Expired' then 1 else 0 end) = 1
then 'Expired'
when max(case when crs.[Description] = 'In Progress' then 1 else 0 end) = 1
then 'In Progress'
end as [CourseResultStatusDescription]
,c.[PointsRequired]
,cr.[ExpiryDateTime]
FROM [training].[CourseResult] cr
INNER JOIN [training].[Course] c
ON cr.[CourseID] = c.[ID] and c.[IsOptional] = 0 -- and cr.ExpiryDateTime > GetDate() and cr.ExpiryDateTime <= dateadd(dd,30,getdate())
INNER JOIN [training].[CourseResultStatus] crs
ON cr.[CourseResultStatusID] = crs.[ID]
INNER JOIN org.RepresentativeTierHistory rth on rth.RepresentativeID = cr.[UserID] and GetDate() between rth.StartDate and rth.EndDate
INNER JOIN org.tier t on t.ID = rth.TierID
LEFT JOIN [org].[User] u
ON u.[ID] = cr.[UserID]
WHERE cr.[UserID] IN (
SELECT hd.DescendantId FROM org.HierarchyDescendant hd WHERE hd.RepresentativeId = #Rep1 UNION ALL SELECT #Rep1 -- for management exchange info
)
group by cr.[CourseID], cr.[UserID], u.[Code], u.[DisplayName], t.[Name], [CourseResultStatusID],c.[PointsRequired],cr.[ExpiryDateTime]
The result of the query is below. As you can see there are 24 records. It is currently showing all the 6 courses per user. It should ideally only only 4 records.
REsultset that I am looking for

Using an array in conditional summation with grouping

I have the sql query thats work fine
SELECT
CAST(L.CreationUtcDateTime AS DATE),
SUM(L.Profit),
SUM(CASE WHEN (L.Network = 0 OR L.Network = 1) THEN L.Profit ELSE 0 END),
SUM(CASE WHEN (L.Network != 0 AND L.Network != 1) THEN L.Profit ELSE 0 END)
FROM
[dbo].[Leads] L WITH (NOLOCK)
LEFT JOIN [dbo].[Transactions] S WITH (NOLOCK) ON L.TransactionId = S.Id
GROUP BY
CAST(L.CreationUtcDateTime AS DATE);
I need to make it more generic by using variables instead of hard-coded constants.
I changed the query to:
DECLARE #matchNetworks TABLE (id int)
INSERT #matchNetworks(id) VALUES (0),(1)
SELECT
CAST(L.CreationUtcDateTime AS DATE),
SUM(L.Profit),
SUM(CASE WHEN (L.Network in (SELECT ID from #matchNetworks)) THEN L.Profit ELSE 0 END),
SUM(CASE WHEN (L.Network not in (SELECT ID from #matchNetworks)) THEN L.Profit ELSE 0 END)
FROM
[dbo].[Leads] L WITH (NOLOCK)
LEFT JOIN [dbo].[Transactions] S WITH (NOLOCK) ON L.TransactionId = S.Id
GROUP BY
CAST(L.CreationUtcDateTime AS DATE);
And now i have an error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
How can I use the predefined array of network ids in my query to avoid an error?
Move the comparison to the FROM clause using a LEFT JOIN:
SELECT CAST(L.CreationUtcDateTime AS DATE),
SUM(L.Profit),
SUM(CASE WHEN mn.ID IS NOT NULL THEN L.Profit ELSE 0 END),
SUM(CASE WHEN mn.ID IS NULL THEN L.Profit ELSE 0 END)
FROM [dbo].[Leads] L LEFT JOIN
[dbo].[Transactions] S
ON L.TransactionId = S.Id LEFT JOIN
#matchNetworks mn
ON mn.ID = l.Network
GROUP BY CAST(L.CreationUtcDateTime AS DATE);

Aggregate case when inside non aggregate query

I have a pretty massive query that in its simplest form looks like this:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum
from table1 r
left join table2 u on r.user_id=u.user_id
left join table3 pi on u.user_id=pi.user_id
I need to add one more condition that gives me count of users with non null application date per rep (like: rep 1 has 3 users with filled application dates), and assign it into categories (since 3 users, rep is a certain status category). This looks something like this:
case when sum(case when application_date is not null then 1 else 0 end) >=10 then 'status1'
when sum(case when application_date is not null then 1 else 0 end) >=5 then 'status2'
when sum(case when application_date is not null then 1 else 0 end) >=1 then 'status3'
else 'no_status' end as category
However, if I was to simply add it to the select statement, all reps will becomes of status1 because the sum() is done over all advisors with application dates filled:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum,
(
select case when sum(case when application_date is not null then 1 else 0 end) >=10 then 'status1'
when sum(case when application_date is not null then 1 else 0 end) >=5 then 'status2'
when sum(case when application_date is not null then 1 else 0 end) >=1 then 'status3'
else 'no_status' end as category
from table3
) as category
from table1 r
left join table2 u on r.user_id=u.user_id
left join table3 pi on u.user_id=pi.user_id
Can you assist with having the addition to my query to be across reps and not overall? Much appreciated!
Based on your description, I think you need a window function:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum,
count(pi.application_date) over (partition by r.rep_id) as newcol
from table1 r left join
table2 u
on r.user_id = u.user_id left join
table3 pi
on u.user_id = pi.user_id;
You can use the count() in a case to get ranges, if that is what you prefer.

Count based on subset of data

I have a join to a table and I want to include all users who have a record after a certain date, but to only include records after another date in the count.
Here is my SQL :
select a.userid, count(ce.codeentryid)
from [account] a
inner join [profile] p
on a.userid = p.userid
inner join codesentered ce
on a.userid = ce.userid and ce.entrydate > '2011-01-01 00:00:00'
where a.camp = 0
group by a.userid
order by a.userid
So here I want to view a list of all users who have entered a code after 1st Jan 2011, but to only include in the count codes entered after 1st Jan 2013. How would I do this?
EDIT : So this would give me all users who have entered a code after 01/01/2011, but only include codes entered after 01/01/2013 in the count?
select a.userid, count(CASE WHEN ce.entrydate > '2013-01-01 00:00:00' THEN 1 ELSE 0 END)
from [account] a
inner join [profile] p
on a.userid = p.userid
inner join codesentered ce
on a.userid = ce.userid and ce.entrydate > '2011-01-01 00:00:00'
where a.camp = 0
group by a.userid
order by a.userid
Remove the date condition from the ON clause, and use this in the SELECT clause instead of COUNT(ce.codeentryid):
SUM(CASE WHEN ce.entrydate > '2011-01-01 00:00:00' THEN 1 ELSE 0 END)
Your question doesn't make sense, because using two dates is redundant. Unless I assume that you want users whose first count is after 2011-01-01 and then only count what happens after 2013-01-01.
If that is what you want, then use a having clause:
select a.userid, sum(CASE WHEN ce.entrydate > '2013-01-01 00:00:00' THEN 1 ELSE 0 END)
from [account] a inner join
[profile] p
on a.userid = p.userid inner join
codesentered ce
on a.userid = ce.userid
where a.camp = 0
group by a.userid
having min(ce.entrydate) > '2011-01-01 00:00:00'
order by a.userid;
Note that count(CASE WHEN ce.entrydate > '2013-01-01 00:00:00' THEN 1 ELSE 0 END) is the same as count(*). count() counts non-null values. Use sum() instead.

SQL Multiple CASE statements return 0 instead of NULL

I Have the following SQL Case statement which works perfectly as long as the Years match MF.Date & M.MemberCurrentYear. There will be scenarios where the years do not match and this returns a Balance of NULL. I would like it to return a Zero.
SELECT SUM(CASE WHEN Type = 1 THEN Amount ELSE Amount * - 1 END) AS Balance
FROM dbo.MemberFinancials AS MF
INNER JOIN dbo.Members AS M ON MF.MemberID = M.MemberID
AND DATEPART(yyyy, MF.Date) = M.MemberCurrentYear
INNER JOIN dbo.FinancialTypes AS FT ON MF.FinancialTypeID = FT.FinancialTypeID
Thanks
SELECT
ISNULL(SUM(CASE WHEN Type = 1 THEN Amount ELSE Amount * - 1 END),0) AS Balance
FROM dbo.MemberFinancials AS MF INNER JOIN
dbo.Members AS M ON MF.MemberID = M.MemberID AND DATEPART(yyyy, MF.Date) = M.MemberCurrentYear INNER JOIN
dbo.FinancialTypes AS FT ON MF.FinancialTypeID = FT.FinancialTypeID
Use ISNULL (Or COALESCE if you prefer...)
SELECT ISNULL( SUM(CASE WHEN Type = 1 THEN Amount ELSE Amount * - 1 END), 0) AS Balance
SELECT
COALESCE(
SUM(CASE WHEN Type = 1 THEN Amount ELSE Amount * - 1 END)
, 0) AS Balance