Why is my query inside of a case when failing? - sql-server

I'm trying to run a SQL Server Case query and I'm running into an error. Let me try to explain why I'm doing what I'm doing, as well as what I'm looking for in the end result.
I have two Tables. 1 that has an opportunity ID (in this case A.ID) and the second table has accounts with one column I'm interested in (B.Q3ABM__C). What I'm asking SQL to do in this case when is that for every A.id that is null in in the B.Q3ABM__C colum give me a 1, and then again vice versa.
Why is this failing?
SELECT B.ID as Account_ID, B.FULL_ACCOUNT_ID__C, A.ID as Opportunity_ID,A.name, stagename,closedate,A.createddate,
OPPORTUNITY_PRODUCT__C,AMOUNT, B.Q3ABM__C, B.Drip_Campaign_Code__c,OPPORTUNITYTYPE_STRING__C,
CASE
WHEN (SELECT A.id FROM SF_OPPORTUNITY as a
LEFT JOIN SF_ACCOUNT as b on A.Accountid = b.ID where B.Q3ABM__C IS NULL) THEN 1
ELSE 0
END AS 'Does Not Exist in Q3DM',
CASE
WHEN (SELECT A.id FROM SF_OPPORTUNITY as a
LEFT JOIN SF_ACCOUNT as b on A.Accountid = b.ID where B.Q3ABM__C IS NOT NULL) THEN 1
ELSE 0
END AS 'Exists in Q3DM'
FROM SF_OPPORTUNITY as a
LEFT JOIN SF_ACCOUNT as b on a.ACCOUNTID = b.ID
WHERE A.createddate >= '7-1-2019'
AND CLOSEDATE <= '12-31-2019'
Thanks!

If you are selecting records in specific period, but you want to check for NULLs and NOT NULLs for all records, you can check for which ID there are NULL values. If NULL vales exists, then we can calculate the first Does Not Exist in Q3DM column, if not exists the second Exists in Q3DM:
WITH DataSource AS
(
SELECT DISTINCT A.id
FROM SF_OPPORTUNITY as a
LEFT JOIN SF_ACCOUNT as b
on A.Accountid = b.ID
where B.Q3ABM__C IS NULL
)
SELECT B.ID as Account_ID
,B.FULL_ACCOUNT_ID__C
,A.ID as Opportunity_ID
,A.name
,stagename
,closedate
,A.createddate
,OPPORTUNITY_PRODUCT__C
,AMOUNT
,B.Q3ABM__C
,B.Drip_Campaign_Code__c
,OPPORTUNITYTYPE_STRING__C
,CASE DS.Id IS NOT NULL THEN 1 ELSE 0 END 'Does Not Exist in Q3DM'
,CASE DS.Id IS NULL THEN 1 ELSE 0 END 'Exists in Q3DM'
FROM SF_OPPORTUNITY as a
LEFT JOIN SF_ACCOUNT as b
on a.ACCOUNTID = b.ID
LEFT JOIN DataSource DS
ON A.ACCOUNTID = DS.ID
WHERE A.createddate >= '7-1-2019'
AND CLOSEDATE <= '12-31-2019';
If you are interested only in the for the given period and checking the value for the current record only:
SELECT B.ID as Account_ID
,B.FULL_ACCOUNT_ID__C
,A.ID as Opportunity_ID
,A.name
,stagename
,closedate
,A.createddate
,OPPORTUNITY_PRODUCT__C
,AMOUNT
,B.Q3ABM__C
,B.Drip_Campaign_Code__c
,OPPORTUNITYTYPE_STRING__C
,CASE DS.Id IS NULL THEN 1 ELSE 0 END 'Does Not Exist in Q3DM'
,CASE DS.Id IS NOT NULL THEN 1 ELSE 0 END 'Exists in Q3DM'
FROM SF_OPPORTUNITY as a
LEFT JOIN SF_ACCOUNT as b
on a.ACCOUNTID = b.ID
LEFT JOIN DataSource DS
ON A.ACCOUNTID = DS.ID
WHERE A.createddate >= '7-1-2019'
AND CLOSEDATE <= '12-31-2019'

Try this (Use column in 'Select' and 'Where' clause as per your requirements):
SELECT A.*,B.* ,
CASE WHEN B.Q3ABM__C IS NULL THEN 1 ELSE 0 END AS 'DOES NOT EXISIT IN Q3DM',
CASE WHEN B.Q3ABM__C IS NOT NULL THEN 1 ELSE 0 END AS 'EXISITS IN Q3DM'
FROM PLS.SF_OPPORTUNITY A
FULL JOIN PLS.SF_ACCOUNT B ON A.ACCOUNTID=B.ID

