Complement of a SQL Join? T-SQL help needed - sql

I will try to make my question sound as unconfusing as possible. I appologize in advance for any wording mistakes as I try to phrase my question as best as I can:
Using T-SQL I need to write a join statement that gets me all the results that have a match in table A and table B
AND (!)
another join statement (or a continuation of the first join) that returns all the results from table A that DID NOT have a match in table B, BUT in this second result set I need to have one of the columns set to "N/A" to identify the records that didn't have a match.
In other words, I need something that would return everything in table A but would also identify the rows that weren't matched in B. That information is then used in a report.
Here is what I have so far:
I have the first part done:
LEFT OUTER JOIN dbo.chart B
ON B.UserName = A.user_name
That gets me the matching records and just the matching records
I tried adding this second join:
JOIN dbo.chart
ON NOT EXISTS (select * from B.UserName = A.user_name)
Hoping it would get me the non-matching records (I was planning to then use REPLACE on the column of interest to label that column "N/A") but there is something clearly wrong with my synthax as that generates exceptions.
My question is what do I need to change to get me the results you need. I do know that I need to have at least one join as I have other part of the query to work with. I just don't know if I need to have that one join return both sets of data of I actually do need a second one just for the non-matching records.
Hope this wasn't too confusing. Any help would be greatly appreciated.
Thank you!
Update: I would just like to emphasize that the reason I considered using a second join instead of getting all of the results at once is because I need to correctly identify and label those rows that weren't matched within everything that I get back.

I'm not sure where you are going with the second join. LEFT JOIN seems to do everything you want...
DECLARE #tableA TABLE (a_id INT) INSERT INTO #tableA VALUES (1), (2), (3), (4)
DECLARE #tableB TABLE (b_id INT) INSERT INTO #tableB VALUES (2), (3)
SELECT * FROM #tableA AS A LEFT JOIN #tableB AS B on A.a_id = b.b_id
a_id | b_id
------+------
1 | NULL
2 | 2
3 | 3
4 | NULL
Unless you mean that you're actually joining 3 tables together?
DECLARE #org TABLE (o_io INT) INSERT INTO #org VALUE (2), (3), (4)
DECLARE #tableA TABLE (a_id INT) INSERT INTO #tableA VALUES (1), (2), (3), (4)
DECLARE #tableB TABLE (b_id INT) INSERT INTO #tableB VALUES (2), (3)
SELECT
*
FROM
#org AS O
INNER JOIN
#tableA AS A
ON O.o_id = A.a_id
LEFT JOIN
#tableB AS B
ON A.a_id = b.b_id
o_id | a_id | b_id
------+------+------
2 | 2 | 2
3 | 3 | 3
4 | 4 | NULL

Sample data:
declare #TableA table
(
TableAID int,
TableAName varchar(10)
)
declare #TableB table
(
TableBID int,
TableBName varchar(10),
TableAID int
)
insert into #TableA values
(1, 'A 1'),
(2, 'A 2'),
(3, 'A 3')
insert into #TableB values
(1, 'B 1', 1),
(2, 'B 2', 2)
N/A instead of TableBName:
select A.TableAName,
coalesce(B.TableBName, 'N/A') as TableBName
from #TableA as A
left outer join #TableB as B
on A.TableAID = B.TableAID
Result:
TableAName TableBName
---------- ----------
A 1 B 1
A 2 B 2
A 3 N/A
Extra column for N/A:
select A.TableAName,
B.TableBName,
case when B.TableBID is null
then 'N/A'
else ''
end as TableBPresent
from #TableA as A
left outer join #TableB as B
on A.TableAID = B.TableAID
Result:
TableAName TableBName TableBPresent
---------- ---------- -------------
A 1 B 1
A 2 B 2
A 3 NULL N/A

Try using a union:
select A.id, b.id, b.desc from tablea A LEFT OUTER JOIN dbo.chart B
ON B.UserName = A.user_name
UNION
select a.id, 0, 'N/A' from tablea where NOT EXISTS (select * from B.UserName = A.user_name)

Related

is there a syntactic shortcut similar to coalesce for union?

What I'm trying to do is:
select
A.Fuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
To replace:
select
A.Fuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
UNION ALL
select
B.wuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
May be this is something "near" what you think (done on MSSQL)?. Pls in every question post sample data, expected result, etc.
CREATE TABLE A (ID INT, DESC_A VARCHAR(10));
INSERT INTO A VALUES (1,'A');
INSERT INTO A VALUES (2,'B');
CREATE TABLE B (ID INT, DESC_B VARCHAR(10));
INSERT INTO B VALUES (2,'Z');
INSERT INTO B VALUES (3,'Y');
SELECT COALESCE(A.ID, B.ID) AS ID
, A.DESC_A
, B.DESC_B
FROM A
FULL JOIN B ON A.ID = B.ID
Output:
ID DESC_A DESC_B
1 A NULL
2 B Z
3 NULL Y

