Replace foreign key IDs in table with values from those tables - mysql

I have a database I extracted from an application (I did not create it). I need to extract the data to use in a different application. The main table contains 3 foreign keys each linking to other tables. I want to replace the FK IDs with a specific column value from those other tables. The main table looks like this:
data:
+------------+------------+--------+----------+-----------+----------+------+
| startdate | enddate | status | employee | cause | duration | type |
+------------+------------+--------+----------+-----------+----------+------+
| 2018-01-05 | 2018-01-07 | 2 | 1 | some text | 3 | 1 |
+------------+------------+--------+----------+-----------+----------+------+
| 2018-02-03 | 2018-02-04 | 1 | 2 | some text | 2 | 2 |
+------------+------------+--------+----------+-----------+----------+------+
status, employee and type are all foreign keys. They link to tables with the following layouts:
users:
+----+-----------+----------+
| id | firstname | lastname |
+----+-----------+----------+
| 1 | John | Doe |
+----+-----------+----------+
| 2 | Dylan | Smith |
+----+-----------+----------+
types:
+----+-----------+
| id | name |
+----+-----------+
| 1 | Annual |
+----+-----------+
| 2 | Quarterly |
+----+-----------+
status:
+----+----------+
| id | name |
+----+----------+
| 1 | Accepted |
+----+----------+
| 2 | Rejected |
+----+----------+
I want the main table to then look like this is
+------------+------------+-----------+--------------+-----------+----------+-----------+
| startdate | enddate | status | employee | cause | duration | type |
+------------+------------+-----------+--------------+-----------+----------+-----------+
| 2018-01-05 | 2018-01-07 | Rejected | John Doe | some text | 3 | Annual |
+------------+------------+-----------+--------------+-----------+----------+-----------+
| 2018-02-03 | 2018-02-04 | Accepted | Dylan Smith | some text | 2 | Quarterly |
+------------+------------+-----------+--------------+-----------+----------+-----------+
I've tried various things I've found on Google as well as SO but I can't get it to come out the way I want it to. I come from a PHP background but I have some experience in SQL, but I am not an expert. I've tried getting the values corresponding to the IDs and saving them in a different table, but I don't know how I can update the table with those returned values:
INSERT into stat
SELECT b.name stat
FROM data a JOIN status b
ON a.status = b.id
This creates a column with the right values in the right order in a table called "stat", like so:
+----------+
| status |
+----------+
| Rejected |
+----------+
| Accepted |
+----------+
This works similarly for the "users" (I also concatenate the first name and last names into a column called "name" in a "user" table. I want to use the concatenated value) and "types" tables. How can I update each respective FK value in the "data" table with their respective values as in the tables above?

Joining from the data table to the three other tables should work here:
SELECT
d.startdate,
d.enddate,
s.name AS status,
CONCAT(u.firstname, ' ', u.lastname) AS employee,
d.cause,
d.duration,
t.name AS type
FROM data d
LEFT JOIN status s
ON d.status = s.id
LEFT JOIN users u
ON d.employee = u.id
LEFT JOIN types t
ON d.type = t.id;
If a given value coming in from one of the joins be missing, and you want to display a placeholder value (instead of the default NULL), then you may use the COALESCE function.

Related

How can i loop through result from mysql query?

I have two tables one for properties and one for users. and I wanna generate a new table that contains propertyID with user name, So i want to loop through the users ids from the properties table then do a query to get the name and assign it to the propertyID like the final table.
Properties
| propertyID | userID |
|------------|--------|
| 123 | 2 |
| 453 | 4 |
| 345 | 6 |
| 378 | 6 |
Users
| userID | name |
|--------|-------|
| 2 | zeyad |
| 4 | test |
| 6 | elan |
Desired Table
| propertyID | name |
|------------|-------|
| 123 | zeyad |
How can i do this?
just like this
CREATE TABLE desiredTable AS
SELECT p.propertyID, u.name FROM Properties p
LEFT JOIN Users u
ON u.userID = p.userID
Careful to the column name you use ("name" is a SQL keyword, use username instead :))

Is it possible to join multiple tables with one to many relations to the primary table without duplicated records?

