does Left Join in SQL not shows matching value in second table? [duplicate] - sql

This question already has answers here:
Isn't SQL A left join B, just A?
(5 answers)
Closed 1 year ago.
I see below question and I believe it is false but the correct answer is true.
A left outer join on tables CUSTOMER and AGENT, with
CUSTOMER being listed first and AGENT being listed
second in the SQL command, yields all of the rows in the
CUSTOMER table, including those that do not have a
matching value in the AGENT table.
Based on above question, it should be below select:
select C.*, A.*
from Customer C
left join Agent A on A.CID = C.CID
So the result of the above Statement is all rows from Customer table + matching values in Customer and Agent table. (My Professor says the True/False question is correct but it seems false).

If you try it you will easily see that the statement quoted is TRUE. When no matching row exists in the agent table, the customer row is still shown e.g. Customer 2 below.
declare #Customer table (id int, [Name] varchar(32));
declare #Agent table (id int, CustomerId int, [Name] varchar(32));
insert into #Customer (id, [Name])
values
(1, 'Test Customer 1'),
(2, 'Test Customer 2');
insert into #Agent (id, CustomerId, [Name])
values
(1, 1, 'Test Agent 1');
select C.id [Customer id], C.[Name] [Customer Name], A.id [Agent id], A.CustomerId [Agent Customer id], A.[Name] [Agent Name]
from #Customer C
left join #Agent A on A.CustomerId = C.id;
Results
Customer id
Customer Name
Agent id
Agent Customer id
Agent Name
1
Test Customer 1
1
1
Test Agent 1
2
Test Customer 2
NULL
NULL
NULL
And as The Impaler points out, you can get duplicate Customer rows if there exist multiple Agents per customer e.g. if you append the following SQL to that which we already have:
insert into #Agent (id, CustomerId, [Name])
values
(2, 1, 'Test Agent 2');
You get
Customer id
Customer Name
Agent id
Agent Customer id
Agent Name
1
Test Customer 1
1
1
Test Agent 1
1
Test Customer 1
2
1
Test Agent 2
2
Test Customer 2
NULL
NULL
NULL

Related

SQL query - Return 0 for the column if no record found

I will like to see the specific fields returns with 0 value when there is no record found.
Below are my SQL queries that I have tried so far.
SELECT Customer, Address, Number of lines, Date
FROM table_name
WHERE Date = '30-5-2022' AND Customer IN (A, B, C)
What it returns is only 1 row as below.
Customer
Address
Number of Lines
Date
A
3
RF
30-5-2022
But what I expected to see is:
Customer
Address
Number of Lines
Date
A
UK
33
30-5-2022
B
0
0
30-5-2022
C
0
0
30-5-2022
The customer B and C has no record on 30-5-2022 but I will still need to see the rows but certain columns can be 0.
Please advise if anything that I have missed out? Thanks a lot!
Try below query:
SELECT A.Customer, ISNULL(B.Address, 0),
ISNULL(B.[Number of lines],0), ISNULL(B.Date, '30-05-2022') Date
FROM
(
SELECT DISTINCT Customer
FROM table_name
) A
LEFT JOIN table_name B
ON A.Customer = B.Customer
AND B.Date = '30-5-2022'
This will output all the customers present in the table. You can filter the customers with WHERE clause in the end of the above query based on your requirement.
dbfiddle Link
Assuming customers are in one table (cust) and other stuff in another table (table_name) and they are connected with an id called custid, you need some sweet, sweet left join action, such as:
SELECT c.Customer, c.Address, isnull([Number of lines],0), d.Date
FROM cust c left join table_name d on c.custid = d.custid and
d.Date = '30-5-2022' where
c.Customer IN ('A', 'B', 'C')
You need to put A, B, C in a table and left-join the main table to it. All other conditions must then go in the ON clause not the WHERE.
You can use a virtual VALUES clause for this
SELECT Customer, Address, Number of lines, Date
FROM (VALUES
('A'),
('B'),
('C')
) v(Customer)
LEFT JOIN table_name tn ON tn.Customer = v.Customer
AND tn.Date = '30-5-2022';
Alternatively you can pass in a Table Valued Parameter, or use a table variable. Don't forget to add a primary key.
DECLARE #tmp TABLE (Customer varchar(100) PRIMARY KEY);
INSERT #tmp(Customer) VALUES
('A'),
('B'),
('C');
SELECT Customer, Address, Number of lines, Date
FROM #tmp v
LEFT JOIN table_name tn ON tn.Customer = v.Customer
AND tn.Date = '30-5-2022';

How to use write a SQL join when there exists empty cell in the join column?

