MySQL select row and join to multiple records - mysql

I have 2 Tables and can't get my MySQL Query to work:
First Table users:
user_id name
------------
1 Bob
2 Alice
Second Table fields:
id unser_id field_id value
-----------------------------
1 1 4 foo
2 1 5 10
3 2 4 bar
Now I would like to join the Tables in a way that outputs:
[0] => (
[user_id] => 1
[name] => Bob
[fields] => (
[4] => foo
[5] => 10
)
[1] => (
[user_id] => 2
[name] => Alice
[fields] => (
[4] => bar
)
)
Thanks a lot!
I tried:
SELECT u.user_id,
u.name
FROM users as u
INNER JOIN fields as f on u.user_id = d.user_id
But I get for every user just the first fields entry.

SELECT user_id,name,GROUP_CONCAT(value SEPARATOR ',')as field FROM users u JOIN fields f
ON u.user_id=f.unser_id GROUP BY user_id
Maybe something like this?

select u.user_id,u.name,f.field_id,f.value from users u LEFT JOIN fields f ON u.user_id = f.unser_id

Related

Joining two tables and create virtual columns by their type

I am currently developing some kind of calendar for scholar exams. Because of the calendar being very slow at the moment, I wanted to enhance the MySQL behind it.
I've read about JOINs and tried to used them in order to directly get the results I need but I am currently stuck at a problem.
I have two tables (simplified)
DAYS EVENTS
id | date id | day_id | class | text
1 | 2016-02-05 1 | 1 | a | Example
2 | 2016-02-06 2 | 1 | b | Example for Class b
etc.
Now, I'd like to receive the following result if I select a day:
[0] => Array
(
[id] => 1
[date] => 2016-01-25
[a] => 1 (ID of the Event on that day with the class 'a')
[b] => 2 (ID of the Event on that day with the class 'b')
[c] => NULL
)
But my query I created as far does not merge the results:
[0] => Array
(
[id] => 1
[date] => 2016-02-05
[a] => 13
[b] =>
[c] =>
)
[1] => Array
(
[id] => 11
[date] => 2016-02-05
[a] =>
[b] => 14
[c] =>
)
How can I achvieve the two results being merged? My query looks like this:
SELECT d.*,
CASE WHEN e.class = 'a' THEN e.id ELSE NULL END AS a,
CASE WHEN e.class = 'b' THEN e.id ELSE NULL END AS b,
CASE WHEN e.class = 'c' THEN e.id ELSE NULL END AS c
FROM examplan_days d
LEFT JOIN examplan_events e ON d.id = e.day_id
I think you want aggregation:
SELECT d.*,
MAX(CASE WHEN e.class = 'a' THEN e.id END) AS a,
MAX(CASE WHEN e.class = 'b' THEN e.id END) AS b,
MAX(CASE WHEN e.class = 'c' THEN e.id END) AS c
FROM examplan_days d LEFT JOIN
examplan_events e
ON d.id = e.day_id
GROUP BY d.id;
If you can have more than one class on a given day, then you should use GROUP_CONCAT() rather than MAX().

MySQL GROUP BY kills ORDER BY

I have a problem and there are already some similar questions but they didn't solve my problem.
I have several hotels and pictures in it. The pictures are ordered by a number. I want all hotels and the picture with the lowest number.
When I try following query I get the right order:
SELECT s25.entry_id AS id,
s25.value AS title,
s35.file AS picture,
s86.value AS picture_sort
FROM sym_entries_data_25 AS s25
LEFT JOIN sym_entries_data_34 AS s34 ON (s25.entry_id = s34.relation_id)
LEFT JOIN sym_entries_data_35 AS s35 ON (s34.entry_id = s35.entry_id)
LEFT JOIN sym_entries_data_86 AS s86 ON (s34.entry_id = s86.entry_id)
ORDER BY s86.value
I will get the Array
[0] => Array
(
[id] => 243
[title] => Hotel
[picture] => louis2.jpg
[picture_sort] => 1
)
[1] => Array
(
[id] => 243
[title] => Hotel
[picture] => louis1.jpg
[picture_sort] => 2
)
[2] => Array
(
[id] => 243
[title] => Hotel
[picture] => louis3.jpg
[picture_sort] => 3
)
...And so on
But I only want one result per hotel, so I tried a GROUP BY:
SELECT s25.entry_id AS id,
s25.value AS title,
s35.file AS picture,
s86.value AS picture_sort
FROM sym_entries_data_25 AS s25
LEFT JOIN sym_entries_data_34 AS s34 ON (s25.entry_id = s34.relation_id)
LEFT JOIN sym_entries_data_35 AS s35 ON (s34.entry_id = s35.entry_id)
LEFT JOIN sym_entries_data_86 AS s86 ON (s34.entry_id = s86.entry_id)
GROUP BY s25.value
ORDER BY s86.value
Then I only get one Result but a random one, not the first one:
[0] => Array
(
[id] => 243
[title] => Hotel
[picture] => louis3.jpg
[picture_sort] => 3
)
What could be the problem here?
when you are doing group by, you have no guarantee regarding the values of the columns you are not grouping by.
for example if you have a table with columns a and b, and data like:
a | b
-----
1 | 2
1 | 3
if you'll do select * from table group by a, you can get either 2 or 3 as the value of b.
if you only want the top result for the hotel, what you need to do is not group by, but take the result where picture_sort equals to the min(picture_sort)

mysql ColumnName AS 1, ColumnName AS 2, with WHERE, using UNION for all columns get AS 1

Have table like this
IdRows | UpperLevelIdRows | CategoriesName |
-------------------------------------------------
2 | 0 | Transport
4 | 2 | Cars
12 | 4 | Alfa Romeo
Query
SELECT IdRows AS IdRows1, CategoriesName AS CategoriesName1 FROM categories
WHERE UpperLevelIdRows = ?
UNION
SELECT IdRows AS IdRows2, CategoriesName AS CategoriesName2 FROM categories
WHERE UpperLevelIdRows = ?
Data for placeholders is
Array
(
[0] => 2
[1] => 4
)
So
SELECT IdRows AS IdRows1 .... WHERE UpperLevelIdRows = 2
and
SELECT IdRows AS IdRows2 .... WHERE UpperLevelIdRows = 4
As result expect get array like
[0] => Array
(
[IdRows1] => 4
[CategoriesName1] => Cars
)
[1] => Array
(
[IdRows2] => 12
[CategoriesName2] => Alfa Romeo
)
But get array like this
[0] => Array
(
[IdRows1] => 4
[CategoriesName1] => Cars
)
[1] => Array
(
[IdRows1] => 12
[CategoriesName1] => Alfa Romeo
)
Instead of IdRows2 see IdRows1
If i execute only the second SELECT IdRows AS IdRows2 ..., then see as expected [CategoriesName2] => Alfa Romeo
Where is my mistake? What need to correct?
From the data i want to create select/option boxes. Like
First select box
echo '<select name="upper_level_id0" id="upper_level_id0" >
<option value="'.$IdRows1.'">'.$CategoriesName1.'</option>
</select>';
Second select box
echo '<select name="upper_level_id1" id="upper_level_id1" >
<option value="'.$IdRows2.'">'.$CategoriesName2.'</option>
</select>';
At the moment found solution using transaction. Loop through all SELECT ...
$db->beginTransaction();
foreach ( $sql_get_id_name as $k_sql => $val_sql ) {
$stmt_get_id_name = $db->prepare( $val_sql );
$stmt_get_id_name->execute( array( $data_get_id_name[$k_sql] ) );
$id_name[] = $stmt_get_id_name->fetchAll(PDO::FETCH_ASSOC);
}
$roll_back = $db->rollBack();
Use this select
select cp.IdRows p_id, cp.UpperLevelIdRows p_parent_id, cp.CategoriesName p_name,
cc.IdRows p_id, cc.UpperLevelIdRows c_parent_id, cc.CategoriesName c_name
from categories cc left join categories cp on cp.IdRows = cc.UpperLevelIdRows
where cc.UpperLevelIdRows = 4
This way you will get all the sons of a parent. The first 3 columns from the result are the parent (in your row data it will be the Cars) and the second its childs (in your row data the Alfa Romeo)

SQL: GROUP BY after JOIN without overriding rows?

I have a table of basketball leagues, a table af teams and a table of players like this:
LEAGUES
ID | NAME |
------------------
1 | NBA |
2 | ABA |
TEAMS:
ID | NAME | LEAGUE_ID
------------------------------
20 | BULLS | 1
21 | KNICKS | 2
PLAYERS:
ID | TEAM_ID | FIRST_NAME | LAST_NAME |
---------------------------------------------
1 | 21 | John | Starks |
2 | 21 | Patrick | Ewing |
Given a League ID, I would like to retrieve all the players' names and their team ID from all the teams in that league, so I do this:
SELECT t.id AS team_id, p.id AS player_id, p.first_name, p.last_name
FROM teams AS t
JOIN players AS p ON p.team_id = t.id
WHERE t.league_id = 1
which returns:
[0] => stdClass Object
(
[team_id] => 21
[player_id] => 1
[first_name] => John
[last_name] => Starks
)
[1] => stdClass Object
(
[team_id] => 21
[player_id] => 2
[first_name] => Patrick
[last_name] => Ewing
)
+ around 500 more objects...
Since I will use this result to populate a dropdown menu for each team containing each team's list of players, I would like to group my result by team ID, so the loop to create these dropdowns will only have to cycle through each team ID instead of all 500+ players each time.
But when I use the GROUP BY like this:
SELECT t.id AS team_id, p.id AS player_id, p.first_name, p.last_name
FROM teams AS t
JOIN players AS p ON p.team_id = t.id
WHERE t.league_id = 1
GROUP BY t.id
it only returns one player from each team like this, overriding all the other players on the same team because of the use of the same column names.
[0] => stdClass Object
(
[team_id] => 21
[player_id] => 2
[first_name] => Patrick
[last_name] => Ewing
)
[1] => stdClass Object
(
[team_id] => 22
[player_id] => 31
[first_name] => Shawn
[last_name] => Kemp
)
etc...
I would like to return something like this:
[0] => stdClass Object
(
[team_id] => 2
[player_id1] => 1
[first_name1] => John
[last_name1] => Starks
[player_id2] => 2
[first_name2] => Patrick
[last_name2] => Ewing
+10 more players from this team...
)
+25 more teams...
Is it possible somehow?
You cannot do this in SQL, since you cannot represent that result in a form of data set. You want to return complex object. What you can do, is to handle this in the code, and help yourself by returning a data set which is sorted by team_id. Whenever your team_id changes, then it is time to create new object in your code and fill it with new list of players.
It would be something like this (syntax might not be correct):
Returned result set:
team_id|player_id|first|last
1|1|f1|l1
1|2|f2|l2
1|3|f3|l3
2|5|f5|l5
2|6|f6|l6
And when this is returned in your code
$lastTeamId=0;
$output=array();
foreach($results as $row){
if($lastTeamId != $row["team_id"]){
$lastTeamId = $row["team_id"];
$output[$lastTeamId] = array();
}
$newPlayer = null;
$newPlayer->id = $row["player_id"];
$newPlayer->first = $row["first"];
$newPlayer->last = $row["last"];
$output[$lastTeamId][] = $newPlayer;
}
In MySQL, you could GROUP BY team_id and then SELECT GROUP_CONCAT(player detail ...). But that runs into restrictions and is not the typical relational approach.

Creating a messages list in SQL

I'm trying to create a messages list, like Facebook.
Showing only last the message from a users conversation (history)
when I send show mine , when I get answer show answered messages (1 row)
database data :
SQL query
SELECT m.message_id, u.username, m.subject, m.message
, m.status, UNIX_TIMESTAMP(m.date) as date
FROM users u
LEFT JOIN messages m ON m.sender_id = u.id
WHERE m.message_id in (SELECT max(msg.message_id)
FROM messages msg
WHERE msg.receiver_id = 3
GROUP BY sender_id)
UNION
SELECT m.message_id, u.username, m.subject, m.message
, m.status, UNIX_TIMESTAMP(m.date) as date
FROM users u
LEFT JOIN messages m ON m.receiver_id = u.id
WHERE m.message_id in (SELECT max(msg.message_id)
FROM messages msg
WHERE msg.sender_id = 3
GROUP BY receiver_id)
GROUP BY u.username
ORDER BY date DESC
I try to receive all messages which I send (my id = 3) and all those sent to me and group by username
SQL result:
Array
(
[0] => Array
(
[message_id] => 10
[username] => 8888
[subject] => без темы
[message] => 555
[status] => 0
[date] => 11 August 2012, 2:22
[user_image] => http://127.0.0.1/auth_system_1/upload_images/65_empty.jpg
)
[1] => Array
(
[message_id] => 7
[username] => 8888
[subject] => hi
[message] => 333
[status] => 0
[date] => 11 August 2012, 2:15
[user_image] => http://127.0.0.1/auth_system_1/upload_images/65_empty.jpg
)
[2] => Array
(
[message_id] => 4
[username] => 6666
[subject] => Тема
[message] => 2
[status] => 0
[date] => 11 August 2012, 2:02
[user_image] => http://127.0.0.1/auth_system_1/upload_images/65_empty.jpg
)
[3] => Array
(
[message_id] => 1
[username] => fffffffff
[subject] => privet
[message] => tttt
[status] => 0
[date] => 11 August 2012, 1:38
[user_image] => http://127.0.0.1/auth_system_1/upload_images/65_empty.jpg
)
)
As you can see GROUP BY username do not work. It shows 3->7 and 7->3 but 7->3 was an answer and last message. I do not know why group does not work. Maybe you can help me with more easier way to solve this problem?
So, the SQL must have "sender_id = 3" , "receiver_id = 3" and from my table data result must be
message_id -> 1
message_id -> 4
message_id -> 10
I think your query is producing the “right” results, as if you'd like to see the last message in some of the conversations, you should really group by the conversation_id. I don't see this field you in schema though.
If you do WHERE sender_id = 3 GROUP BY receiver_id, then it is correct, that query returns you messages 1 and 7, 'cos those messages had been sent to different people, thus in your design they're different conversations.
If you want to see only the very last message sent by you in general, just remove GROUP BY in the second part of your UNION. Otherwise, consider re-designing your schema.
EDIT:
Try this query:
SELECT m.message_id, u.username, m.subject, m.message,
m.status, UNIX_TIMESTAMP(m.date) as `date`
FROM users u
LEFT JOIN messages m ON m.sender_id = u.id
WHERE m.message_id IN (
SELECT max(message_id)
FROM messages
WHERE receiver_id = 3 OR sender_id = 3
GROUP BY least(sender_id,receiver_id),
greatest(sender_id,receiver_id)
);
Some notes:
UNION is not needed anymore;
This approach will treat all e-mails between 2 parties as a single conversation, which is not always true. You might want to re-design this approach;
It is a bad style to use reserved words (like date) for columns' names or aliases, try to avoid this. Or use backticks if you do use them.