SQL join on N-M realtion with AND condition - sql

I have TableA and TableB and a relation TableAB for a many to many (N-M) relation
TableA JOIN TableAB JOIN TableB
How can I add an AND-condition to check if TableAB has entries for multiple conditions in TableB
Maybe a stupid but simple example:
TableA = firstnames
TableB = lastnames
I want all firstnames that are used in combination with lastname "Smith", "Black" AND "Taylor"
I thought about using
WHERE lastname = "Smith" or lastname = "Black" or lastname = "Taylor"
GROUP BY firstname and check if count(*) = 3
But this seems to be not the right way, as lastname = "Smith" or lastname = "Smith" or lastname = "Smith" should not return an empty table.
And it should be easy to extend, as the filter is user input, and don't know how many conditions I get.
Bonus question: I use Sequelize with a Many-to-Many Association, not sure how to put this in here
EDIT:
Example:
TableA
id
firstname
1
Tom
2
Alice
3
Bob
TableB
id
lastname
1
Smith
2
Black
3
Taylor
TableAB
id
id_firstname
id_lastname
1
1
1
2
2
1
3
2
2
2
3
3
Meaning we have Tom Smith, Alice Smith, Alice Black, Bob Taylor
Query: Give me all firstnames with lastname Smith AND Black
Result: Alice

as always: there are different ways to achieve that. One is a multiple join of the NM table. Check the select on the end of the script (written for MS SQL Server). The rest of the script is just to create the sample data.
create table TableA (
ID int not null,
Firstname nvarchar(255) not null);
create table TableB (
ID int not null,
Lastname nvarchar(255) not null);
create table TableAB(
IDA int not null,
IDB int not null);
GO
insert into TableA values (1, 'Tom');
insert into TableA values (2, 'Alice');
insert into TableA values (3, 'Bob');
insert into TableB values (1, 'Smith');
insert into TableB values (2, 'Black');
insert into TableB values (3, 'Taylor');
insert into TableAB values (1, 1);
insert into TableAB values (2, 1);
insert into TableAB values (2, 2);
insert into TableAB values (3, 3);
GO
select A.Firstname
from TableA A
inner join TableAB AB1 on A.ID = AB1.IDA
inner join TableB B1 on B1.ID = AB1.IDB
inner join TableAB AB2 on A.ID = AB2.IDA
inner join TableB B2 on B2.ID = AB2.IDB
where B1.Lastname = 'Smith'
and B2.Lastname = 'Black'
other way would be a sub-select to return all combinations of Lastname + Firstname and then filter for the requested Lastname and count the occurance:
select Firstname
from (
select distinct A.Firstname, B.Lastname
from TableA A
inner join TableAB AB on A.ID = AB.IDA
inner join TableB B on B.ID = AB.IDB
) t
where Lastname in ('Smith', 'Black')
group by Firstname
having count(*) = 2
Both ways will return Alice as a result only.

Thank you for your reply. The second one is similar to what I thought would work:
select A.Firstname, B.Lastname
from TableA A
inner join TableAB AB on A.ID = AB.IDA
inner join TableB B on B.ID = AB.IDB AND Lastname in ('Smith', 'Black')
group by A.Firstname having count(*) = 2
But it must be checked if all user inputs of lastnames are distinct as "in ('Smith', 'Smith')" will not work with count = 2
That's why I thought there exists better solutions. The first suggestion would be fine with this, but its a mess to implement this automated with prepared statements in sequelize.

Related

SQL SELECT and check column from second table

I have two tables in my database. From the Table B i collect some data. I include by using JOIN the data from Table A. In this table there is a column user_id. The user id in table B is the the id from Table A. No i want to get all data from table A but without showing data if the table A id is in the table B column user_id available. I think the trick is the correct usage of JOIN but i need to check the whole column and not just one line.
SQL Query (which is probably not working)
SELECT * FROM tableB
JOIN tableA
ON tableB.user_id = tableA.id
WHERE tableB.user_id != tableA.id
Please see my example i have prepared in Excel:
To select all the data from tableA where id is not available in tableB you don't need join rather you can use not exists or not in.
I would prefer not exists
Using not exists:
SELECT * from tableA a
WHERE NOT EXISTS (
SELECT 1
FROM tableB b
WHERE b.user_id = a.id
);
using not in:
SELECT * from tableA a
WHERE id not in (SELECT user_id FROM tableB );
DB-Fiddle:
create table TableA (id int, name varchar(50), lastname varchar(50));
insert into TableA values(1,'john','smith');
insert into TableA values(2,'Paul','smith');
create table TableB (id int, user_id int, something varchar(50));
insert into TableB values(1,2,'bla');
insert into TableB values(2,3,'bla');
Query: (using not exists)
SELECT * from tableA a
WHERE NOT EXISTS (
SELECT 1
FROM tableB b
WHERE b.user_id = a.id
);
Output:
id
name
lastname
1
john
smith
Query: (using not in)
SELECT * from tableA
WHERE id not in (SELECT user_id FROM tableB );
Output:
id
name
lastname
1
john
smith
db<fiddle here
I think I get what you want. You want not exists:
SELECT *
FROM tableB b JOIN
tableA a
ON b.user_id = a.id
WHERE NOT EXISTS (SELECT 1
FROM tableA a2
WHERE b.user_id = a2.id
);
EDIT:
I wrote the above, but I didn't fully follow the JOIN conditions. You either seem to want to join on id:
SELECT *
FROM tableB b JOIN
tableA a
ON b.id = a.id
WHERE NOT EXISTS (SELECT 1
FROM tableA a2
WHERE b.user_id = a2.id
);
Or no JOIN at all:
SELECT *
FROM tableB b
WHERE NOT EXISTS (SELECT 1
FROM tableA a2
WHERE b.user_id = a2.id
);

