We have a situation that I have been able to recreate with the following simple example. I have the following two sample tables:
CREATE TABLE contact_info
(
id INT UNSIGNED AUTO_INCREMENT,
priContactId INT,
secContactId INT,
blahBlah VARCHAR(32),
PRIMARY KEY(id)
);
and
CREATE TABLE name_lookup
(
id INT UNSIGNED AUTO_INCREMENT,
contactID INT,
contactName VARCHAR(32),
PRIMARY KEY(id)
);
I populate them as follows:
INSERT INTO contact_info(priContactId, secContactId, blahBlah) VALUES(1, 3, "Team A"), (4, 2, "Team B");
INSERT INTO name_lookup(contactID, contactName) VALUES(1, "John Doe"), (2, "Mary Smith"), (3, "Jose Garcia"), (4, "Larry Brown");
Obviously, the contents of the tables are as follows:
+----+--------------+--------------+----------+
| id | priContactId | secContactId | blahBlah |
+----+--------------+--------------+----------+
| 1 | 1 | 3 | Team A |
| 2 | 4 | 2 | Team B |
+----+--------------+--------------+----------+
+----+-----------+-------------+
| id | contactID | contactName |
+----+-----------+-------------+
| 1 | 1 | John Doe |
| 2 | 2 | Mary Smith |
| 3 | 3 | Jose Garcia |
| 4 | 4 | Larry Brown |
+----+-----------+-------------+
We would like to perform a JOIN operation so that we get output like this:
+-------------+-------------+--------+
| John Doe | Jose Garcia | Team A |
+-------------+-------------+--------+
| Larry Brown | Mary Smith | Team B |
+-------------+-------------+--------+
The join constraints for both the priContactId and secContactId columns are the same and I am having tough time figuring out what the JOIN query should look like.
FYI, we are using MySQL version 5.6.49.
Two separate columns needs 2 separate table copies join.
SELECT t1.contactName name1, t2.contactName name1, t3.blahBlah team
FROM name_lookup t1
JOIN name_lookup t2
JOIN contact_info t3 ON t1.contactID = t3.priContactId
AND t2.contactID = t3.secContactId
This would normally be handled with two joins. You don't have not null constraints on the two contacts, so I would suggest outer joins:
select nl_pri.contactName as PrimaryName, nl_sec.contactName as SecondaryName,
ci.blahblah
from contact_info ci left join
name_lookup nl_pri
on ci.priContactId = nl_pri.contactId left join
name_lookup nl_sec
on c.secContactId = nl_sec.contactId;
Notes:
The left join keeps all contacts, even those that don't have both primary and secondary.
Table aliases make the query easier to write and read. The table aliases should be meaningful.
You should define the foreign key relationships, so it is clear that the contact ids refer to the name_lookup table.
Related
I'm new to sql and do not understand what this join statement is doing. Does this statement ON people.state_code=states.state_abbrev mean that people.state_code and states.state_abbrev are now one?
SELECT people.first_name,
people.state_code, states.division
FROM people
JOIN states ON people.state_code=states.state_abbrev;
It will take the columns first_name and state_code from the table people and the column division from the table states and put them together in a join table where the entries in the state_code and state_abbrev columns match. The join table is produced only for display in response to this query; the underlying tables with the data entries are not amended.
In this case the '=' means equal (like values are equal) and is part of the join condition based on which data is retrieved by the select statement. You are 'linking' the two tables based on a condition so you can retrieve related data...
Relational data base - there are relations between tables and between data.
For example:
table_1
PERSON_ID FIRST_NAME LAST_NAME ADDRESS_ID
1 |John |Doe |2
table_2
ADRESS_ID STREET
1 | 5th Avenue
2 | 1st Street
SELECT FIRST_NAME, STREET
FROM table_1 t1
JOIN table_2 t2 ON t1.ADDRESS_ID = t2.ADDRESS_ID;
will return
John, 1st Street
Does this statement ON people.state_code=states.state_abbrev mean that people.state_code and states.state_abbrev are now one?
Answer: NO. people.state_code and states.state_abbrev should be the same value on the respective tables.
Let me give you an example taken from https://www.mysqltutorial.org/mysql-join/
Suppose you have below tables:
CREATE TABLE members (
member_id INT AUTO_INCREMENT,
members_name VARCHAR(100),
PRIMARY KEY (member_id)
);
CREATE TABLE committees (
committee_id INT AUTO_INCREMENT,
committees_name VARCHAR(100),
PRIMARY KEY (committee_id)
);
Some data examples:
+-----------+--------+
| member_id | members_name |
+-----------+--------+
| 1 | John |
| 2 | Jane |
| 3 | Mary |
| 4 | David |
| 5 | Amelia |
+-----------+--------+
+--------------+--------+
| committee_id | committees_name |
+--------------+--------+
| 1 | John |
| 2 | Mary |
| 3 | Amelia |
| 4 | Joe |
+--------------+--------+
To do the INNER JOIN we can use members_name and committees_name not the id because they are auto_increment and the data are not related.
So the query would be:
SELECT
m.member_id,
m.members_name AS member,
c.committee_id,
c.committees_name AS committee
FROM members m
INNER JOIN committees c ON c.name = m.name;
Giving below result:
+-----------+--------+--------------+-----------+
| member_id | member | committee_id | committee |
+-----------+--------+--------------+-----------+
| 1 | John | 1 | John |
| 3 | Mary | 2 | Mary |
| 5 | Amelia | 3 | Amelia |
+-----------+--------+--------------+-----------+
Conclusion: The values of the columns are equaly the same
I have an update statement that updates a table with a concatenated name generated from a random combination of three other tables (First, Middle, and Last) to create a random full name. I use alter table Full add unique index(full); to ensure that no name is used twice, and any time it would use a name that is already in use it instead makes it null.
The tables I am using give me a possible 27 unique names. I want to figure out how to loop my update statement so that it fills in every possible unique combination.
db-fiddle table
create table First (id int(10), first varchar(255));
Insert into First (id, first) values (1, 'John'), (2, 'Bill'), (3, 'Chad');
create table Middle (id int(10), middle varchar(255));
Insert into Middle (id, middle) values (1, 'Ethan'), (2, 'Dave'), (3, 'Ron');
create table Last (id int(10), last varchar(255));
Insert into Last (id, last) values (1, 'Smith'), (2, 'Miller'), (3, 'Darnold');
create table Full (id int(10) auto_increment primary key, full varchar(255));
insert into Full (id) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30);
This is my current Update statement
alter table Full add unique index(full);
Update ignore Full u1
join (select id,
#i:=Floor(1+ RAND() * 3),
#j:=Floor(1+ RAND() * 3),
#k:=Floor(1+ RAND() * 3),
(select concat(l.last, ', ', f.first, ' ', m.middle)
from First as f
join Middle as m on m.id = #j
join Last as l on l.id = #k
where f.id = #i) full
from Full) u2
on u1.id = u2.id
set u1.full = u2.full;
I can't loop it as is because it generates a full list of names every time. I need to change it so that after the first run it only updates null names if any possible combinations are left. I have experimented with inserting it into a case statement, but I can't get it to work correctly. As it is, it just changes one of the names in the table to 1, so its obviously not working as intended.
Update ignore Full
join ( select id,
#i:=Floor(1+ RAND() * 3),
#j:=Floor(1+ RAND() * 3),
#k:=Floor(1+ RAND() * 3),
( select concat(l.last, ', ', f.first, ' ', m.middle)
from First as f
join Middle as m on m.id = #j
join Last as l on l.id = #k
where f.id = #i) full
From Full) u2
Set u1.full = Case
when u1.full is null
then u1.full = u2.full
else u1.full = u1.full
end;
If it was working as intended it would fill the all the null rows except 3, since that is all the unique combinations that are possible given my small table size. I want to make sure it's working as intended before I scale up the tables.
Edit:
I don't actually NEED every possible combination. In my actual table I do need every null row filled with a unique combination, and as it is now when a name is duplicated it is null. So I need to fill those while also not duplicating a name already in the table.
Edit2:
I have changed it to this
Update ignore Full f1
join (select id,
(SELECT concat(last, ', ', first, ' ', middle) fullname FROM First,Middle,Last ORDER BY RAND() limit 1) fullname
from Full ) f2
on f1.id = f2.id
set f1.full = f2.fullname;
Which works. Now I just need it to loop until there are no null rows left.
Works for me:
SELECT first, middle, last FROM first,middle,last ORDER BY RAND();
+-------+--------+---------+
| first | middle | last |
+-------+--------+---------+
| Bill | Ethan | Smith |
| John | Dave | Miller |
| Bill | Ron | Darnold |
| Bill | Ethan | Darnold |
| John | Ethan | Miller |
| Chad | Ethan | Darnold |
| Chad | Ethan | Smith |
| Bill | Ron | Miller |
| John | Dave | Darnold |
| Chad | Dave | Miller |
| Chad | Ron | Miller |
| John | Ethan | Darnold |
| Chad | Dave | Smith |
| Bill | Ron | Smith |
| John | Dave | Smith |
| Bill | Ethan | Miller |
| John | Ron | Smith |
| John | Ethan | Smith |
| Bill | Dave | Smith |
| Chad | Dave | Darnold |
| Chad | Ethan | Miller |
| John | Ron | Miller |
| Chad | Ron | Darnold |
| John | Ron | Darnold |
| Bill | Dave | Darnold |
| Bill | Dave | Miller |
| Chad | Ron | Smith |
+-------+--------+---------+
27 rows in set (0.00 sec)
I created a table of employees. Some of those employees report to other employees defined in the same table. Let's call them managers.
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
managerID INT NULL,
FOREIGN KEY (managerID) REFERENCES employees (id) ON DELETE RESTRICT
) ENGINE=INNODB;
Then, some values are entered in the database:
INSERT INTO employees(id, name, managerID)
VALUES (1, "Ivan", NULL),
(2, "Peter", 1),
(3, "Alexandra", 1),
(4, "Maria", 2),
(5, "Marian", 2),
(6, "Margarita", 3),
(7, "Mihail", 3),
(8, "Eva", 6);
I want a SELECT query which returns all employees and all the managers they report to. For now the next best thing I have is this:
SELECT employee.name AS Name, manager.name AS `Reports to:`
FROM employees,
LEFT JOIN employees AS manager ON employee.managerID = manager.id;
Which has predictable output:
Employee Reports to:
Ivan NULL
Peter Ivan
Alexandra Ivan
Maria Peter
Marian Peter
Margarita Alexandra
Mihail Alexandra
Eva Margarita
What I want is a such SELECT query that will return a variable number of columns based on the fact how many managers a single employee has. It's apparent to me that a recursive SELF JOIN will be required, but I don't know how to tell the DBMS I want a variable number of columns for each row and I don't know how to phrase the condition.
Desired output:
I understand that you want to generate a resultset where each record corresponds to an employee, with columns that show all managers of that employee (manager of the employee, then manager of the manager, etc...).
The thing to keep in mind is that a SQL query must return a fixed set of columns. The RDBMS needs to know in advance how many columns will be returned by the query to be able to parse it and execute it.
Here is a solution that can handle up to 5 levels of management. It workds by extending your logic with more LEFT JOINs. It's not pretty, but it works. You can add mor joins to handle more management levels.
SELECT
e0.name AS Name,
e1.name AS Manager1,
e2.name AS Manager2,
e3.name AS Manager3,
e4.name AS Manager4,
e5.name AS Manager5
FROM employees e0
LEFT JOIN employees e1 ON e1.id = e0.managerID
LEFT JOIN employees e2 ON e2.id = e1.managerID
LEFT JOIN employees e3 ON e3.id = e2.managerID
LEFT JOIN employees e4 ON e4.id = e3.managerID
LEFT JOIN employees e5 ON e5.id = e5.managerID
In this DB Fiddle with your sample data, the query returns:
| Name | Manager1 | Manager2 | Manager3 | Manager4 | Manager5 |
| --------- | --------- | --------- | -------- | -------- | -------- |
| Ivan | | | | | |
| Peter | Ivan | | | | |
| Alexandra | Ivan | | | | |
| Maria | Peter | Ivan | | | |
| Marian | Peter | Ivan | | | |
| Margarita | Alexandra | Ivan | | | |
| Mihail | Alexandra | Ivan | | | |
| Eva | Margarita | Alexandra | Ivan | | |
I'm trying to find a way to make my queries easier to read.
Say I have a table: with IDs, names, and partners. Partners is a list of ids.
- 0, john, null
- 1, mike, "0,2"
- 2, sarah, "0,1"
Is there a way I can do a subquery to show names instead of the partner ids?
select u.id, u.name, (select i.name from users i where i.id in u.parners) from users;
Something so I can get results like:
- 0, john, null
- 1, mike, "john,sarah"
- 2, sarah, "john, mike"
I've tried something like what I've shown above, but I can't figure out anything special. Any help would be appreciated!
Normalization is the best way for this. Otherwise, see FIND_IN_SET()
SELECT a.ID,
a.Name,
GROUP_CONCAT(b.Name) Partners
FROM tableName a
LEFT JOIN tableName b
ON FIND_IN_SET(b.id, a.partners)
GROUP BY a.ID, a.Name
SQLFiddle Demo
Suggested schema:
UserList
ID (PK)
Name
other columns
Partners
UserID (FK to UserList.ID)
PartnerID (FK to UserList.ID)
It is better to follow Normalization rules as this is a One to Many relationship.
I have created two tables here
1.users
create table users(
id int not null auto_increment,
name varchar(40) not null,
primary key(id)
);
insert into users( name) values ('John'),('Mike'),('Sarah');
+----+-------+
| id | name |
+----+-------+
| 1 | John |
| 2 | Mike |
| 3 | Sarah |
+----+-------+
2.partners
create table users(
id int not null auto_increment,
name varchar(40) not null,
primary key(id)
);
insert into partners values (2,1),(2,3),(3,1),(3,2);
+---------+------------+
| user_id | partner_id |
+---------+------------+
| 2 | 1 |
| 3 | 1 |
| 3 | 2 |
| 2 | 3 |
+---------+------------+
You can use the following query to select Users and their partner
select u.name Users, group_concat(u1.name) Partners from users u
left join partners p on u.id = p.user_id
left join users u1 on p.partner_id = u1.id
group by Users;
Output:
+-------+------------+
| Users | Partners |
+-------+------------+
| John | NULL |
| Mike | John,Sarah |
| Sarah | John,Mike |
+-------+------------+
I have a number of tables I am trying to combine with joins but as such, the results are returned in a number of rows whereas I would like to have them generated as new columns.
member_information Table
MemberID | FirstName | LastName
---------------------------------
1 | John | Harris
2 | Sarah | Thompson
3 | Zack | Lewis
member_dependent_information Table
MemberID | FirstName | LastName | Type
---------------------------------------
1 | Amy | Harris | 1
2 | Bryan | Thompson | 1
2 | Dewey | Thompson | 2
2 | Tom | Thompson | 2
3 | Harry | Lewis | 2
3 | Minka | Lewis | 1
MySQL Query:
SELECT
t1.FirstName,
t1.LastName,
t1.MemberID,
IF(t2.Type = '1',CONCAT(t2.FirstName,' ',t2.LastName),'') AS Spouse_Name,
IF(t2.Type = '2',CONCAT(t2.FirstName,' ',t2.LastName),'') AS Child_Name,
FROM
member_dependent_information t2
INNER JOIN
member_information t1
USING
(MemberID)
ORDER BY
t1.LastName ASC,
t1.MemberID ASC;
Ideal Results
MemberID | FirstName | LastName | Spouse_Name | Child_Name1 | Child_Name2
--------------------------------------------------------------------------------
1 | John | Harris | Amy Harris | NULL | NULL
2 | Sarah | Thompson | Bryan Thompson | Dewey Thompson | Tom Thompson
3 | Zack | Lewis | Mika Lewis | Harry Lewis | NULL
ACTUAL RESULTS
MemberID | FirstName | LastName | Spouse_Name | Child_Name
-------------------------------------------------------------------
1 | John | Harris | Amy Harris | NULL
2 | Sarah | Thompson | Bryan Thompson | NULL
2 | Sarah | Thompson | NULL | Dewey Thompson
2 | Sarah | Thompson | NULL | Tom Thompson
3 | Zack | Lewis | Mika Lewis | NULL
3 | Zack | Lewis | NULL | Harry Lewis
While my query returns the "correct" data in multiple rows, it does not combine the result into one single row as needed.
The suggestion for Pivot Tables / Crosstabs has been mentioned below but every reference I am able to find suggests using mathematic calculations or that the number of fields to be returned is known. I will not know this information as a single member COULD have up to 100 dependents (although more like 4-8)
UPDATE #1
I feel I am getting closer to the final solution. I added the function GROUP_CONCAT to my query which returns ALL firstnames in a single column and ALL last names in a single column but still need to break them out into their own individual columns.
New function is:
SELECT
t1.MemberID,
t1.FirstName,
t1.LastName,
GROUP_CONCAT(t2.FirstName) AS Dep_Firstnames,
GROUP_CONCAT(t2.LastName) AS Dep_LastNames
FROM
member_information t1
LEFT OUTER JOIN member_dependent_information t2
ON t1.MemberID = t2.MemberID
WHERE
t1.Status = 1
GROUP BY
t1.MemberID
Sometimes the first step to solving your problem is knowing what it's called. After that, it's simply a matter of googling. What you are trying to create is called a pivot table or crosstab report. Here is a link explaining how to create pivot tables in MySQL. And here is a more in depth tutorial.
UPDATE:
Now that you've updated the question, I have a clearer idea of what you are trying to accomplish. I'll give you an alternative solution which is similar but not exactly what you want based on MySQL's GROUP_CONCAT function.
select t1.FirstName, t1.LastName, group_concat(concat(t2.FirstName, ' ', t2.LastName))
from member_information as t1
left outer join member_dependent_information as t2 on t2.MemberID=t1.MemberID
group by t1.MemberID;
I've verified this query as follows. First the setup:
create table member_information (
MemberID int unsigned auto_increment primary key,
FirstName varchar(32) not null,
LastName varchar(32) not null
) engine=innodb;
create table member_dependent_information (
MemberID int unsigned not null,
FirstName varchar(32) not null,
LastName varchar(32) not null,
Type int unsigned not null,
foreign key (MemberID) references member_information(MemberID)
) engine=innodb;
insert into member_information (MemberID, FirstName, LastName) values
(1, 'John', 'Harris'),
(2, 'Sarah', 'Thompson'),
(3, 'Zack', 'Lewis');
insert into member_dependent_information (MemberID, FirstName, LastName, `Type`) values
(1, 'Amy', 'Harris', 1),
(2, 'Bryan', 'Thompson', 1),
(2, 'Dewey', 'Thompson', 2),
(2, 'Tom', 'Thompson', 2),
(3, 'Harry', 'Lewis', 2),
(3, 'Minka', 'Lewis', 1);
And now the query and results:
mysql> select t1.FirstName, t1.LastName, group_concat(concat(t2.FirstName, ' ', t2.LastName))from member_information as t1
-> left outer join member_dependent_information as t2 on t2.MemberID=t1.MemberID
-> group by t1.MemberID;
+-----------+----------+------------------------------------------------------+
| FirstName | LastName | group_concat(concat(t2.FirstName, ' ', t2.LastName)) |
+-----------+----------+------------------------------------------------------+
| John | Harris | Amy Harris |
| Sarah | Thompson | Bryan Thompson,Dewey Thompson,Tom Thompson |
| Zack | Lewis | Harry Lewis,Minka Lewis |
+-----------+----------+------------------------------------------------------+
3 rows in set (0.00 sec)
I'm not sure how the OP transformed from having programID and Status to how it is now, but the closest thing I would be able to get is (which does not require pivot tables):
SELECT t1.MemberID,
t1.FirstName,
t1.LastName,
concat(t2.FirstName, ' ', t2.LastName) as Spouse_Name,
group_concat(concat(t3.FirstName, ' ', t3.LastName) ORDER BY t3.FirstName) as Children_names
FROM member_information t1
LEFT JOIN member_dependent_information t2 ON (t1.MemberID=t2.MemberID AND t2.Type=1)
LEFT JOIN member_dependent_information t3 ON (t1.MemberID=t3.MemberID and t3.Type=2)
GROUP BY MemberID;
Which produces:
+----------+-----------+----------+----------------+-----------------------------+
| MemberID | FirstName | LastName | Spouse_Name | Children_names |
+----------+-----------+----------+----------------+-----------------------------+
| 1 | John | Harris | Amy Harris | NULL |
| 2 | Sarah | Thompson | Bryan Thompson | Dewey Thompson,Tom Thompson |
| 3 | Zack | Lewis | Minka Lewis | Harry Lewis |
+----------+-----------+----------+----------------+-----------------------------+
and it would be "easy" with any programming language to extract that Children_names into separate columns.
This is not exactly what you are looking for, but it might be a step in the right direction.
SELECT
Table1.memberIDs,
Table1.firstname,
Table1.lastnames,
Table2.programIDs,
Table3.description
FROM
Table1,
Table2,
Table3
WHERE
Table1.memberIDs = Table2.memberIDs AND
Table2.programIDs = Table3.programID