Person Table
+----+------+
| id | name |
+----+------+
| 1 | John |
+----+------+
Car Table
+----+----------+
| id | car |
+----+----------+
| 1 | Audi |
| 1 | BMW |
| 1 | Mercedes |
+----+----------+
Credit_Card Table
+----+-----------------+
| id | bank |
+----+-----------------+
| 1 | Scotiabank |
| 1 | Bank of America |
+----+-----------------+
the id in Person table is the foreign key for the other two, I wonder if there is a way to join these tables together without showing duplicated records like this:
+----+------+----------+-----------------+
| id | name | car | bank |
+----+------+----------+-----------------+
| 1 | John | Audi | Scotiabank |
|NULL| NULL | BMW | Bank of America|
|NULL| NULL | Mercedes | NULL |
+----+------+----------+-----------------+
You can do this, but you shouldn't. Instead, I'l recommend putting all the values in one row:
select p.id, p.name,
(select group_concat(c.car)
from car c
where c.id = p.id
) as cars,
(select group_concat(b.bank)
from banks b
where b.id = p.id
) as banks
from person p ;
Why shouldn't you put the result in multiple rows as you specify? Well, it is definitely possible. However, the columns in a row should "related" to each other (this is much of what the "relational" in "relational algebra" is talking about). When you replace the first columns with NULL, then they no longer have any relationship to the rest of the columns. Similarly, the values in the last two columns have nothing to do with each other.
Plus, I'm guessing that the above pretty much does what you really want.

How to change the integer to string in a lookup table?

I cannot understand why joining tables are so difficult for me... But i would like to change a foreign key (platform_id) to the name from the lookup table. Dont know if this is the best approache, but should give me the best scaling options in the future.
Table companys
+----+--------+-------------+
| id | name | platform_id |
+----+--------+-------------+
| 1 | apple | 1 |
| 2 | google | 2 |
+----+--------+-------------+
Table platforms
+----+----------+-------------------+
| id | name | data |
+----+----------+-------------------+
| 1 | Software | {'market cap':''} |
| 2 | Retail | {'market cap':''} |
| 3 | Medicin | {'market cap':''} |
+----+----------+-------------------+
Expected result
+----+--------+-------------+
| id | name | platform_id |
+----+--------+-------------+
| 1 | apple | software |
| 2 | google | retail |
+----+--------+-------------+
What if i would like to also have the data column from platsforms table?
Use JOIN & create a view :
CREATE VIEW company_view
AS
SELECT c.id, c.name, p.name as platform_id, p.data
FROM company c INNER JOIN
platform p
ON p.id = c.platform_id;
If you want to change foreign key constraint to string value instead of platform_id. So, it is not advisable. Think if you need to change the name of platforms, you need to change ever where instead of one table.

Select max value in subquery

