Referencing Two Tables in SQL DB - mysql

I'm new to SQL, how could I answer the following question? Do I use join?
Thank you so much.
What are the names and home cities for people searched for the word "drain"?
.schema is below:
CREATE TABLE `users` (
`id` INTEGER,
`name` VARCHAR,
`email` VARCHAR,
`city` VARCHAR,
`state` VARCHAR,
`last_visit` DATE,
`page_views` INTEGER,
PRIMARY KEY (`id`)
);
CREATE TABLE `search_terms` (
`id` INTEGER,
`word` VARCHAR,
PRIMARY KEY (`id`)
);

select u.name, u.city
from users u
inner join search_terms st on st.id = u.id
where st.word = 'drain'
I hope this helps.

You do need to join search_terms table with users table. I assume the search_terms.id is referencing to users.id.
Linking them both, will give us the results of the search terms for each user.
from there you can add more filters (WHERE conditions) to have more specific results.
So, to get the names and the home cities for each user you need to select name and city from users table, then join search_terms table and link id columns to know what words that each user has used in the search.
The query should be something like this :
SELECT name, city
FROM users
LEFT JOIN search_terms ON search_terms.id = users.id
WHERE
search_terms.word LIKE 'drain';

You could use a JOIN or a sub-query. Here's the basic strategy:
Get the id values that match what you're looking for
Look up any other info for those id values
.
SELECT name, city
FROM users usr
INNER JOIN search_terms stm ON usr.id = stm.id
WHERE stm.word = 'drain';
or ...
SELECT name, city
FROM users
WHERE id IN (SELECT id FROM search_terms WHERE word = 'drain');

Related

How to create a query with JOIN and WHERE or how to make them friends?

