MySQL/PHP query to reverse relationship - php

I have a mysql table with the following columns:
ID Units
1 1234,6543,9876
2 1234,6543
3 6543
4 9876
5 0987
I would like to reverse the relationship to get an output like this:
Unit IDs
1234 1,2
6543 1,2,3
9876 1,4
0987 5
I was wondering if this could be done in a query or some php, without chunking through with explodes etc?

Using comma-separated lists in SQL is awkward. This is a denormalized design, and SQL is not well suited to work with data in this format.
I would fetch all the data back into PHP and manipulate it there.
$id_per_unit = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$unit_array = explode(",", $row["Units"]);
foreach ($unit_array as $unit) {
$id_per_unit[$unit][] = $row["Id"];
}
}

Something like this:
$query = "SELECT `Unit`, `IDs` FROM `table` ORDER BY `Unit`";
$data = mysqli_query($con, $query);
$prev_unit = '';
while ($row = mysqli_fetch_array($data)) {
if ($prev_unit != $row['Unit']) {
// echo a new row with the new unit and then ID
} else {
// echo just the ID in the same row, this unit is the same as the last one.
}
$prev_unit = $row['Unit'];
}

With only SQL, you can do something like this :
SELECT unit , GROUP_CONCAT(id)
FROM (
SELECT id,substring_index(Units,',',1) AS unit
FROM Table1
UNION
SELECT id,REPLACE(
REPLACE(SUBSTRING_INDEX(Units,',',2),SUBSTRING_INDEX(Units,',',1),'')
,',','') AS unit
FROM Table1
UNION
SELECT id,REPLACE(
REPLACE(SUBSTRING_INDEX(Units,',',3),SUBSTRING_INDEX(Units,',',2),'')
,',','') AS unit
FROM Table1) AS UNITS
WHERE unit != ''
GROUP BY unit
See SQLFIDDLE

Related

Best practice for writing code in php for foreach->foreach->if(1st loop id == 2nd loop id)->result

First loop table
user_id | fname | lname
1 | first | emp
2 | second| emp
3 | third | emp
Second loop table
shift_id | employee_id
1 | 1
2 | 2
3 | 2
if($employees)
{
foreach ($employees as $employee)
{
if($employee['user_id'] == $shift['employee_id'])
{
echo ucwords($employee['fname']. ' ' .$employee['lname']);
}
}
}
I am getting the right result but I think there is some better way of writing this.
You can use joins in table. Left join means that the user line has to exists (because: LEFT) and the shifts enty is optional.
SELECT user.user_id, user.fname, user.lname, shifts.shift_id
FROM yourUserTable AS user
LEFT JOIN yourShiftsTable AS shifts ON(user.user_id = shifts.employee_id)
Now you get it in your initial array, as if you'd select it as one row from a table and no longer need to do tricks in PHP to combine information. If you can, always try to get the database to manage data, it does that way faster than PHP can.
Please note, the query could be a little off, I just wrote this out of the top of my head.
Just some test code I whipped up to test this from the information provided for this "Demonstration Code".
Note: I have used the mysqli class for the database (instantiating $db ) and have excluded the SQL Table setup.
What you would have had is something along the lines of this...
Case 1 - The original
$db = new mysqli('localhost', 'root', 'test', 'phptutorials_st26');
echo '<h2>Create $employees </h2>';
$query = "SELECT * FROM users";
$result = $db->query($query);
$employees = $result->fetch_all(MYSQL_ASSOC);
var_dump($employees);
echo '<h2>Create $shifts </h2>';
$query = "SELECT * FROM shifts";
$result = $db->query($query);
$shifts = $result->fetch_all(MYSQL_ASSOC);
var_dump($shifts);
echo '<h2>Using foreach on $employees and $shifts</h2>';
if ($employees) {
foreach ($employees as $employee) {
foreach ($shifts as $shift) {
if ($employee['user_id'] == $shift['employee_id']) {
echo ucwords($employee['fname'] . ' ' . $employee['lname']);
echo '<br>';
}
}
}
}
The Result from the above is
First Emp
Second Emp
Second Emp
Case 2 - Using a Join
Well using a join, as everyone has already stated, is the way to go...
$sql = "SELECT u.user_id, u.fname, u.lname, s.shift_id
FROM users AS u
JOIN shifts AS s ON(u.user_id = s.employee_id)
";
$result = $db->query($sql);
$employees = $result->fetch_all(MYSQL_ASSOC);
// To see what comes out because we always check things.
var_dump($joined_result);
(Don't ask me why I love using very abbreviated aliases for the table names! It's just "a thing".)
Then your "loop" simply becomes...
echo '<h2>Using foreach on join</h2>';
foreach ($employees as $employee) {
echo ucwords($employee['fname'] . ' ' . $employee['lname']);
echo '<br>';
}
And the result is...
First Emp
Second Emp
Second Emp
Case 2 - has reduced the code and only requires 1 Trip to the Database.
Does that help you any?
You could do it this way also. Its a little shorter.
SELECT TABLE1.FNAME, TABLE1.LNAME, TABLE2.EMPLOYEE_ID
FROM TABLE1, TABLE2
WHERE TABLE1.USER_ID = TABLE2.EMPLOYEE_ID;

