Join 1 Row to Multiple Rows in PDO - php

Here is my scenario:
Database Name: Children
+-------------+---------+---------+
| child_id | name | user_id |
+-------------+---------+---------+
1 Beyonce 33
2 Cher 33
3 Madonna 33
4 Eminem 33
Database Name: Parents
+-------------+---------+---------+
| parent_id | child_id | parent_name |
+-------------+---------+---------+
1 1 Obama
2 1 Michelle
3 4 50cents
4 4 Gaga
Desired Output:
+-------------+---------+---------+
| child_id | name | parent Name |
+-------------+---------+---------+
1 Beyonce Obama (Row 1) Michelle (Row 2)
PHP SQL Query in PDO:
$sql = "SELECT Children.child_id, Children.name, Parents.parent_name
FROM Children
LEFT JOIN Parents
ON Children.child_id = Parents.child_id
WHERE Children.user_id = ?
";
$stmt = $db_PDO->prepare($sql);
if($stmt->execute(array($userId))) // $userId defined earlier
{
// Loop through the returned results
$i = 0;
foreach ($stmt as $row) {
$fetchArray[$i] = array (
'childId' => $row['child_id'],
'childName' => $row['name'],
'parentName' => $row['parent_name'],
// How do I save the multiple parents from other rows here ????
);
$i++;
}
}
How can I run a query that Joins 1 row to multiple rows in second table in PDO? I have read other topics here but I am unsure. Is it easier to add a second query that gets the linked parents for each child_id separately in a loop? I am worried that will be too much query. Can someone help me solve this?

Well, took me some fiddling to test it all out but here you go.
Unfortunately one cannot easely pivot tables in mysql but there are alternatives.
http://sqlfiddle.com/#!9/1228f/26
SELECT GROUP_CONCAT(
CONCAT_WS(':', Parents.parent_id,Parents.parent_name) ) FROM Parents where Parents.child_id=1
;
SELECT
Children.child_id,
Children.name,
GROUP_CONCAT(
CONCAT_WS(':', Parents.parent_id,Parents.parent_name) ) as parents
FROM
Children
LEFT JOIN Parents
ON Children.child_id = Parents.child_id
WHERE Children.user_id = 33
Group by Children.child_id
This query uses the group concat to concatenate all resulsts we want into a colon seperated string with the values we want, and comma's between the individual fields.
We could do some tricky magic to make them individual fields but that would break our php because we wouldnt know how much fields each query would return(adopted, orphan, no known parents, etc...)
In php you could feed them into an object
$parents = array();
$loop1 = explode(',',$row['parents']);
foreach($loop1 as $parentset) {
$parentdetail = explode(":",$parentset);// decide yourself how much detail you want in here... I jsut went with name and id.
$parent = new stdClass();
$parent->id = $parentdetail[0];
$parent->name = $parentdetail[1];
array_push($parents,$parent);
}
var_dump($parents);

Execute the below query . You will get the output as required, i just used the group by which will group the records as per the selected column
select a.child_id, name ,group_concat(parent_name) from children a, parents b where a.child_id =b.child_id group by a.child_id

HI this query works only if you are passing child id ,
select a.child_id, name ,group_concat(parent_name ) parent_name from children a, parents b where a.child_id =b.child_id and a.child_id=1
here i am using a function called group_concat which is used for concatinating the rows.It automatically takes the rows whose count is greater than 1.So no need of the extra code again

Related

MySQL count child elements

I have a categories table:
id | name | parent_id
1 | Camaro | 0
2 | Chevelle | 0
3 | Sale - Camaro Parts | 1
4 | Bestselling Parts | 1
My first request looks like:
'SELECT
*
FROM
`categories`
WHERE
parent_id = :parent_id';
And after I'm fetching result set I make sub query to check if row has child elements:
foreach($result as $r) {
$r->hasChild = count(ORM::forTable('categories')->where('parent_id', $r->id)->findArray());
$data[] = $r;
}
Is any way to avoid multiple connection to DB in foreach loop and get data in first query?
Thanks!
This isn't awful to do, so long as you only want the count of children below the selected rows. If you want the entire hierarchy, you'll need to use a better RDMS.
The main part of the solution here is self joining the same table. Then we can use the count() aggregate function to see how many children are attached to each item.
select
categories.id
, categories.name
, categories.parent_id
, count(chld.id)
from
categories
left join categories chld
on categories.id = chld.parent_id
where
parent_id = :parent_id
group by
categories.id
, categories.name
, categories.parent_id
You can do a self join to the table on parent_id and id. Based on whether you want categories with child or not you can do a left join or inner join. Kind of a similar question is mentioned here -
Mysql Self Join to find a parent child relationship in the same table

SELECT results from another table AS column array