I need to make a query, where there are columns of client's names and their orders per month.
Some clients don't have orders at some months and there fields must have 0.
The problem is, when i use WHERE and OUTER JOIN (no matter which one) at one query*, nessesary zero`s cutteed by WHERE. So how can i solve that?
Descripton of tables are pinned.
SELECT name
, ordering.id_client
, COUNT(order_date)
FROM ordering
RIGHT
OUTER
JOIN client
ON client.id_client = ordering.id_client
WHERE month(order_date) = 1
GROUP
BY name;
**Descripton**: (https://i.imgur.com/TrUGOLW.png)
**Example of my query** (there are 6 notes about clients at my db, showed only 4 of 6):
(https://i.imgur.com/ABP6pP0.png)
**MRE stuff**
Client: create table client(id_client int primary key auto_increment, name var char(50), passport_code int, addr varchar(70));
insert into client values(null, 'Penny Anderson', 6485, 'New Orlean');
Ordering: create table ordering(id_order int primary key auto_increment, id_client int, order_date date, foreign key(id_client) references client(id_client));
insert into ordering values(null, 1, date('2020-05-01'));
Try a simple left join starting from client's table
SELECT client.name
, client.id_client
, COUNT(order_date)
FROM client
LEFT JOIN ordering ON client.id_client = ordering.id_client
AND month(ordering.order_date) = 1
GROUP BY client.id_client;
If the condition for join is related to the left joined table then add this condition in the related ON clause and not in where otherwise this work as an inner join

Is it possible to do a search query and specify which matches not to return, in the same query?

I have a search box that returns values from multiple tables using the LIKE operator. However I now want to make sure that any matches where x = y are not returned. Is this possible to do in the same query?
My attempt:
$search_sql = "SELECT * FROM User
INNER JOIN UserSkills ON User.UserId = UserSkills.UserId
INNER JOIN Skills ON UserSkills.SkillId = Skills.SkillId
INNER JOIN UserTasks on User.UserId = UserTasks.UserId
WHERE Description LIKE '%".$value."%'
OR FName LIKE '%".$value."%'
OR LName LIKE '%".$value."%'
OR JobRole LIKE '%".$value."%'
OR UserId NOT IN (
SELECT UserID from UserTasks WHERE TaskID = $Task)
GROUP BY Description" or die(mysqli_error($con));
$search_query = mysqli_query($con, $search_sql);
if(mysqli_num_rows($search_query) !=0) {
$search_rs = mysqli_fetch_assoc($search_query);
}
This attempt returns false (no results). There are results to be found.
This is the previous version that successfully returns results, before adding the new table (UserTasks), and NOT IN:
$search_sql = "SELECT * FROM User
INNER JOIN UserSkills ON User.UserId = UserSkills.UserId
INNER JOIN Skills ON UserSkills.SkillId = Skills.SkillId
WHERE Description LIKE '%".$value."%'
OR FName LIKE '%".$value."%'
OR LName LIKE '%".$value."%'
OR JobRole LIKE '%".$value."%'
GROUP BY Description" or die(mysqli_error($con));
If you want to exclude records where x = y, you would need to add something like AND x <> y (x does not equal y) to your WHERE. Keep in mind that you would need to parenthesize the WHERE components based on your intention when combining ANDs and ORs.
Without seeing the data in your tables I'm not sure why you currently are not getting results. My recommendation would be to start taking pieces out until you get the result you expect, and then adding them back testing your expectations until something doesn't match.
A few other notes:
The or die(..) here I don't think does anything, since the left side is simply string concatenation which I don't think could fail. You are not actually doing any MySQL related work when assigning to $search_sql.
I would recommend looking into parameterized queries rather than using string concatenation and variable interpolation to generate your query. It will be a bit more work initially, but tends to be easier to maintain and reason about and can easily be more secure.
I think INNER JOIN and JOIN have no functional difference, so I would pick one or the other and be consistent.
Using SELECT * when grouping by something has the potential to give unexpected/inconsistent results. Any column that is not in either the GROUP BY clause or contained in an aggregate will return the value on the first row of the group, but first is not guaranteed to be consistent (unless you ORDER). Other DBMS's (Oracle and SQL Server, at least, I think) actually forbid this behavior.
Update
SELECT Description
FROM User u
JOIN UserSkills us ON u.UserId = us.UserId
JOIN Skills s ON us.SkillId = s.SkillId
WHERE
(
Description LIKE '%".$value."%'
OR FName LIKE '%".$value."%'
OR LName LIKE '%".$value."%'
OR JobRole LIKE '%".$value."%'
)
AND UserId NOT IN (
SELECT UserID
FROM User u
JOIN UserTasks ut ON u.UserId = ut.UserId
WHERE TaskID = $Task
)
GROUP BY Description
Update with schema and data
CREATE TABLE users (
id INT NOT NULL,
first VARCHAR(16),
last VARCHAR(16),
PRIMARY KEY (id)
);
CREATE TABLE skills (
id INT NOT NULL,
description VARCHAR(64) NOT NULL,
job_role VARCHAR(64) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE user_skills (
user_id INT NOT NULL,
skill_id INT NOT NULL,
FOREIGN KEY user_skills_user (user_id) REFERENCES users (id),
FOREIGN KEY user_skills_skill (skill_id) REFERENCES skills (id)
);
CREATE TABLE user_tasks (
user_id INT NOT NULL,
task_id INT NOT NULL,
PRIMARY KEY (user_id,task_id),
FOREIGN KEY user_tasks_user (user_id) REFERENCES users (id)
);
INSERT INTO users VALUES (0,'FA','LA');
INSERT INTO users VALUES (1,'FB','LB');
INSERT INTO users VALUES (2,'FC','LC');
INSERT INTO skills VALUES (0,'Skill Description A','Job Role A');
INSERT INTO skills VALUES (1,'Skill Description B','Job Role B');
INSERT INTO skills VALUES (2,'Skill Description C','Job Role C');
INSERT INTO user_skills VALUES (0,0);
INSERT INTO user_skills VALUES (0,1);
INSERT INTO user_skills VALUES (0,2);
INSERT INTO user_skills VALUES (1,0);
INSERT INTO user_skills VALUES (1,1);
INSERT INTO user_skills VALUES (2,1);
INSERT INTO user_tasks VALUES (0,1);
INSERT INTO user_tasks VALUES (1,2);
INSERT INTO user_tasks VALUES (2,1);
SELECT *
FROM users u
JOIN user_skills us ON u.id = us.user_id
JOIN skills s ON us.skill_id = s.id
WHERE
(
s.description LIKE ''
OR u.first LIKE '%F%'
OR u.last LIKE ''
OR s.job_role LIKE ''
)
AND u.id NOT IN (
SELECT user_id
FROM user_tasks
WHERE task_id = 2
)
;
This should return 4 records, 3 for FA LA by 3 skills, and one for FC LC by 1 skill. FB LB is not returned because it is assigned task 2. Changing to task_id = 1 should return 2 records, FB LB by 2 skills.

How can I write a query that shows the Institutions that have more than one one contact at them?

CREATE TABLE Institutions
(
Institution_ID INTEGER PRIMARY KEY,
Institution_Name VARCHAR(200))
CREATE TABLE Contact_Persons
(
Contact_No INTEGER PRIMARY KEY,
First_Name VARCHAR(60) NOT NULL,
Last_Name VARCHAR(60) NOT NULL,
Institution_ID INTEGER,
FOREIGN KEY (Institution_ID) REFERENCES Institutions(Institution_ID))
How can I write a mysql query that shows the Institutions that have more than one one Contact Person at them? The Query should show Institution_ID and Institution_Name from Table Institutions. The following Query works but It only shows Institution_ID. I want to see Institution_Name too. I think there must be a join between these two tables. Please help.
SELECT Institution_ID
FROM Contact_Persons
GROUP BY Institution_ID
HAVING COUNT(*)>1;
You were right, you just need to join your two tables, and make sure you also group by everything you want to select (unless it is an aggregate), so in this case add Institution_Name to the group by clause:
SELECT i.Institution_ID, i.Institution_Name
FROM Contact_Persons AS cp
INNER JOIN Institutions AS i
ON i.Institution_ID = cp.Institution_ID
GROUP BY i.Institution_ID, i.Institution_Name
HAVING COUNT(*) > 1;
Try this
SELECT I.Institution_ID,I.Institution_name
FROM Contact_Persons as C inner join Institutions as I on
C.Institution_ID=I.Institution_ID
GROUP BY I.Institution_ID,I.Institution_name
HAVING COUNT(*)>1;

Alternative to GROUP_CONCAT? Multiple joins to same table, different columns

I'm not even sure of the correct terminology here. MySQL newbie, more or less.
Given a couple tables defined as follows:
CREATE TABLE users
( user_id int(11) NOT NULL auto_increment
, name VARCHAR(255)
, pri_location_id mediumint(8)
, sec_location_id mediumint(8)
, PRIMARY KEY (user_id)
);
CREATE TABLE locations
( location_id mediumint(8) NOT NULL AUTO_INCREMENT
, name varchar(255)
, PRIMARY KEY (location_id)
)
I'm trying to do a query to get the user name and both primary and secondary locations in one go.
I can get one like this:
SELECT u.name AS user_name, l.name as primary_location FROM users u, locations l WHERE u.primary_location_id=l.location_id
But I'm drawing a total blank on the correct syntax to use to get both in one query.
SELECT u.name AS user_name, l1.name as primary_location , l2.name as secondary_location
FROM users u
JOIN locations l1 ON(u.pri_location_id=l1.location_id)
JOIN locations l2 ON(u.sec_location_id = l2.location_id);
First of, I would strongly consider changing your DB schema if allowable to add a users_locations table that can be used to properly describe this many to many relationship.
This table could look like:
user_id location_id location_type
1 1 primary
1 2 secondary
2 1 secondary
2 2 primary
and so forth.
You would likely want a compound primary key across all three columns. And location_type might best be enum data type.
Your query would then be like
SELECT
u.name AS user_name
l.name AS location_name
ul.location_type AS location_type
FROM users AS u
INNER JOIN user_location AS ul /* possibly use left join here if user can exist without a location */
ON u.user_id = ul.user_id
INNER JOIN locations AS l
ON ul.location_id = l.location_id
ORDER BY ul.location_type ASC
This would return up to two records per user (separate record for primary and secondary, primary listed first)
If you need this collapsed to a single record you could do this:
SELECT
u.name AS user_name
COALESCE(CASE WHEN ul.location_type = 'primary' THEN l.name ELSE NULL END CASE) AS primary_location,
COALESCE(CASE WHEN ul.location_type = 'secondary' THEN l.name ELSE NULL END CASE) AS secondary_location
FROM users AS u
INNER JOIN user_location AS ul /* possibly use left join here if user can exist without a location */
ON u.user_id = ul.user_id
INNER JOIN locations AS l
ON ul.location_id = l.location_id
GROUP BY `user_name`
If however you are stuck with current schema, then solution by #Jlil should work for you.

MySQL one table inner join with 3 different entity tables

I got 4 tables, Email_Company_Contact_Ref table is the table which linked with Email, Company, and Contact.
**Email_Company_Contact_Ref**
id = primary key
email_id = reference to Email.`id`
ref_id = it can be Company.id / Contact.id
table = reference from which table name
I try to use left join to get my output, but I got duplicated result. If I try inner join, I will not get any result at all, it is because Company and Contact this two tables does not have any thing common.
This is the output I would like to complete.
I able to use UNION to get the output, but it not really effective. I am thinking it should be a way to get the output result.. Please help.
Thanks!
Here is my mysql answer, hope this can help
SELECT e.email, r.table, c1.name AS company_name, c2.name AS contact_name
FROM email_company_contact_ref r
JOIN email e ON e.id = r.email_id
LEFT JOIN company c1 ON (c1.id = r.ref_id AND r.table = 'company')
LEFT JOIN contact c2 ON (c2.id = r.ref_id AND r.table = 'contact')
GROUP BY r.table, e.email
I don't think it can be done without a UNION. Here's my suggestion.
SELECT email_address, eccr.table table, company_name, contact_name
FROM Email e, Email_Company_Contact_Ref eccr,
(SELECT "Company" table, id, company_name, NULL contact_name
FROM Company
UNION ALL
SELECT "Contact" table, id, NULL company_name, contact_name
FROM Contact) cc
WHERE e.id = eccr.email_id
AND eccr.table = cc.table
AND eccr.email_id = cc.id
I'm not getting the ref_id part... Is it a foreign key? Or is that the primary key for the Email_Company_Contact_Ref table?
I would think you'd want to put the reference for the Email table in the Company and Contact tables. If you need more than one emails for them, then you should create two join tables: Company_Email and Contact_Email. Your current design (with references to table names as values for a column) is bad SQL design -- just because things like RoR promote it, it won't get any better.
With proper design, the equivalent of that complicated query would look something like:
CREATE TABLE Company_Email (company_id integer, email_address varchar(100),
FOREIGN KEY company_id REFERENCES Company (id));
CREATE TABLE Contact_Email (contact_id integer, email_address varchar(100),
FOREIGN KEY contact_id REFERENCES Contact (id));
SELECT email_address, 'Company' AS kind, company_name AS name
FROM Company_Email ce JOIN Company c ON company_id = c.id
UNION
SELECT email_address, 'Contact', contact_name
FROM Contact_Email ce JOIN Contact c ON contact_id = c.id;
If you can't change it, you'll have to do the UNION along the lines Barmar explained it.
Or, you can do a SELECT DISTINCT to get rid of the duplicates from your left joined query.