Select persons which have overlap with other table - sql

I didn't even know how to come up with a good title, so I hope I can describe my problem in a right way :)
So I have a person table, and it has a N:N relationship with keywords via the table PersonKeywords.
Then I also have a Search table, and it also has a N:N relationship with keywords via the table SearchKeywords.
Now the person can have a relationship with keyword A and B, and the search record can have a relationship with the keywords A and C.
Now I want the person in my resultset, because it has at least one (in this 'A') of the keywords the search record has.
I also want the person who has 'A', the one with 'C', the one with 'A' and 'C', but not the one with only B.
So it's a match on two lists, but I don't know where to start to create such a statement...

So you have three people...
declare #persons table (id int identity(1,1), name varchar(10))
insert #persons (name) values ('Babs'),('Ken'),('Neville'),('Sue')
Babs has A and B, Ken has A and C, Neville has B only and Sue has C only
declare #personkeywords table (personid int, keyword varchar(5))
insert #personkeywords values (1,'a'),(1,'b'),(2,'a'),(2,'c'),(3,'b'),(4,'c')
The search is for A or C
declare #searchkeywords table (searchid int, keyword varchar(5))
insert #searchkeywords values (1,'a'),(1,'c')
So...
select distinct persons.*
from #persons persons
inner join #personkeywords personkeywords on persons.id = personkeywords.personid
inner join #searchkeywords searchkeywords on personkeywords.keyword = searchkeywords.keyword
where
searchkeywords.searchid = 1
Gives
1 Babs
2 Ken
4 Sue

Although I don't have very much information to work with, the following should at least help you...
SELECT s.SearchID, k.Keyword, p.PersonID, p.Name
FROM Search s
INNER JOIN SearchKeywords sk ON s.SearchID = sk.SearchID
INNER JOIN Keywords k ON sk.KeywordID = k.KeywordID
LEFT OUTER JOIN PersonKeywords pk ON k.KeywordID = pk.KeywordID
LEFT OUTER JOIN Person p ON pk.PersonID = p.PersonID
WHERE k.Keyword = 'mykeyword'
GROUP BY s.SearchID, k.Keyword, p.PersonID, p.Name

Related

How to select all dishes containing a list of ingredients