Select from 2 tables not working with php mysql

I have two different tables of the following structure:
grouprel
id | userId | pupID | groupId
pupils
id | userId | fname | lname
pupId in groulrel is equal to id in pupils.
I want to fetch pupils from a different group and then order them by fname, lname.
Now I have two queries like this:
$q = "SELECT * FROM grouprel WHERE userid = ". $userid ." AND groupId = ". $_GET['id'] ."";
$r = mysqli_query($mysqli, $q);
while ($rows = mysqli_fetch_object($r)) {
$query = "SELECT id, fname, lname FROM pupils WHERE userid = ". $userid ." AND id = ". $rows->pupId ." AND status = 0 ORDER BY fname, lname";
$result = mysqli_query($mysqli, $query);
while($row = mysqli_fetch_object($result)) {
echo stuff...
}
}
This works, but it doesn't order the names alphabetically like I want to.
How could I fix this?
This is iterating over the first query:
while ($rows = mysqli_fetch_object($r)) {
And this iterates over each instance of the second query:
while($row = mysqli_fetch_object($result)) {
So if the first query returns 1,2,3, and each iteration of the second query returns A,B, then your output would be:
1 A
1 B
2 A
2 B
3 A
3 B
The second query is ordering by the ORDER BY clause you gave it. But you are ordering the entire output by the first query.
Ultimately, why do you need these separate queries at all? Executing a database query in a loop is almost always the wrong idea. It looks like all you need is one query with a simple JOIN. Guessing on your logic, something like this:
SELECT
pupils.id, pupils.fname, pupils.lname
FROM
pupils
INNER JOIN grouprel ON pupils.id = grouprel.pupId
WHERE
pupils.userid = ?
AND grouprel.groupId = ?
AND pupils.status = 0
ORDER BY
fname, lname
It may take a little tweaking to match exactly what you're looking for, but you can achieve your goal with a single query instead of multiple separate queries. Then the results of that query will be ordered the way you told MySQL to order them, instead of the way you told PHP to order them.

SQL returns 2 of the 3 rows

I'm trying to select something from a table and insert that information into another table
For example I've 3 rows in table A which I want to insert into table B, he only inserts 2 of the 3.
I got this: I've tried it with fetch_array() but I get only the error non-object
EDIT: THE PART OF THE SCRIPT
$log = $db->query("SELECT itemname FROM log_mitem WHERE mobname = '".$mobname."' AND game = '".$game."'") or die($db->error);
if($log1 = $log->fetch_object());
{
while($loco = $log->fetch_object())
{
You shouldn't have that first if, just do:
$log = $db->query("SELECT itemname FROM log_mitem WHERE mobname = '".$mobname."' AND game = '".$game."'") or die($db->error);
while($loco = $log->fetch_object()) {
// do something
}
Also note that you don't need a while loop for this trivial task, you can use INSERT INTO ... SELECT syntax
INSERT INTO table1 ( column1 )
SELECT col1
FROM table2
WHERE cond1

SQL: can I JOIN 2 tables according the first table "array" value?

Im trying to find a better way to return 2 tables at once.
My first table is:
[ID] [area]
1 13,12,15
6 18,17,13
and the second table is:
[areaname] [singlearea]
textOf12 12
textOf18 18
textOf15 15
Now, I need to return for each [ID] hits area names, for example:
For the ID: 1, I need the following array: (textOf12,textOf15)
and for the ID 6 I need: (textOf18) only.
This is what i have for now (I don't think its a nice code):
$getall = "SELECT * FROM table1";
$resultfull = mysql_query($getall);
while ($res = mysql_fetch_assoc($resultfull))
{
$uarray = array();
$sqlarea = explode(",", $res['area']);
foreach($sqlarea as $userarea)
{
$areaarray = runquery("SELECT areaname From table2 WHERE singlearea = '".$userarea."'");
$value = mysql_fetch_object($areaarray);
array_push($uarray,$value->areaname);
}
var_dump($uarray);
any suggestions?
Thank you very much!
Comma separated ID list and ID value pretty good matching using like:
select t1.id, t2.areaname
from table1 t1, table2 t2
where concat(',', t1.area, ',') like concat('%,', t2.singlearea, ',%')
However It's recommended to use additional link table!

search for element in a php array

I've got
a users table named "members"
a rooms table named "rooms"
a table that associates the user id to the ids of the rooms "membersRooms"
I should write a loop that prints a dropdown for each user with all the rooms, but that adds the attribute "selected" to rooms associated with the user
What's wrong with this loop?
$members = mysql_query("SELECT * FROM members ");
$rooms = mysql_query("SELECT * FROM rooms");
while($member = mysql_fetch_array($members)){
echo("<select>");
$roomsOfUser = mysql_query("SELECT roomID FROM membersRooms WHERE userID=".$member["id"]);
$cuArray = mysql_fetch_array($roomsOfUser);
while($room = mysql_fetch_array($rooms)){
if(in_array($room["id"],$cuArray,true))
echo("<option selected='selected'>".$room["roomName"]."</option>");
else
echo("<option>".$class["roomName"]."</option>");
}
echo("</select>");
}
To make this a little easier on you, you could try utilizing left and right joins on your database. This would significantly reduce your server load and still allow you to do the same functionality.
I believe, if I'm reading your database structure right, that you'ld want something along the lines of:
SELECT members.id as memberID, rooms.id as roomID, rooms.roomName, membersRooms.roomID as memberRoom
FROM members
LEFT JOIN membersRooms
ON members.id = membersRooms.userID
RIGHT JOIN rooms
ON membersRooms.roomID = rooms.id
Then in PHP you should be able to just keep track of when your memberID changes, and when it does, start a new select. If I didn't totally bungle that SQL (which I might have) then the resulting rows should look something like:
memberID | roomID | roomName | memberRoom
1 1 foo 1
1 2 bar 1
2 1 foo 1
2 2 bar 1
So on your loop iteration you would use roomID and roomName to build your select, and if RoomID matched memberRoom then you would select that row.
$rooms query while is dead
while runs once time in while
put this $rooms = mysql_query("SELECT * FROM rooms"); query line
in first while
OK, so you need information from 3 tables - members, rooms, and membersRooms. The rows from members and membersRooms line up 1:1, so we can get both of those with 1 query.
This method will minimize the number of queries needed - if you ever see yourself querying the database in a loop, ask yourself if there's a better way.
$member_query = mysql_query("SELECT * FROM members LEFT JOIN membersRooms ON (members.id = membersRooms.userID)");
$room_query = mysql_query("SELECT * FROM rooms");
$rooms = array();
while ($room = mysql_fetch_assoc($room_query))
$rooms[] = $room;
while ($member = mysql_fetch_assoc($member_query)) {
echo '<select>';
foreach($rooms as $room) {
echo "<option value='{$room['roomID']}' ";
if ($member['roomID'] == $room['id'])
echo 'selected="selected"';
echo ">{$room['roomName']}</option>";
}
echo '</select>';
}
It's worth noting that if members:rooms is a 1:many relation, you don't need to use a third table to join them - just add a roomId to members, and you're fine.

Categories