I can't figure out how to get results from 2 tables, in 1 query result (can't simple JOIN)
I have these 2 tables in my MySQL database:
Table 1: sales
id
name
info
Table 2: users
sale_id
user_id
Now, every sale have different number of assigned users. Some sale have 2 users, some sale have 10 users.
In single row, I need to have columns from sale table, and all assigned users to it (connected with same Sale_id)
I need result, something like this:
enter image description here
Try this :
SELECT s.*,
(SELECT GROUP_CONCAT(u.user_id SEPARATOR ', ')
FROM users u
WHERE u.sale_id = s.id) AS users
FROM sales s
Some insight on your programming language would have been nice.
And yes, as suggested by wogsland and icoder, one typically use joins and loop through results to build en array. But the use of GROUP_CONCAT, as Yoleth pointed out, is what you need. I don’t know if it was the goal here, but it can reduce memory used in the result because there is no row repetition.
SELECT info FROM Sales AS s,
(
SELECT sale_id, GROUP_CONCAT(user_id) AS assigned_users
FROM Users
GROUP BY sale_id) AS u
WHERE s.id=u.sale_id;
In a single query, with a fancy JOIN:
SELECT s.info AS info, u.sale_id AS sale_id, GROUP_CONCAT(u.user_id) AS assigned_users
FROM Sales AS s LEFT JOIN Users AS u
ON s.id=u.sale_id
WHERE sale_id IS NOT NULL GROUP BY u.sale_id;
You can simply join two tables and get query result set like this:
saleID | saleName | userID | userName
1 | Oct Sale | 5 | Tim
1 | Oct Sale | 6 | Nik
2 | Nov Sale | 7 | Bill
Then you can walk each row and build associative array from that data:
$sales = array();
while( $row = mysqli_fetch_assoc($result)) {
if (!array_key_exists($row['saleID'], $sales)) {
$sales[$row['saleID']] = array(
'saleID' => $row['saleID'],
'saleName' => $row['saleName'],
'users' => array()
);
}
array_push($sales[$row['saleID']]['users'], array(
'userID' => $row['userID'],
'userName' => $row['userName']
));
}
Well, MySQL isn't going to return you a nice nested array like that. But you can create it by looping through the result. Assuming your MySQL connection is named $mysqli then try something like
$sales = array();
$result = $mysqli->query("SELECT sales.*, users.user_id FROM sales, users WHERE sales.id = users.sales_id");
while ($row = $result->fetch_assoc()) {
$sales[$row->id]['sales_id'] = $row->id;
$sales[$row->id]['name'] = $row->name;
$sales[$row->id]['info'] = $row->info;
$sales[$row->id]['assigned_users'][] = $row->user_id;
}

Retrieve total amounts of rows containing strings

I have a table in MySQL, with 5(sort of) possible values in the column 'type'... I say sort of because the data type is 'set' and 1 type has a subcategory... It's for a type of property, so the possible types are retail, office, hospitality, industrial, residential(multi family), residential(single family).
I'm attempting to paginate the results and I need to know how many pages each should have. So I need a query that tells me how many of each type are in the table, the user can select residential as a category, or single, multi as subcategories.
I can't figure out how to do a query that tells me how many of each there are, or how to retrieve those numbers as variables I can use to divide be items per page.
id | type
-----------------------
1 | office
2 | residential,single
3 | industrial
4 | residential,multi
5 | retail
6 | office
7 | hospitality
8 | residential,single
etc....
so if this was the data, I would need to get:
$office = 2
$residential = 3
$industrial = 1
$single = 2
etc...
Use array_count_values() function
Check the link http://php.net/manual/en/function.array-count-values.php
From their website http://php.net/manual/en/function.array-count-values.php;
and Try this code
<?php
$query= // Run your select query.
$result= mysqli_query($link, $query);
//Run the while loop
while($row= mysqli_fetch_array($result))
{
$array[]=$row['Column_Name'];//Store the result in array
}
$count = array_count_values($array);//Use array count
print_r($count);//See the result
Or if you see The out put the way you want
Run Foreach loop on the $count array
foreach($count as $key => $value) {
//Get the out put From new array
echo $value .' '. $key.'<br/>' ;
}
A count and group by should do the trick;
SELECT id, type, COUNT(*) as count
FROM mytable
GROUP By id

Getting rows from a database then modify them with additional rows from database

Here's the table structure
id | name | parent_id
1 fruit 0
2 apple 1
3 banana 1
4 orange 1
5 vegetable 0
6 carrot 5
7 tomato 5
I need get every row where a parent_id is 0, but I also need to to set an array on each row which is equal to 0 with the name of all it's children.
So I would have something like:
id = 1, name = fruit, children = [apple, banana, orange]
I'm aware there are better table structures but I must use the table structure stated.
I've tried getting all rows from the db then looping through them, if parent_id = 0 then push that to an array, otherwise it's a child so find parent in array and add it to that.
But there must be a better way?
I thinks you should use:
SELECT pr.id, pr.name, (
SELECT GROUP_CONCAT(name) FROM test as ch WHERE ch.parent_id = pr.id GROUP BY ch.parent_id
) AS children
FROM test as pr
WHERE parent_id = 0
Without thinking about performance or beauty:
SELECT t.id, name, (
SELECT GROUP_CONCAT(name) FROM table WHERE parent_id = t.id GROUP BY parent_id
) AS children
FROM table t
WHERE parent_id = 0
Avoiding a sub query, just using a self join the following should do it:-
SELECT t1.id, t1.name, GROUP_CONCAT(CONCAT('name:', t2.name))
FROM test t1
LEFT OUTER JOIN test t2
ON t1.id = t2.parent_id
WHERE t1.parent_id = 0
GROUP BY t1.id, t1.name
SQL fiddle for it:-
http://www.sqlfiddle.com/#!2/d4479a/1
I had come across a similar issue, where I had to fetch all subordinates of a user in a recursive manner.
We wrote a function where we loop through each of the child records recursively until we reach the final subordinate. We keep on concatenating the subbordinates we find in each loop.
Hope through this you can think of a solution and resolve your problem.