Is there a way of this query working with JOINs or must I use a UNION?

I am probably trying to use JOINs for purposes they were not intended here.
Here's my (simplified) table structure:
Table A
ID
Table C ID
IsStatic (bit)
Table B
ID
Table A ID (nullable)
Table C ID
Table C
ID
My goal is to get all of Table B rows joined to Table A rows where Table B's Table A ID column has a value and equals Table A's ID column value.
I also need all of Table B rows where Table B's Table A ID column has no value.
I also need all of Table A rows with there were no joined Table B rows and Table A's IsStatic column is true.
Table C must also be associated with Table A or Table B. If Table B does not have a value for TableAID then it's value for TableCID should equal TableC's ID value. Otherwise TableA's TableCID should equal TableC's ID value.
Here's some SQL to create some TABLE variables and populate with sample data:
DECLARE #TableA TABLE (TableAID int, TableCID int, IsStatic bit)
DECLARE #TableB TABLE (TableBID int, TableAID int, TableCID int)
DECLARE #TableC TABLE (TableCID int)
INSERT INTO #TableC (TableCID) VALUES (1)
INSERT INTO #TableC (TableCID) VALUES (2)
INSERT INTO #TableA (TableAID, TableCID, IsStatic) VALUES (1, 1, 0)
INSERT INTO #TableA (TableAID, TableCID, IsStatic) VALUES (2, 2, 1)
INSERT INTO #TableA (TableAID, TableCID, IsStatic) VALUES (3, 2, 1)
INSERT INTO #TableA (TableAID, TableCID, IsStatic) VALUES (4, 2, 0)
INSERT INTO #TableB (TableBID, TableAID, TableCID) VALUES (1, NULL, 1)
INSERT INTO #TableB (TableBID, TableAID, TableCID) VALUES (2, 1, 1)
INSERT INTO #TableB (TableBID, TableAID, TableCID) VALUES (3, 2, 2)
Here's my (simplified) query that didn't quite work:
SELECT
a.TableAID,
b.TableBID
FROM #TableC c
LEFT OUTER JOIN #TableB b ON
(b.TableAID IS NOT NULL OR (b.TableAID IS NULL AND b.TableCID = c.TableCID))
LEFT OUTER JOIN #TableA a ON
a.TableCID = c.TableCID
AND ((a.IsStatic = 1 AND b.TableBID IS NULL)
OR (b.TableBID IS NOT NULL AND b.TableAID = a.TableAID))
The result of this query using the sampel data is:
TableAID TableBID
-----------------
NULL 1
1 2
NULL 3 (not required)
NULL 2 (not required)
2 3
The required result is:
TableAID TableBID
-----------------
NULL 1
3 NULL (missing)
2 3
1 2
Problem with this query is that if TableB.TableAID has no value then the Table A rows where TableA.IsStatic is true without any matching TableB rows are never included. Also some TableB rows are being included and they shouldn't be.
The only other way I can see of doing this is with a union with a not exists but I was hoping to do this in a more efficient way.
Update: Adding a WHERE clause removes the "not required" rows but still omits the missing row.
WHERE (b.TableBID IS NULL OR b.TableAID IS NULL OR b.TableAID = a.TableAID)
The result of the same query with the where clause is:
TableAID TableBID
-----------------
NULL 1
1 2
2 3
select b.TableAID, b.TableBID
from #TableB b
left join #TableA a on a.TableAID = b.TableAID
inner join #TableC c on c.TableCID = case when a.TableAID IS NULL then b.TableCID else a.TableCID end
union all
select a.TableAID, NULL
from #TableA a
inner join #TableC c on c.TableCID = a.TableCID
left join #TableB b on b.TableAID = a.TableAID
where b.TableAID is NULL
and a.IsStatic = 1
What a mind twister. I think that this is another way to express it. You'll have to see if the performance is good or not:
select a.TableAID, b.TableBID
from (select a.*
from #TableA a
join #TableC c
on c.TableCID = a.TableCID) a
full outer join (select b.*
from #TableB b
join #TableC c
on c.TableCID = b.TableCID) b
on b.TableAID = a.TableAID
where b.TableBID is not null or a.IsStatic = 1
I should also mention that it's hard to know for sure if the above query really respects your requirements using the sample data you provided. To illustrate, if I use this simplified query below that simply ignores the #TableC table, I still get the right results with your sample data:
select a.TableAID, b.TableBID
from #TableA a
full outer join #TableB b
on b.TableAID = a.TableAID
where b.TableBID is not null or a.IsStatic = 1
EDIT: Funny discussion in the comments about the interpretation of OP' requirements... But if I had to address Anton's point:
select a.TableAID, b.TableBID
from (select a.*,
case when c.TableCID is not null then 1 end as has_c
from #TableA a
left join #TableC c
on c.TableCID = a.TableCID) a
full outer join (select b.*,
case when c.TableCID is not null then 1 end as has_c
from #TableB b
left join #TableC c
on c.TableCID = b.TableCID) b
on b.TableAID = a.TableAID
where (b.TableBID is not null or a.IsStatic = 1)
and (a.has_c = 1 or b.has_c = 1)