Using an Inner Join and returning only 1 record per match on the left

I don't know why I'm not able to figure this out ATM. Here's a very contrived example.
create table Dog (
[Name] varchar(10),
[BreedId] int
);
create table Breed (
[BreedId] int,
[Name] varchar(10)
);
insert into DOG (name, breedid)
VALUES ('Mix', 1),
('Mix2', 2),
('Mix2', 3);
insert into breed(breedid, name)
VALUES
(1, 'BullDog'),
(1, 'Poodle'),
(2, 'BullDog'),
(2, 'Poodle'),
(4, 'Poodle');
The following produces two rows because the join table has two matching values, I just want one value as long as there is a matching record on the right.
select d.*
from dog d
inner join breed b on d.breedid = b.breedid
results in:
Mix 1
Mix 1
Mix 2
Mix 2
Is there a way to do this in the join clause / without doing a select in the where clase or other options like group by.
select d.*
from dog d
where d.breedid in (select breedid from breed)
Try "select distinct". You can also group by the columns that are duplicates and take the max() of columns that have unique values.
Edit: how to filter by breed.breedid without using a where clause.
select d.*
From dog as d
Inner join (select distinct breedid from breed) as b on b.breedid = d.breedid
I would use exists if you only want columns from dog:
select d.*
from dog d
where exists (select 1 from breed b where d.breedid = b.breedid);

SQL case where in with two conditions

Table_A Table_B
CityID StreetID AreaID PersonName CityID StreetID
Select tabB.PersonName from Table_B tabB WHERE CityID IN (SELECT tabB.StreetID
FROM Table_B tabB Where AreaID = 5) AND StreetID
I created a subquery to select all StreetID id in table b where areaID = 5. there are duplicate street id and unique City id. My problem is city id and street id of table B should match table A. How should I put this in query?
Inner join the tables then select PersonName and the StreetID...
You are looking for all persons where the area 5 is among the areas the person's street belongs to. (At least this is what your query suggests.)
It's a pity that SQL Server is not able to deal with WHERE (CityID, StreetID) IN like some other DBMS do.
So use EXISTS instead:
select personname
from table_b b
where exists
(
select null
from table_b2
where b2.areaid = 5
and b2.cityid = b.cityid
and b2.streetid = b.streetid
);
SELECT tabB.PersonName
FROM Table_B tabB
INNER JOIN Table_A tabA
ON
tabB.CityID = tabA.CityID AND tabB.StreetID = tabA.StreetID AND tabA.AreaID = 5

SQL Challenge: Why SQL query does not group according to NULL

I would like to challenge the SQL community here. Let us have the following data and SQL query:
create table A
(
a_id integer unique,
code int
);
create table B
(
a_id integer foreign key references A(a_id),
b_count int
);
insert into A values (1, 20);
insert into A values (3, 30);
insert into A values (null, 30);
insert into B values (1, 100);
insert into B values (1, 120);
insert into B values (null, 200);
select A.a_id, sum(B.b_count) Bsum
from A
left join B on A.a_id = B.a_id
group by A.a_id
Why doesn't the SQL query sum the value for the NULL? In other words, why we get
a_id Bsum
---------------
NULL NULL
1 220
3 NULL
instead of
a_id Bsum
---------------
NULL 200
1 220
3 NULL
This has nothing to do with the sum(). The reason is the left join and the way NULL works for comparisons. This is your query:
select A.a_id, sum(B.b_count) Bsum
from A left join
B
on A.a_id = B.a_id
group by A.a_id;
On the rows where a_id is NULL, the = evaluates to NULL -- which is not true. What you seem to want is:
select A.a_id, sum(B.b_count) Bsum
from A left join
B
on A.a_id = B.a_id or (A.a_id is null and B.a_id is null)
group by A.a_id;

Getting the Emp_ID

In Table_A I have atrribute "Verantwortlicher". This attribute contains the last name and the first name of the person respectively i.e. "Doe John"
In Table_B I have two attributes "Lastname_NAM" and "Firstname_NAM" which of course contain lastnames and firstnames of a person respectively i.e.
"Lastname_NAM" - Doe
"Firstname_NAM" - John
Table_A has a foreign key (Responsible_Emp_ID) which is referencing Table_B´s primary key (Emp_ID)
What I would like is to join these two tables so that beside the strig-type full name in the Table_A I also have a ID of that person (which of course comes from Table_B)
I forgot to mention that I need this data so I can FILL the values in aforeign key. Table_A is EMPTY.
I´m using T-SQL
Please help,
Thanks, D.
Try following code
SELECT
B.EmpID,
ISNULL(A.Verantwortlicher,B.Firstname_NAM++B.Lastname_NAM)
FROM
TableB B
LEFT JOIN TableA A
ON B.EmpID = A.Responsible_Emp_ID
Try this:
--Populate TableA
INSERT INTO TableA(Responsible_Emp_ID, Verantwortlicher)
SELECT EmpID, FirstName + ' ' + LastName
FROM TableB B
--SELECT to join the 2 tables
SELECT B.EmpID, A.Verantwortlicher
FROM TableB B
INNER JOIN TableA A
ON B.EmpID = A.Responsible_Emp_ID
--Just make the join
SELECT C.FullName, A.EmpID FROM
(SELECT FirstName + ' ' + LastName AS FullName
FROM TABLE B) C
INNER JOIN TableA A
ON C.FullName = A.Verantwortlicher