Mysql query with multiple ID columns and php

I have 2 joined tables, each one has a primary key column named id.
SELECT t1.*, t2.* from t1 join t2 on t1.fk_id=t2.id
When I run the query above, both id fields are selected (t1.id and t2.id). My question is, how can I select the correct ID while I am looping through the result set? If I select $result->id, I will get the t2.id. Is there any way that I can get the t1.id also without explicitly selecting it in the query (i.e. t1.id as t1_id?) Also, please, let us know about some of your practices when it comes to naming the primary key columns.
Thanks!
SELECT t1.id as id1, t2.id as id2, t1.*, t2.* from t1 join t2 on t1.fk_id=t2.id
You are probably using mysqli_result::fetch_assoc to return each row of your result set as an associative array. MySQL will let you have two columns with the same name in a query, but these do not map to an associative array the way you want them to—even though the associative array is doing exactly as it should.
Assume two tables, book and author, linked by the junction table book_author. In MySQL, you can run the following query, which returns two id columns:
SELECT b.*, a.*
FROM book AS b
JOIN book_author AS ba ON ba.book_id = b.id
JOIN author AS a ON a.id = ba.author_id
LIMIT 2;
+----+-----------------+----+--------------+
| id | title | id | name |
+----+-----------------+----+--------------+
| 1 | Design Patterns | 1 | Erich Gamma |
| 1 | Design Patterns | 2 | Richard Helm |
+----+-----------------+----+--------------+
If you try to map one of these rows to an associative array, you end up with a single id element in your array:
$row = $result->fetch_assoc();
print_r($row);
Array
(
[id] => 1
[title] => Design Patterns
[name] => Erich Gamma
)
The last id column in the row will overwrite any that precede it. Here’s the second row from the result set:
Array
(
[id] => 2
[title] => Design Patterns
[name] => Richard Helm
)
This is just the same as modifying the value of an element in an associative array;
$row = array();
$row['id'] = 1;
print_r($row);
Array
(
[id] => 1
)
$row['id'] = 2;
print_r($row);
Array
(
[id] => 2
)
If you give each column a unique name in your query, either by doing so in the table itself, or giving it an alias in the query, the problem is avoided:
SELECT b.id AS book_id, b.title,
a.id AS author_id, a.name
FROM book AS b
JOIN book_author AS ba ON ba.book_id = b.id
JOIN author AS a ON a.id = ba.author_id
LIMIT 2;
+---------+-----------------+-----------+--------------+
| book_id | title | author_id | name |
+---------+-----------------+-----------+--------------+
| 1 | Design Patterns | 1 | Erich Gamma |
| 1 | Design Patterns | 2 | Richard Helm |
+---------+-----------------+-----------+--------------+
$row = $result->fetch_assoc();
print_r($row);
Array
(
[book_id] => 1
[title] => Design Patterns
[author_id] => 1
[name] => Erich Gamma
)
Alternatively, you could (and almost certainly should) use prepared statements instead. Although this can get round the problem of duplicate column names, using unique column names in your queries still makes things much easier to read and debug:
$sql = 'SELECT b.*, a.* ' .
'FROM book AS b ' .
'JOIN book_author AS ba ' .
'ON ba.book_id = b.id ' .
'JOIN author AS a ' .
'ON a.id = ba.author_id';
$stmt = $mysqli->prepare($sql);
$stmt->execute();
$stmt->bind_result($book_id, $book_title, $author_id, $author_name);
while ($stmt->fetch()) {
printf("%s, %s, %s, %s\n",
$book_id,
$book_title,
$author_id,
$author_name);
}
You'll often see the primary key for table XXX named xxx_id. This keeps the name of the same "information identifier" the same everywhere: for example in another table YYY, you'll have YYY.xxx_id with a foreign key constraint to XXX.xxx_id. This makes joins easier (you don't have to specify the "on" constraint at all in many databases) and it solves the problem you're running into as well.
I'm not saying you should prefix every column name to create a faux-namespace, but in the case of "id" it is actually useful and descriptive. It is, after all, not just any kind of ID, it's a user ID, site ID, game ID, contact ID, what have you.

Categories