Not in or Not exist Query Very Slow for Large Data Sybase

I have a table A which is having around 50000 records and a table B which is having 50000 records as well.
sample data:
A B
1 1
2 2
3 null
4 null
I want to find records 3, 4 which are present in Table A but not in Table B.
I am using
select id from A where id NOT IN(select id from B)
I have also tried NOT Exist, but as the records are very large in number, it still takes a lot of time.
select id from A where NOT Exists(select id from B and B.id = A.id)
Left Outer Join cant be used to find the missing records as the id is not present in Table B.
Is there any way to make the Query Work Faster in Sybase itself?
Or Shifting the database to MongoDB is the solution?
I'm not sure why you are not prepare LEFT JOIN, I tried with the LEFT JOIN it returns your expected result.
Sample execution with the given data:
DECLARE #TableA TABLE (Id INT);
DECLARE #TableB TABLE (Id INT);
INSERT INTO #TableA (Id) VALUES (1), (2), (3), (4);
INSERT INTO #TableB (Id) VALUES (1), (2), (NULL), (NULL);
SELECT T1.Id
FROM #TableA T1
LEFT JOIN #TableB T2 ON T2.Id = T1.Id
WHERE T2.Id IS NULL
Result
3
4
In performance perspective, always try to avoid using inverse keywords like NOT IN, NOT EXISTS. Because to check the inverse items DBMS need to runs through all the available records and drop the inverse selection.
LEFT JOIN / IS NULL and NOT EXISTS are semantically equivalent, while NOT IN is not. These method differ in how they handle NULL values in table_right.
Therefore, You should go for LEFT JOIN to improve your sql performance.
select A.id from A LEFT JOIN B
on A.id = B.id
where B.id is null
order by A.id;

Select null on a join where a record doesn't exist in another table