I am looking to build a query which would return the dishes that contain a list of ingredients but I can't figure it out. The dish returned must contain at least the list of ingredients requested.
Ingredients
============
ID
IngredientName
IngredientAmount
P_ID
Dishes
==========
ID
DishName
DishIngredients
==========
ID
DishID
IngredientID
So far I built the following query, but it returns all meals that contain at least 1 of the ingredients in the list, not only those which contain all ingredients in the list.
The list of ingredients to match is a string converted to a table because I want to use it as an argument in a stored procedure.
DECLARE #IdIngredients nvarchar(1024) = '4174, 4028'
DECLARE #Ingredients TABLE (IdIngredient int)
INSERT #Ingredients (IdIngredient)
SELECT Convert(int, value) FROM STRING_SPLIT(#IdIngredients, ',')
SELECT DISTINCT D.Id
FROM Dishes D
INNER JOIN DishIngredients DI ON DI.DishID = D.ID
WHERE IngredientID IN (SELECT IdIngredient FROM #Ingredients)
Any help would be welcome. I am sure there is a way to manage it either with a join or by counting matching ingredients, but I can't figure it out.
While writing this post, I maybe found a solution but I still need to test it:
DECLARE #IdIngredients nvarchar(1024) = '4174, 4028'
DECLARE #Ingredients TABLE (IdIngredient int)
INSERT #Ingredients (IdIngredient)
SELECT Convert(int, value) FROM STRING_SPLIT(#IdIngredients, ',')
SELECT DISTINCT D.Id
FROM Dishes D
INNER JOIN DishIngredients DI ON DI.DishID = D.ID
WHERE IngredientID IN (SELECT IdIngredient FROM #Ingredients)
GROUP BY D.Id
HAVING COUNT(D.Id) = (SELECT COUNT(*) FROM #Ingredients)
A double NOT EXISTS should do the trick:
SELECT D.*
FROM Dishes As D
WHERE Not Exists
(
SELECT 1
FROM #Ingredients As I
WHERE Not Exists
(
SELECT 1
FROM DishIngredients As DI
WHERE DI.DishID = D.Id
And DI.IngedientId = I.IdIngredient
)
)
Or, if you'd prefer, a NOT EXISTS with an EXCEPT:
SELECT D.*
FROM Dishes As D
WHERE Not Exists
(
SELECT IdIngredient
FROM #Ingredients
EXCEPT
SELECT IngredientId
FROM DishIngredients As DI
WHERE DI.DishID = D.Id
)
EXCEPT and INTERSECT (Transact-SQL) - SQL Server | Microsoft Docs
So far I buit following query but it returns all meals that contain at least 1 of the ingredients in the list but not only those which contain all ingredients in the list.
When you split the ingredients list, you can gather how many ingredients there are. Let it be X.
Now if you join the dishes with the recipes,
SELECT D.*, COUNT(*) AS N
FROM Dishes As D
JOIN DishIngredients AS DI ON (DI.DishID = D.ID)
JOIN Ingredients AS I ON (I.ID = DI.IngredientID)
WHERE Ingredients.IngredientName IN (#IngredientNameList)
GROUP BY D.ID
it will report, for each dish, how many ingredients there are from the supplied ingredient list.
The dishes you want are those HAVING N = X.
(If you have the ingredients IDs, even better, you can save yourself the JOIN with Ingredients and use IngredientID instead).

How can I run my SQL, to get the result such as "John Doe, plumbing"

I have table category, with id and cat_name
Example: 152, Plumbing
I have table user, with category_id and name
Example: 152, John Doe
When I do
SELECT name, category_id FROM user
As a result I will have something like
John Doe, 152
Question:
How can I run my SQL, to get the result such as
John Doe, plumbing
It is quite easy to get this, Please use below query for the same
SELECT User.Name, Category.cat_name FROM User
INNER JOIN Category ON Category.Id = User.category_id
Happy coding :-)
Try basic JOINS
SELECT C.cat_name, U.Name
FROM category C JOIN User U ON C.id = U.category_id
this will work:
select a.name,b.cat_name from user a, category b
where a.category_id=b.id
As you can understand you have two tables, and there is some 1 to 1 relationship between records of these tables(user & category).
So here we will use a simple join to connect this two table.
SELECT c.cat_name, u.name
FROM category as c, user as u
WHERE c.id = u.category_id
When I do category as c, this is called aliasing. This has two benefits. One is to keep our query short and sweet(no need to repeat category, just use c) and second is SQL query engine has 100% clarity of what we want to select.
Let's you also want to select id, then you should use c.id or u.category_id.
Check these alternate methods-
IF OBJECT_ID('Category') IS NOT NULL
DROP TABLE Category
IF OBJECT_ID('User') IS NOT NULL
DROP TABLE [User]
CREATE TABLE Category
(ID INT,Cat_name VARCHAR(20))
CREATE TABLE [User]
(Category_id INT,[Name] VARCHAR(20))
INSERT INTO Category(ID ,Cat_name)
VALUES (152,'Plumbing')
INSERT INTO [User](Category_id ,[Name])
VALUES (152,'John Doe')
--Method 1 (using CROSS APPLY)
SELECT U.[Name], C.Cat_name FROM [User] U CROSS APPLY Category C WHERE U.Category_id=C.ID
--Method 2 (using INNER JOIN)
SELECT U.[Name], C.Cat_name FROM [User] U INNER JOIN Category C ON U.Category_id=C.ID
--Method 3 (using WHERE and WITHOUT JOIN)
SELECT U.[Name], C.Cat_name FROM [User] U,Category C WHERE U.Category_id=C.ID

would I need to use a Union here, a Join, or something else?

I cant figure out if what i'm needing to do here is a Join statement, or a Union.
Pets
Id name color
1 wiskers grey
2 midnight black
3 ralph yellow
4 Bob brown
Shots table
Id Rabbies a123
2 Yes No
4 No No
Notes tables
Id Notes
4 This pet is blind
2 This pet has no owner
The result im looking for:
Id Name Color Rabbies A123 Notes
1 Wiskers grey Null Null Null
2 midnight black Yes No This pet has no owner
......
I think you want left joins:
select p.*, s.rabies, s.a123, n.notes
from pets p left join
shots s
on s.id = p.id left join
notes n
on n.id = p.id;
Joins and Unions are both used to combine data, and both could potentially be used here. However, I would recommend using a Join, Joins combine columns from different tables, which seems to be what you want to include. You want all columns included for a single row (for the ID of the animal).
https://www.codeproject.com/Articles/1068500/What-Is-the-Difference-Between-a-Join-and-a-UNION
Try that link for more information.
If this is the case
create table Shots (id serial primary key, Rabbies varchar, a123 varchar, pet_id int);
insert into shots (pet_id, Rabbies, a123) values (2, 'Yes','No'), (4, 'No','No');
create table notes (id serial primary key, notes varchar, pet_id int);
insert into notes (pet_id, notes) values (4, 'This pet is blind'), (2, 'This pet has no owner');
select p.id, p.name, p.color, s.rabbies, s.a123, n.notes
from pets p
left join shots s on p.id = s.id
left join notes n on p.id = n.id;

SQL Logic or Query to find what is missing

Hi all I need your help on the following logic. Currently I have a table that has 300 records, that are related but on this new tables I have the columns called them country, POS so for each combination of country + POs I should have 1 record of table A.
but the situation is that when I am checking the last table someone only inserted some records of table A into table b, and now I have to find what are the missing combination.
could you guide me on the logic that I should use for this, any question please let me know.
Example
Table A name Categories
Milk
Hot Sauces
Meat
Table B
Category POS Country
Milk DF Mexico
Meat DF Mexico
Hot Sauces DF Mexico
Milk CC Canada
Like you can see Canada still missing 2 categories but this table have all Americas countries so let say I have 20 countries. So 20 multiple by 300 categories I should have 6000 distinct records or more because each country have different quantities of POS, right, but someone only inserted let say 3600 records so now I have to find what combination are missed.
If you Don't have a country table you can derive one by selecting DISTINCT Country from your TableB. Then cross join that with Categories for a Cartesian Join (all possible combinations) between Countries and Categories.
SELECT countries.country, c.Category
FROM
(SELECT DISTINCT Country
FROM
#TableB) as countries
CROSS JOIN #Categories c
LEFT JOIN #TableB b
ON countries.Country = b.Country
AND c.Category = b.Cateogry
WHERE
b.Cateogry IS NULL
If you actually need All Possible Combinations of POS and Country and Categories. In this case it sounds like POS is more like a store than a point of sale but same concept. Just derive a POS table if you don't have one and cross join it with the cross join of countries and categories.
SELECT
countries.country, c.Category, pos.POS
FROM
(SELECT DISTINCT Country
FROM
#TableB) as countries
CROSS JOIN #Categories c
CROSS JOIN (SELECT DISTINCT POS
FROM
#TableB) as pos
LEFT JOIN #TableB b
ON countries.Country = b.Country
AND c.Category = b.Cateogry
AND pos.POS = b.POS
WHERE
b.Cateogry IS NULL
But I would guess that not every store is in every country so you probably want to constrain the POS combiantions to POS's that are available in a particular country. Again you can derive the table if you don't have one this time include Country and do an inner join between the derived country table and it.
SELECT
countries.country, c.Category, pos.POS
FROM
(SELECT DISTINCT Country
FROM
#TableB) as countries
CROSS JOIN #Categories c
INNER JOIN (SELECT DISTINCT Country, POS
FROM
#TableB) as pos
ON countries.Country = pos.Country
LEFT JOIN #TableB b
ON countries.Country = b.Country
AND c.Category = b.Cateogry
AND pos.POS = b.POS
WHERE
b.Cateogry IS NULL
test data used:
DECLARE #Categories AS TABLE (Category VARCHAR(25))
DECLARE #TableB AS TABLE (Cateogry VARCHAR(25),POS CHAR(2), Country VARCHAR(25))
INSERT INTO #Categories VALUES ('Milk'),('Hot Sauces'),('Meat')
INSERT INTO #TableB VALUES ('Milk','DF','Mexico'),('Meat','DF','Mexico'),('Hot Sauces','DF','Mexico'),('Milk','CC','Canada'),('Milk','XX','Canada')
Hi, You can use below logic to get missing data,
SELECT column_name FROM tableA WHERE column_name NOT IN
(SELECT column_name FROM tableB)
Change the required column names and table names in the query. Use same column names in all three places

using joins combine two or more tables into one Table

I have two types of databases like,
Db1:
Degrees:
ID
Name
Type
Qualifications:
Id
CandidateId
DegreeId
specialization
DB2:
UG_LIST :
Name
ID
PGLIST :
Name
ID
DoctorateList:
Name
ID
Here Degrees table is single one. All degrees like Ug, Pg, doctorate are stored single table called Degrees.But In database -2, every item created as separate tables. So I have to take these three table values store into Qualifications table.
I used joins for this. But I can't create the select query for this.
SELECT top(50) 'INSERT INTO CandidateQualifications(candidateId,DegreeId,specialization) VALUES('+
Cast(c.CandidateID as varchar(50))+',''' +
Isnull(Cast(u.Id as varchar(50)),'NULL') Or cast(p.Id as varchar(50)) or cast(d.Id as varchar(50))+','+
IsNull(''''+c.ugspecification+'''', 'NULL')+')'
FROM candidatedetails as c
right join UG_List As u ON c.qualification=u.UGName
right join PG_List As p ON c.qualification=u.pgname
right join Docorate_List As d ON c.qualification=u.docorate
Please tell how to do this? I use Or operator but it is not accepting.
Any idea?
Replace:
Isnull(Cast(u.Id as varchar(50)),'NULL') Or cast(p.Id as varchar(50)) or cast(d.Id as varchar(50))
With:
Cast(Coalesce(u.Id,p.Id,d.Id)as varchar(50))
I'd expect the joins to be left
SELECT top(50) 'INSERT INTO CandidateQualifications(candidateId,DegreeId,specialization) VALUES('+
Cast(c.CandidateID as varchar(50))+',''' +
Cast(Coalesce(u.Id,p.Id,d.Id)as varchar(50))+','+
IsNull(''''+c.ugspecification+'''', 'NULL')+')'
FROM candidatedetails as c
LEFT join UG_List As u ON c.qualification=u.UGName
LEFT join PG_List As p ON c.qualification=p.pgname
LEFT join Docorate_List As d ON c.qualification=d.docorate