Schema:
CREATE TABLE PRODUCT
(
PRODUCTID INT,
PRODUCTNAME VARCHAR(100),
PRODUCTUSER VARCHAR(100)
);
CREATE TABLE USER
(
USERID INT,
USERNAME VARCHAR(100),
USEREMAIL VARCHAR(100)
);
INSERT INTO PRODUCT(PRODUCTID,PRODUCTNAME,PRODUCTUSER)
VALUES (1, 'Product1', 'Chen'), (2, 'Product2', 'Bob'),
(3, 'Product3', ''), (4, 'Product4', '');
INSERT INTO USER (USERID, USERNAME, USEREMAIL)
VALUES (1, 'Chen', 'chen#email.com'),
(2, 'Bob', 'bob#email.com'),
(3, 'Paul', 'paul#email.com'),
(4, '', ''), (5, '', '');
Product table:
ProductID ProductName ProductUser
------------------------------------
1 Product1 Chen
2 Product2 Bob
3 Product3
4 Product4
User table:
UserID UserName UserEmail
--------------------------------
1 Chen chen#email.com
2 Bob bob#email.com
3 Paul paul#email.com
4
5
I want to join Product and User table to get all the Product Names and the User name (if available) as output. The challenge is that the common field Product.ProductUser and User.UserName both contain empty values as shown in example above. I know this is not a good schema design but I am cannot change the schema as it is out of my control.
Expected output:
PROCUTNAME USERNAME
----------------------
Product1 Chen
Product2 Bob
Product3
Product4
Sample query 1:
SELECT PRODUCTNAME, USERNAME
FROM PRODUCT P
JOIN USER U
ON P.PRODUCTUSER=U.USERNAME
Above query is an inner join so returns duplicates for Product3 and Product4 due to the join on empty. Adding a WHERE clause ProductUser<>'' results in exclusion of Product3 and Product4.
Usually if the ProductUser were NULL instead of empty then I know that a LEFT JOIN would be the solution. I think that I understand the difference between using the criteria in the ON clause and WHERE clause of the LEFT JOIN.
So trying a left join with criteria in the ON clause:
SELECT PRODUCTNAME, USERNAME
FROM PRODUCT P
LEFT JOIN USER U
ON P.PRODUCTUSER=U.USERNAME AND P.PRODUCTUSER<>''
So the above query works by 1st doing an inner join based on the given criteria in ON clause, and then pulls in all other rows from the product table and puts a null for username. (or in other words it first lists all products, and then joins on those records that satisfy the ON criteria.)
This gives me output as expected. But I am not sure whether my approach is correct so trying another approach:
SELECT PRODUCTNAME, USERNAME
FROM PRODUCT P
LEFT JOIN (SELECT * FROM USER WHERE USERNAME<>'') U
ON P.PRODUCTUSER=U.USERNAME
This also works.
Is the left join with criteria in the ON clause a correct way to approach the problem?
Can you just do a group by to remove the duplicates?
declare #prod TABLE (PRODUCTID INT, PRODUCTNAME VARCHAR(100), PRODUCTUSER VARCHAR(100));
declare #user table (USERID INT, USERNAME VARCHAR(100),USEREMAIL VARCHAR(100));
INSERT INTO #prod(PRODUCTID,PRODUCTNAME,PRODUCTUSER) values (1,'Product1','Chen'),
(2,'Product2','Bob'),(3,'Product3',''),(4,'Product4','');
INSERT INTO #user(USERID,USERNAME,USEREMAIL) VALUES (1,'Chen','chen#email.com'),
(2,'Bob','bob#email.com'),(3,'Paul','paul#email.com'),
(4,'',''),(5,'','');
select * from #prod
select * from #user
select
p.ProductId,
p.ProductName,
u.USERNAME
FROM #prod p
left join #user u on p.PRODUCTUSER = u.USERNAME
group by
p.PRODUCTID,
p.PRODUCTNAME,
u.USERNAME
select
main.PRODUCTNAME
, case
when sub.USERNAME is null
then ''
else sub.USERNAME
end USERNAME
from #PRODUCT main
left join
#USER sub
on main.PRODUCTUSER = sub.USERNAME
and sub.USERNAME like '%[A-Za-z]%'

How to insert data in multiple rows of temp tables in sql