Related

SQL Server : left join - check for right table column value is null

I have a query as follows:
select --this select should always give me 1 record
tbl1.Id, tbl1.Name, tbl1.Address, tbl2.relNo,
CASE WHEN tbl3.Comments IS NOT NULL THEN 1 ELSE 0 END AS 'Required'
from
table1 tbl1
inner join
table2 tbl2 on tbl2.Id = tbl1.Id
left join -- This left join table gives me 5 records for one instance
(select
R.Id, C.Comments
from
tblC C
inner join
tblR R on R.Id = C.id) tbl3 on tbl3.Id = tbl2.Id
I want to write a CASE statement on the rows my left join is giving to check for null value as above and my final select query always return only 1 row. Is there a way to check if all five Comments Column values from my left join be checked for NULLs in the above query?
I would take a shortcut an use a COUNT() OVER PARTITION
CASE WHEN COUNT(*) OVER (PARTITION BY tbl3.Id) =0 THEN 0 ELSE 1 END AS 'Required'
You would have to DISTINCT your output above. Another option would be to GROUP BY and filter in the HAVING clause.
select --this select should always give me 1 record
tbl1.Id, tbl1.Name, tbl1.Address, tbl2.relNo
From table1 tbl1
inner join table2 tbl2 on tbl2.Id = tbl1.Id
left join (-- This left join table gives me 5 records for one instance
SELECT R.Id,
C.Comments
FROM tblC C
INNER JOIN tblR R on R.Id = C.id
) tbl3 on tbl3.Id = tbl2.Id
GROUP BY
Id, Name, Address, relNo
HAVING
COUNT(*) = 5
Is this what you're looking for?
(CASE WHEN (select count(tbl3.id) FROM tbl3 WHERE tbl3.Comments IS NULL) then 1 else 0 end) as 'RequiredVal'
select --this select should always give me 1 record
tbl1.Id, tbl1.Name, tbl1.Address, tbl2.relNo,
CASE WHEN tbl3.Comments IS NOT NULL THEN 1 ELSE 0 END AS 'Required'
, (CASE WHEN (select count(tbl3.id) FROM tbl3 WHERE tbl3.Comments IS NULL) then 1 else 0 end) as 'RequiredVal'
From table1 tbl1
inner join table2 tbl2 on tbl2.Id = tbl1.Id
left join (-- This left join table gives me 5 records for one instance
SELECT R.Id,
C.Comments
FROM tblC C
INNER JOIN tblR R on R.Id = C.id
) tbl3 on tbl3.Id = tbl2.Id

How to compare data any field of two table in SQL Server?