I've got a problem that I can't seem to figure out after a bunch of failed attempts.
I've got three tables that I need to do a join on for some reporting, and in the 2nd table a record might not exist. If the record doesn't exist, I need to report a null value for the data that comes from the 3rd table.
In the most basic form, here are the table structures (it's for a survey)
Table A (Survey)
-----------------------
SurveyNumber | SurveyId
016243023708 1152010
Table B (Response)
----------------------------------
SurveyId | QuestionId | ResponseId
1152010 1279235 486
Table C (Response Values)
--------------------
ResponseId | Value
486 Yes
To explain why a record may not exist in Table B is simply because the values are inserted as the survey is completed. If the user leaves the survey without finishing (they can come back later), the record in Table B won't be there. The value in table C should be reported as null for this.
If it makes it any easier, I need to do the reporting specifically for questionid 1279235.
This is the query I've come up with so far (it shows me everything but the surveys with a missing record in table b for question 1279235).
SELECT A.SurveyNumber, A.SurveyId, B.QuestionID, C.Value
FROM tblA A
LEFT JOIN tblB B
ON A.SurveyId = B.SurveyId
LEFT JOIN tblC C
ON B.ResponseId = C.ResponseId
WHERE B.QuestionId = 1279235
I can provide more clarification if it is needed.
Thanks in advance
do not put the condition in the where clause, but instead on the join part since records on tablec may not exist.
SELECT A.SurveyNumber, A.SurveyId, B.QuestionID, C.Value
FROM tblA A
LEFT JOIN tblB B
ON A.SurveyNumber = B.SurveyNumber AND
B.QuestionId = 1279235
LEFT JOIN tblC C
ON B.ResponseId = C.ResponseId
try this:
SELECT
A.SurveyNumber, A.SurveyId, B.QuestionID, C.Value
FROM tblA A
LEFT JOIN tblB B ON A.SurveyId=B.SurveyId AND B.QuestionId=1279235
LEFT JOIN tblC C ON B.ResponseId=C.ResponseId
EDIT working example:
DECLARE #tblA table (SurveyNumber varchar(12),SurveyId int)
INSERT INTO #tblA VALUES ('016243023708', 1152010)
INSERT INTO #tblA VALUES ('016243023708', 1152011)
DECLARE #tblB table (SurveyId int, QuestionId int, ResponseId int)
INSERT INTO #tblB values (1152010, 1279235, 486)
INSERT INTO #tblB values (1152011, 1279235, 487)
DECLARE #tblC table (ResponseId int, ValueOf varchar(10))
INSERT INTO #tblC values (486, 'Yes')
SELECT
A.SurveyNumber, A.SurveyId, B.QuestionID, C.ValueOf
FROM #tblA A
LEFT JOIN #tblB B ON A.SurveyId=B.SurveyId AND B.QuestionId=1279235
LEFT JOIN #tblC C ON B.ResponseId=C.ResponseId
OUTPUT:
SurveyNumber SurveyId QuestionID ValueOf
------------ ----------- ----------- ----------
016243023708 1152010 1279235 Yes
016243023708 1152011 1279235 NULL
(2 row(s) affected)

Alternative to using subqueries in SQL statements?

I have two tables:
TableA: (a temporary table)
ItemId (int)
TableB:
ItemId (int), ParentID (int)
I want to retrieve all items in Table A where the ParentID of any of the items in Table A doesn't exist as an ItemID. (i.e. I want to get the root of the items in TableA)
This query does what I want:
SELECT a.ItemID
FROM TableA a
INNER JOIN TableB b ON a.ItemId = b.ItemID
WHERE b.ParentID NOT IN ( SELECT * from TableA )
as does this one:
SELECT b.ItemID
FROM TableB b
WHERE b.ItemID IN ( SELECT * FROM TableA)
AND b.ParentID NOT IN ( SELECT * FROM TableA )
I am not satisfied with either of the queries, particularly because of the use of NOT IN/IN. Is there a way to do this without them? Perhaps a cleaner way that doesn't require subqueries?
Sample Data:
Table A
-------
2
3
5
6
Table B
--------
1 | NULL
2 | 1
3 | 1
4 | 3
5 | 3
6 | 3
Desired Result:
2
3
Thanks
Without subqueries:
SELECT ItemID
FROM TableA
INTERSECT
SELECT b.ItemID
FROM TableB AS b
LEFT OUTER JOIN TableA AS a
ON b.ParentID = a.ItemID
WHERE a.ItemID IS NULL;
...but is your fear of subqueries rational? :) I'd find this equivalent query easier to read and understand:
SELECT ItemID
FROM TableA
INTERSECT
SELECT ItemID
FROM TableB
WHERE NOT EXISTS (
SELECT *
FROM TableA AS a
WHERE a.ItemID = TableB.ParentID
);
Take a look at Select all rows from one table that don't exist in another table to see 5 different ways to do this kind of query by using
NOT IN
NOT EXISTS
LEFT and RIGHT JOIN
OUTER APPLY (2005+)
EXCEPT (2005+)
Here is a script that you can run
CREATE TABLE #TableA( ItemId int)
INSERT #TableA values(1)
INSERT #TableA values(2)
INSERT #TableA values(3)
INSERT #TableA values(4)
INSERT #TableA values(5)
INSERT #TableA values(6)
CREATE TABLE #TableB( ItemId int, ParentID int)
INSERT #TableB values(1,1)
INSERT #TableB values(2,2)
INSERT #TableB values(4,3)
INSERT #TableB values(5,4)
this will do it for parent
SELECT a.ItemID
FROM #TableA a
LEFT JOIN #TableB b ON a.ItemId = b.ParentID
WHERE b.ItemID IS NULL
SELECT a.ItemID
FROM #TableA a
WHERE NOT EXISTS (SELECT 1 FROM #TableB b WHERE a.ItemId = b.ParentID)
Output
ItemID
5
6
You can use outer joins. Something like this:
SELECT a.ItemID
FROM TableA a
INNER JOIN TableB b ON a.ItemId = b.ItemID
LEFT JOIN TableB parentB on a.ItemID = parentB.ParentID
WHERE parentB.ParentID IS NULL
Your Tables A and B seem to store a tree structure. I'd interpret table A as "Nodes" (storing elements of the tree) and Table B as "Edges" (linking a node to it's parent). The inner join variant is very elegant since it covers all cases of "no edge to parent", "edge to PrantID null" and "edge to non existant parent" at once.
cheers