How I can insert in same row for example I want to insert all these columns data in first row then second and so on. But my query is inserting data when customer name data is complete, status data is inserted after one row of customer number last data.
CREATE TABLE #tblCustomer
(
CustomerNumber NVARCHAR(1000),
Status NVARCHAR (1000),
CustomerType NVARCHAR (1000)
)
INSERT
INTO #tblCustomer (CustomerNumber)
Select c.CustomerNumber
From Customer.Customer c
INSERT
INTO #tblCustomer (Status)
Select ses.Name
From Customer.Customer c
Left Outer Join COM.StatusEngine_EntityStatus sees
On c.Status = sees.EntityStatusId
And sees.EntityId = 'CustomerStatus'
Join COM.StatusEngine_Status ses
On sees.Status = ses.Status
INSERT
INTO #tblCustomer (CustomerType)
select t.Description
From Customer.Customer c
Join Customer.Type t
On c.TypeId = t.pkTypeId
Receiving output:
0001 null null
0002 null null
NULL active null
NULL active null
NULL null individual
NULL null individual
Expected Output:
0001 active individual
0002 active individual
Without knowing more about your tables, you can insert the first records like so...
INSERT INTO #tblCustomer (CustomerNumber)
select c.CustomerNumber from Customer.Customer c
And then update the remaining columns this way...
UPDATE #tblCustomer
set #tblCustomer.Status = c.Status
from Customer.Customer c
left outer join COM.StatusEngine_EntityStatus sees
on c.Status = sees.EntityStatusId and sees.EntityId = 'CustomerStatus'
join COM.StatusEngine_Status ses
on sees.Status = ses.Status
join #tblCustomer temp
on c.CustomerNumber = temp.CustomerNumber
However doing it like this is really inefficient, you should strive to create an insert that updates all columns in one go.
You can do it like this (I have verified the code with the Northwind sample database from Microsoft - I have chosen that one since you can use it for each SQL server version since SQL 2000):
declare #NumberOfItems int = 10;
CREATE TABLE #tblCustomer (
CustomerNumber NVARCHAR(1000)
,Name NVARCHAR (1000)
,CustomerType NVARCHAR (1000))
insert into #tblCustomer
select CustomerNumber, Name, Status from (select top(#NumberOfItems) ROW_NUMBER() OVER(ORDER BY CustomerID) as No, CustomerID as CustomerNumber from Customers) c
left join (select * from (select top(#NumberOfItems) ROW_NUMBER() OVER(ORDER BY ContactName) as No, ContactName as Name from Customers) q2) j1 on c.No=j1.No
left join (select * from (select top(#NumberOfItems) ROW_NUMBER() OVER(ORDER BY ContactTitle) as No, ContactTitle as Status from Customers) q3) j2 on c.No=j2.No
select * from #tblCustomer
drop table #tblCustomer
It will create a column with numbers from 1 to n for each element you want to import and then it joins it together.
The result of this query is:
Note: While this works, it is not the preferred way to do it, because there is no primary key - normally one would look for primary key / foreign key relationships to join the data together. The way you're intending to fill it puts data together which doesn't necessarily belong together (here each column is sorted and then put together by its row number - i.e. it picks values from rows sorted by its extract column and then putting them together again). If you have no primary key because you're importing data from other sources, you can add WHERE clauses to create a better connection between the inner and the outer select statements - you can find a nice article which might help you with such kind of subqueries here.
This is untested, however, I believe this is what you're after:
INSERT INTO #tblCustomer (CustomerNumber, [Status], CustomerType))
SELECT c.CustomerNumber, ses.[Name], t.[Description]
FROM Customer.Customer c
JOIN COM.StatusEngine_EntityStatus sees ON c.Status = sees.EntityStatusId --Changed to JOIN, as it is turned into a implicit INNER join by the next JOIN
AND sees.EntityId = 'CustomerStatus'
JOIN COM.StatusEngine_Status ses ON sees.[Status] = ses.[Status];
Note my comment regarding your LEFT OUTER JOIN, in that I've changed it to an INNER JOIN.
straight forward SQL here:
CREATE TABLE #tblCustomer
(
CustomerNumber NVARCHAR(1000),
Status NVARCHAR (1000),
CustomerType NVARCHAR (1000)
)
INSERT INTO #tblCustomer (CustomerNumber, Status, CustomerType)
SELECT DISTINCT
c.CustomerNumber,
ses.Name,
t.Description
FROM Customer.Customer c
LEFT OUTER JOIN COM.StatusEngine_EntityStatus sees
On c.Status = sees.EntityStatusId
And sees.EntityId = 'CustomerStatus'
LEFT OUTER JOIN COM.StatusEngine_Status ses
On sees.Status = ses.Status
LEFT OUTER JOIN Customer.Type t
On c.TypeId = t.pkTypeId

return rows only if a certain conditions match up with another table

I want an SQL query that returns a list of users, but only if they have a certain link and not another.
Table 1 links to table 2 users.agreementid = agreements.id, it can link multiple times as a user can be linked to different agreements
All the users in table 1 that only have a link to an agreement where the product is AggregationAgreement, if the user has a link to 2+ agreement ids and any of the links are to a ServiceAgreement then this should not be returned
So I have 2 tables:
TABLE 1- USERS
USER AGREEMENTID
--------------------
USER1 1
USER2 3
USER1 3
USER3 3
USER3 4
USER4 1
TABLE 2- AGREEMENTS
ID PRODUCT
-------------------------
1 ServiceAgreement
2 ServiceAgreement
3 AggregationAgreement
4 AggregationAgreement
5 ServiceAgreement
So for my results on the above example i only expect USER2 and USER3 to be returned.
USER 1 has two links but one of those is ServiceAgreement so it shouldn't be returned in the results.
USER 2 has link to just 1 aggregation agreement so this should be returned in the results.
USER 3 has two links but both are to AggregationAgreement so this should be returned in the results.
USER 4 has one link but it's to a ServiceAgreement so this should no be returned in the results.
Hope that all makes sense, as always appreciate any help.
This would return the users with the agreements, excluding ones that have a ServiceAgreement linked.
SELECT USERS.[UserName]
, AGREEMENTS.[AgreementId]
, AGREEMENTS.[Product]
FROM USERS
INNER JOIN AGREEMENTS
ON AGREEMENTS.[AgreementId] = USERS.[AgreementId]
WHERE USERS.[UserName] NOT IN
(
SELECT USERS.[UserName]
FROM USERS
INNER JOIN AGREEMENTS
ON AGREEMENTS.[AgreementId] = USERS.[AgreementId]
WHERE AGREEMENTS.[Product] = 'ServiceAgreement'
)
Try this:
select *
from users u
where exists
(
select 1
from agreements a
where u.agreementid=a.id and a.product='AggregationAgreement'
)
and not exists
(
select 1
from agreements a2
where u.agreementid=a2.id and a2.product<>'AggregationAgreement'
)
You can also use a query like below
select user
from
(
select
user=u.[user],
weight= case a.product ='ServiceAgreement' then -1 else 0 end -- negative weight for undesirable agreement
from
users u join agreements a
on u.AgreementId=a.AgreementId
and a.product in ('ServiceAgreement','AggregationAgreement') -- we only have two agreements of interest here
)t
group by [user]
having sum(weight)=0 -- should not be negative
Using EXCEPT:
declare #Users table ([User] nvarchar(10), [AGREEMENTID] int)
insert into #Users values
('USER1', 1)
, ('USER2', 3)
, ('USER1', 3)
, ('USER3', 3)
, ('USER3', 4)
, ('USER4', 1)
declare #Agreements table ([ID] int, [Product] nvarchar(100))
insert into #Agreements values
(1, 'ServiceAgreement')
, (2, 'ServiceAgreement')
, (3, 'AggregationAgreement')
, (4, 'AggregationAgreement')
, (5, 'ServiceAgreement')
select u.[User] from #Users u inner join #Agreements a on u.AGREEMENTID = a.ID and a.Product = 'AggregationAgreement'
except
select u.[User] from #Users u inner join #Agreements a on u.AGREEMENTID = a.ID and a.Product = 'ServiceAgreement'

What is "master join" in sql

CREATE TABLE car (id INT, name1 CHAR(15))
CREATE TABLE sales (sid INT, cid INT, year1 INT)
INSERT INTO car
VALUES (1, 'vento'), (2, 'vento'), (3, 'baleno'), (4, 'swift')
INSERT INTO sales
VALUES (1, 1, 2017), (2, 3, 2018), (3, 3, 2017), (5, 4, 2017)
--verify
--SELECT * FROM car
--SELECT * FROM sales
--1st query
SELECT sid, cid, year1, name1
FROM sales master
INNER JOIN car ON cid = id
--2nd query
SELECT sid, cid, year1, name1
FROM sales
INNER JOIN car ON cid = id
What is the difference between the 1st and the 2nd query?
What is the purpose of "master join" and when should we use it?
Nothing is different, it is functionally the same.
To explain, I've qualified the column names to demonstrate that master is just an alias.
I'd highly recommend qualifying the rest of the query as well, since ...car on cid = id works now, but isn't good form because if ever sales or car tables had the same column name, you'd get an error about ambiguity.
Also, decide if you want to use INNER JOIN, LEFT OUTER JOIN, etc.. [Types of joins] because it's more clear what you desire from a maintenance point of view later on.
SELECT master.sid
,master.cid
,master.year1
,c.name1
FROM sales master
INNER JOIN car c ON master.cid = c.id --(1st)
SELECT s.sid
,s.cid
,s.year1
,c.name1
FROM sales s
INNER JOIN car c ON s.cid = c.id --(2nd)
There is no concept called Master Join in sql.
SELECT
sid, cid, year1, name1
FROM sales master
INNER JOIN car
ON
cid = id
The above query takes master as alias name for sales table and does the inner join between sales table and car table
you can refer cid using alias (master) as below:
SELECT
sid, cid, year1, name1
FROM sales master
INNER JOIN car
ON
master.cid = id