I have two tables with same columns.
Table A have ID, Name, Des, Status and
Table B have ID, Name, Des, Status.
I want to compare data any field of Table B with Table A, except column ID because same.
As same picture above, when FETCH data of Table B, detect data of ID ID001 and ID003 not same, idea of my mind same
IF (SELECT COUNT (SELECT * FROM TABLE A RIGHT JOIN TABLE B ON A.ID = B.ID) != 0)
BEGIN
PRINT 'BLAH BLAH, NOT SAME'
END
If you have idea or solution, share for me, Thank you so much.
You can use CHECKSUM or BINARY_CHECKSUM:
SELECT a.*, b.*
FROM TableA a INNER JOIN TableB b ON b.ID = a.ID
WHERE CHECKSUM(b.Name, b.Des, b.Status) <> CHECKSUM(a.Name, a.Des, a.Status)
See also this link. It should be faster then multiple OR conditions.
IF (SELECT COUNT(*)
FROM TableA a INNER JOIN TableB b ON b.ID = a.ID
WHERE BINARY_CHECKSUM(b.Name, b.Des, b.Status)
<> BINARY_CHECKSUM(a.Name, a.Des, a.Status)
>0
PRINT 'Not the same.'
Since it is not too much clear how do you want to show your differences, this is one approach:
SELECT A.ID,
(CASE WHEN A.Name <> B.Name THEN 'Diff Name' ELSE '') NameCompare,
(CASE WHEN A.Des <> B.Des THEN 'Diff Des' ELSE '') DESCompare,
(CASE WHEN A.Status <> B.Status THEN 'Diff Status' ELSE '') StatusCompare
FROM A
INNER JOIN B
ON A.ID = B.ID
This is a simple join
To get all different rows, you can say:
select a.*, b.*
from TableA a inner join TableB b on b.ID = a.ID
where b.Name <> a.Name or b.Des <> a.Des or b.Status <> a.Status

T-SQL Aggregate function subquery

I get the following error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
With this code:
SELECT
loc.Location
,COUNT(CASE
WHEN hr.SAC in (SELECT [SAC] FROM dbo.[Titles] WHERE [title] = 'XYZ')
THEN 1
ELSE NULL
END) AS XYZ_Trainee_Count
,COUNT(CASE
WHEN hr.SAC in (SELECT [SAC] FROM dbo.[Titles] WHERE [title] = 'ABC')
THEN 1
ELSE NULL
END) AS ABC_Trainee_Count
FROM
dbo.n_HRODS hr INNER JOIN dbo.Locations loc
ON loc.LocationID = hr.LocationID
INNER JOIN dbo.EmpData dat
ON dat.EmpID = hr.EmpID
WHERE dat.Trainee = 1
GROUP BY loc.Location
dbo.[Titles] is a view that combines two columns from two other tables. I'm basically doing it this way because the programmer before me did something like this:
,COUNT(CASE
WHEN SAC in ( lists about 30 items)
THEN 1
ELSE NULL
END)
Obviously, I don't want to list 30 items in that case statement... and when those items change for whatever reason in 3 years, then who's going to remember to go back in this code and updated those items? Nobody...
Thanks in advance for your help.
You can do this with a couple of extra LEFT OUTER JOINs to that title table:
SELECT
loc.Location
,COUNT(CASE WHEN titles1.[SAC] IS NOT NULL THEN 1 ELSE NULL END) AS XYZ_Trainee_Count
,COUNT(CASE WHEN titles2.[SAC] IS NOT NULL THEN 1 ELSE NULL END) AS ABC_Trainee_Count
FROM
dbo.n_HRODS hr INNER JOIN dbo.Locations loc
ON loc.LocationID = hr.LocationID
INNER JOIN dbo.EmpData dat
ON dat.EmpID = hr.EmpID
LEFT OUTER JOIN dbo.[Titles] titles1
ON titles1.[title]='XYZ' AND
hr.SAC = titles1.[SAC]
LEFT OUTER JOIN dbo.[Titles] titles2
ON titles2.[title]='ABC' AND
hr.sac = titles2.[SAC]
WHERE dat.Trainee = 1
GROUP BY loc.Location
Alternatively, if you are really married to those subqueries in your SELECT statement because the actual query is a big nightmare and the thought of monkeying with the joins is enough to make you faint, then you can just remove the aggregation from this query and shove it all into a subquery before aggregation:
SELECT location, count(XYZ_Trainee) AS XYZ_Trainee_Count, count(ABC_Trainee) as ABC_Trainee
FROM
(
SELECT
loc.Location
,CASE
WHEN hr.SAC in (SELECT [SAC] FROM dbo.[Titles] WHERE [title] = 'XYZ')
THEN 1
ELSE NULL
END AS XYZ_Trainee
,CASE
WHEN hr.SAC in (SELECT [SAC] FROM dbo.[Titles] WHERE [title] = 'ABC')
THEN 1
ELSE NULL
END AS ABC_Trainee
FROM
dbo.n_HRODS hr INNER JOIN dbo.Locations loc
ON loc.LocationID = hr.LocationID
INNER JOIN dbo.EmpData dat
ON dat.EmpID = hr.EmpID
WHERE dat.Trainee = 1
) sub
GROUP BY location
I would aim for the first solution though since it's going to be easier to maintain and probably get a better execution path from your RDBMS and run quicker as a result. Although... that's just a guess.
Very similar to JNevill first answer. But if you join with title once, you can check and count for any number of title you want.
SELECT
loc.Location
,COUNT(CASE WHEN t.[title] = 'XYZ' THEN 1 END) AS XYZ_Trainee_Count
,COUNT(CASE WHEN t.[title] = 'ABC' THEN 1 END) AS ABC_Trainee_Count
FROM
dbo.[n_HRODS] hr
INNER JOIN dbo.[Locations] loc
ON loc.LocationID = hr.LocationID
INNER JOIN dbo.[EmpData] dat
ON dat.EmpID = hr.EmpID
INNER JOIN dbo.[Titles] t
ON hr.SAC = t.[SAC]
WHERE dat.Trainee = 1
GROUP BY loc.Location

Finding records in another table

I wanted to show that a record in one table exists in another table and used the code below. Would this be possible?
CASE WHEN EXISTS (
SELECT *
FROM tblA a
INNER JOIN tblB b ON a.AccountID = b.AccountID
) THEN 'YES' ELSE 'NO' END
As Dave Gugg mentioned in his comment that is possible. The alternative is using a LEFT JOIN and for those rows that are not matched in table B display NO, for those that match display YES:
SELECT a.*, CASE WHEN b.AccountID IS NOT NULL THEN 'YES' ELSE 'NO' END as YesNoCol
FROM tblA a
LEFT JOIN tblB b ON a.AccountID = b.AccountID
Nope. Do something like this instead:
SELECT CASE WHEN (SELECT COUNT(*)
FROM tblA AS A
INNER JOIN tblB AS B
ON A.AccountID = B.AccountID) > 1
THEN 'YES' ELSE 'NO' END AS T;

LEFT JOIN EXISTS

I have two tables, Main and Details - they have a one-to-many relationship, with one row in Main potentially having multiple rows in Details.
I am trying to create a query that returns all the information from Main, plus the whether the associated rows in Details contain one of a set of codes. Of course, since it's a one-to-many, there may be several of those codes present in Details - and I don't want to double count rows. I need to do it a couple of times over, too.
What I kind of want is something like this:
SELECT m.*, CASE WHEN x.ID IS NOT NULL THEN 1 ELSE 0 END AS Codes1, CASE WHEN y.ID IS NOT NULL THEN 1 ELSE 0 END AS Codes2
FROM [Main] m
LEFT JOIN EXISTS(SELECT d.ID FROM [Details] d WHERE m.ID = d.ID AND d.Code IN (<<Codes1>>)) x
LEFT JOIN EXISTS(SELECT d.ID FROM [Details] d WHERE m.ID = d.ID AND d.Code IN (<<Codes2>>)) y
Is there some way to do this? (This seems something that should be obvious and I'm overcomplicating to the nth degree, but I'm genuinely drawing a blank...
Remove the EXISTS from your query and add a DISTINCT
SELECT DISTINCT
m.*,
CASE WHEN x.ID IS NOT NULL THEN 1 ELSE 0 END AS Codes1,
CASE WHEN y.ID IS NOT NULL THEN 1 ELSE 0 END AS Codes2
FROM [Main] m
LEFT JOIN (SELECT d.ID FROM [Details] d WHERE m.ID = d.ID AND d.Code IN (<<Codes1>>)) x
LEFT JOIN (SELECT d.ID FROM [Details] d WHERE m.ID = d.ID AND d.Code IN (<<Codes2>>)) y
I think this will work:
SELECT M.*,
CASE WHEN C1.Codes IS NULL THEN 0 ELSE 1 END as Codes1,
CASE WHEN C2.Codes IS NULL THEN 0 ELSE 1 END as Codes2
FROM Main M
LEFT
JOIN
(
SELECT d.Id,
COUNT(d.Code) as Codes
FROM Details d
WHERE d.Code in (<<Codes1>>)
GROUP
BY d.Id
) C1
ON C1.Id = M.Id
LEFT
JOIN
(
SELECT d.Id,
COUNT(d.Code) as Codes
FROM Details d
WHERE d.Code in (<<Codes2>>)
GROUP
BY d.Id
) C2
ON C2.Id = M.Id
If you don't need the M.* then this might be better:
SELECT M.Id,
SUM(CASE WHEN D1.Id IS NULL THEN 0 ELSE 1 END) AS Codes1Count,
SUM(CASE WHEN D2.Id IS NULL THEN 0 ELSE 1 END) AS Codes2Count
FROM Main M
LEFT
JOIN Details D1
ON D1.Id = M.Id
AND D1.Code in (<<Codes1>>)
LEFT
JOIN Details D2
ON D2.Id = M.Id
AND D2.Code in (<<Codes2>>)
GROUP
BY M.Id
Here's a slim version of code that will get you what you want
SELECT abb1.*, abb2.mycount
FROM Main AS abb1
JOIN (SELECT Main.ID, COUNT(Details.ID) AS mycount
FROM Main
JOIN Details on Details.ID = Main.ID
GROUP BY Main.ID) AS abb2

Resources