I have these two tables:
Student:
| name | email |
|---------------------|-------------------------|
| Arturo Vidal | arturo.vidal#usm.cl |
| Bastian Quezada | bastian#usm.cl |
| Javier Jeria | javier#usm.cl |
| Sebastian Piñera | sebastian#presidente.cl |
| Sebastian Gallardo | sebastian#usm.cl |
Class:
| classId | email | signUpDate |
|---------|-------------------------|-------------|
| 1 | sebastian#usm.cl | 2018-01-01 |
| 1 | javier#usm.cl | 2019-10-01 |
| 1 | bastian#usm.cl | 2018-07-01 |
| 2 | sebastian#usm.cl | 2018-05-04 |
| 2 | bastian#usm.cl | 2018-01-01 |
| 3 | bastian#usm.cl | 2018-12-05 |
| 3 | sebastian#usm.cl | 2018-02-01 |
| 4 | arturo.vidal#usm.cl | 2018-03-01 |
| 5 | sebastian#presidente.cl | 2018-03-01 |
I want to show the name the last student that signed up for each classId. That means, I should get a name for classId 1, one for classId 2, etc. My solution for firstly getting the mails (to know the student's name after) is this:
select classId, email, max(signUpDate)
from Class
group by classId
it prints the max date, which is ok, but it also prints the wrong mails for each date:
| ClassId | email | max(signUpDate) |
|---------|-------------------------|-----------------|
| 1 | sebastian#usm.cl | 2019-10-01 |
| 2 | sebastian#usm.cl | 2018-05-04 |
| 3 | bastian#usm.cl | 2018-12-05 |
| 4 | arturo.vidal#usm.cl | 2018-03-01 |
| 5 | sebastian#presidente.cl | 2018-03-01 |
which is completely wrong (). Therefore, when I try to join the the values for getting the names, I get incorrect values.
In other words, I don't understand why are the rows mixing up. Is there any solution for getting correct emails for the max(signUpDate) for each ClassId?
Thanks for your time
i have created the test data fiddle and made an easy and understandable query to fetch the required data, i.e:
SELECT DISTINCT classId,
std.name,
Class.email,
signUpDate
FROM CLASS
INNER JOIN Student std ON std.email = Class.email
WHERE signUpDate IN
(SELECT max(signUpDate)
FROM CLASS
GROUP BY classId)
Sql Fiddle here
This is an instance of a very common class of questions: find the whole row FOR EACH GROUP of the field that maximizes some value (in the group). In your case, you want to GROUP BY the ClassId, and FOR EACH ONE OF THESE GROUPS, you want the whole row of the field with the maximum signupDate.
SHORT ANSWER: You can use this query:
SELECT
C.ClassId,
S.name
FROM
(
SELECT A.*
FROM Class AS A
LEFT JOIN Class AS B
ON A.email = B.email AND A.signupDate < B.signupDate
WHERE B.email IS NULL
) AS C
LEFT JOIN Student AS S ON S.email=C.email
LONG ANSWER:
Here you can find a very clear explanation of what I have just said.
Assuming that we can use the e-mail at your tables as unique identifier, you can do FIRST a join (on the e-mail field) of the table "Class" with itself, to select the "maximum date" for each class id. After that, you join (on the e-mail field) with the table "Student". After that, you will have a table with all the fields of the "Class" table and all the fields of the "Student" table. You can select the fields that you need. In the following example, I will select "Class.classId" and "Student.name"
If you run this query:
SELECT A.*
FROM Class AS A
LEFT JOIN Class AS B
ON A.email = B.email AND A.signupDate < B.signupDate
WHERE B.email IS NULL
You obtain this table:
+---------+-------------------------+------------+
| ClassId | email | signupDate |
+---------+-------------------------+------------+
| 1 | javier#usm.cl | 2019-10-01 |
| 2 | sebastian#usm.cl | 2018-05-04 |
| 3 | bastian#usm.cl | 2018-12-05 |
| 4 | arturo.vidal#usm.cl | 2018-03-01 |
| 5 | sebastian#presidente.cl | 2018-03-01 |
+---------+-------------------------+------------+
Now you can join this with the table "Student", and select the fields that you want. If you run the query provided in the "short answer" part of this post, you get the following result:
+---------+--------------------+
| ClassId | name |
+---------+--------------------+
| 4 | Arturo Vidal |
| 3 | Bastian Quezada |
| 1 | Javier Jeria |
| 5 | Sebastian Piñera |
| 2 | Sebastian Gallardo |
+---------+--------------------+
Try this:
SELECT A.classId, C.name, C.email, B.signUpDate
FROM
(SELECT classId, max(signUpDate) maxSignUpDate
FROM Class
GROUP BY classId) A JOIN Class B
ON A.classId=B.classId AND A.maxSignUpDate=B.signUpDate
JOIN Student C ON C.email=B.email;
I assume the email to be the ID field of the Student table. See MySQL Join Made Easy and MySQL GROUP BY for insights.
See it run on SQL Fiddle.

MySQL How can I make a better query to connect two tables through my bridge table?

I have these tables:
words:
+----+------+
| ID | DATA |
+----+------+
| 1 | jo |
| 2 | yes |
| 3 | jupp |
| 4 | yeah |
| 5 | jepp |
| 6 | joah |
| 7 | ne |
| 8 | nee |
| 9 | no |
| 10 | nope |
| 11 | nah |
+----+------+
statements:
+----+------+
| ID | DATA |
+----+------+
| 1 | ja |
| 2 | nein |
+----+------+
and a bridge table that connects the words from table "words" with the DATA from table "statements":
bridge:
+--------------+---------+
| ID_statement | ID_word |
+--------------+---------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 6 |
| 2 | 8 |
| 2 | 9 |
+--------------+---------+
I wanted to get a SELECT QUERY to get all words associated with the statement "ja".
this query does the job, but seems more complicated than it should be:
SELECT words.DATA FROM words
JOIN bridge ON words.ID = bridge.ID_word
JOIN statements ON statements.ID = bridge.ID_statement
WHERE statements.ID = (
SELECT ID FROM statements WHERE statements.DATA = "ja"
);
Intuition tells me that I am doing that far to comlicated, but I can't figure out where the complication lies.
awkward.
I like to name the primary keys the same as the columns that reference them. So in your example, in the words table, you'd name the primary key ID_word. In the statements table, you'd name the primary key ID_statement.
The advantage is that you can make your SQL join a little more concise with the USING(...) syntax. This syntax assumes there's a column by that name in both tables of a join, and you want the join to match where the column is equal to the same column in the other table.
SELECT words.DATA FROM words
JOIN bridge USING (ID_word)
JOIN statements USING (ID_statement)
WHERE statements.DATA = 'ja';
Also you don't need to run a subquery in your example. The rows in statement matching the IDs from the rows in statement where DATA='ja' are the same set as the rows in statement where DATA='ja'.
how would one describe the relationship here? one-to-many?
The relationship modeled by a bridge table is a many-to-many relationship. The specific data in your example doesn't show it, but it's possible that many different statements could reference the same word. What you do show is that each statement can reference many words.