issue with a complex Sql join query - mysql

I have two table that I need to combine in a specific way. I need the information in one table displayed once but I need the information in the next table to return every matching data it has. here is my example
user
| id | name |
-------------
| 1 | mike |
| 2 | john |
| 3 | bill |
-------------
class
-----------------
| uid | subject |
-----------------
| 1 | math |
| 1 | English |
| 2 | math |
| 3 | math |
| 3 | English |
-----------------
expected results
--------------------
|name | mike |
---------------------
|subject| math |
| | English |
---------------------
or
--------------------
|name | subject |
---------------------
|mike | math |
| | English |
---------------------
this is my current query
SELECT user.name, class.subject
FROM user
join class on class.uid = user.id
WHERE (user.name like '%mike%')
returns
--------------------
|name | subject |
---------------------
|mike | math |
|mike | English |
---------------------
but I want to remove all name duplicates. can anyone help with this query

You could concatenate the values into a delimited string with a join type function... e.g.
Select u.name, (
select
GROUP_CONCAT(c.subject) FROM classes c where u.id = c.uid
) As subjects
From user u
so you end up with
--------------------
|name | subjects |
---------------------
|mike | math, English |

set #lastname = null;
select case when name = #lastname
then ""
else #lastname := name
end name,
subject
from (select name, subject
from user
join class on id = uid
where name like '%mike%'
order by name) x
FIDDLE
However, as others pointed out in the comments, this is usually done in the application code, not in the SQL. In your loop that processes rows, just check if the name is the same as the previous one, and leave that table field blank. Or use a grid widget that automates grouping for you.

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 :))

Replace foreign key IDs in table with values from those tables

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.

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: get result from 2 tables

I have 2 tables.
Phonebook:
_________________________________
| id | Photo | Name | Number |
|---------------------------------|
| 1 | abc.jpg | John | 123-45-67 |
|---------------------------------|
| 2 | def.jpg | Sam | 482-34-00 |
|_________________________________|
History:
____________________________________
| id | Name | Date | Type |
|------------------------------------|
| 24 | John | 17.03.2014 | Incoming |
|------------------------------------|
| 25 | Sam | 18.03.2014 | Incoming |
|------------------------------------|
| 26 | Sam | 19.03.2014 | Outgoing |
|____________________________________|
I need to get all columns from History table where id = $id (25 in my case) and get Image column from Phoneboock where History.Name = Phonebook.Name (Sam in my case). Below is the result that I want to get:
______________________________________________
| id | Name | Date | Type | Image |
|----------------------------------------------|
| 25 | Sam | 18.03.2014 | Incoming | def.jpg |
|______________________________________________|
It seems your problem is the SQL to get your data, so use this:
SELECT History.*, Phonebook.Photo
FROM History INNER JOIN Phonebook ON Phonebook.Name = History.Name
AND History.id = $id
You can use JOIN as follows:
SELECT * FROM History INNER JOIN Phonebook WHERE History.name = Phonebook.name
That will give you a join of all the rows. You can customize it more to get what you want. Look at JOIN here for more info
If you are looking for the SQL Select statement, it would look something like this:
SELECT HISTORY.*, PHONEBOOK.Photo FROM HISTORY, PHONEBOOK
WHERE HISTORY.Name = PHONEBOOK.Name AND HISTORY.id='25'

coalesce problems

i have 2 tables for example
user_table level_table
================== ===================
| name | levelid | | id | levelname |
================== ===================
| tom | 1 | | 1 | beginner |
------------------ -------------------
| jon | 2 | | 2 | intermed |
------------------ -------------------
| tom | null | | 3 | expert |
------------------ -------------------
| jon | 1 |
------------------
i need to select user table with levelname only where if null, it will show my defined text such 'no level'. and there's no identical column header.
so it would appear like the left one instead of this right one.
Right result Wrong result
================== ==================
| name | level | | name | level |
================== ==================
| tom | beginner | | tom | beginner |
------------------ ------------------
| jon | intermed | | jon | beginner |
------------------ ------------------
| tom | no level | | tom | beginner |
------------------ ------------------
| jon | beginner | | jon | beginner |
------------------ ------------------
i've tried coalesce but seems there's a mistake where all levelname rows shows the same result.
SELECT name,
COALESCE(
(SELECT levelname FROM user_table,level_table WHERE levelid=id),
'no level') AS level
FROM user_table
It should be like this, you need to join both tables using LEFT JOIN
SELECT a.name, COALESCE(b.levelname, 'no level') levelname
FROM user_table a
LEFT JOIN level_table b
ON a.levelid = b.id
The difference between LEFT JOIN and INNER JOIN is that LEFT JOIN return all rows on the left table whether it has a match on the right table or not while INNER JOIN only return rows that are present on both tables.
Visual Representation of JOINS