i have a problem with an association between 2 tables:
i have
table 1 "flowers": "id", "flower";
table 2 "colors": "id", "color";
table 3 "association_flowers_colors": "id", "id_flower", "id_color";
now, a single flower can have many colors, now the problem is the father all the informations inside a single array.
es: if i get all the informations inside the first table and fetch them i'm gonna have
array([0](array[id]=>0, [flower]=>rose), [1]array([id]=>1, [flower]=>tulip));
then if i gather all the informations from the second table i'm gonna have
array([0]array([id]=>0, [color]=>red)[1]), [1]array([id]=>1, [color]=>white));
Now to the problem: i'd need to get an array along the lines of
array([0]array([id]=>0, [flower]=>rose, [color]=>red, [color]=>white), [1]array([id]=>1, [flower]=>tulip, [color]=>red, [color]=>white));
is there a simple way to do it with a single elaborated query or should i go with while loop or something like that and then merge the informations i get?
Thanks
edit:
function get_flowers(){
include('../actions/db_connection.php');
$result = mysqli_query($db_connection, "select *
from flowers f
left outer join association_flowers_colors a on f.id = a.id_flower
left outer join colors c on c.id = a.id_color");
mysqli_close($db_connection);
$flowers = array();
while ($row = mysqli_fetch_array($result, MYSQL_ASSOC)) {
$flowers[] = $row;
};
return $flowers;
}
I ended up with this after reading juergen's reply, it looks like it works the problem is that it gives me an array for each color(for a total of 4 arrays) instead of giving me only 2 arrays with both colors inside(for a total of 2 arrays).
edit2:
Ok, i didnt find a way to retrieve all the informations in 2 arrays(instead of 4) but i managed to print them in a readable way anyway:
while($row = mysqli_fetch_array($flowers)){
if ($flowerID != $row['id_flower']) {
echo "<div class='flowerContainer'>";
echo "<input type='hidden' name='flowerID' value='".$row['id_flower']."'>";
echo "Name: ".$row['flower']."<br />";
}
echo $row['color']."<br />";
if ($flowerID == $row['id_flower']) {
echo "</div>";
echo "<hr>";
}
$flowerID = $row['id_flower'];
}
in this way even if the loop repeats all the information for every array, it will only output the information if there's a new entry and it won't repeat anything.
select *
from flowers f
left outer join association_flowers_colors a on f.id = a.id_flower
left outer join colors c on c.id = a.id_color
Related
I am sure that somewhere in the web this question has been already asked but i have not found it, because i don't know for what exactly i should search. So i will try to describe the problem:
I have a Blog written in PHP and a SQL-database, where i receive the needed data.
Each Blog entry contains 1 image and 1 text-field and has its own id.
To get this data i execute this query (pseudo-code) -> this is working fine:
foreach($db->query("SELECT news.id, news.position, news_text.date, news_text.text, news_images.image
FROM news
LEFT OUTER JOIN news_text ON news.id=news_text.id
LEFT OUTER JOIN news_images ON news_images.id = news.id
ORDER BY position") as $row){
echo "{$row['id']}";
echo "{$row['position']}";
echo "{$row['date']}";
echo "{$row['text']}";
echo "{$row['image']}";
}
Now the problem starts. I want to add a comment field, where users can add a message. So for each blog-entry there can be several comments (would probably be a 1:n connection).
- I tried to add a foreach loop in a foreach loop (stupid)
- I tried with several SQL-query but only get rubbish as a result
- I don't see the logic how to connect the database news_comment with the others
Here is my simplified database:
Can someone give me a hint how i could solve this problem. Result should be:
foreach($db->query("SELECT news.id, news.position, news_text.date, news_text.text, news_images.image
FROM news
LEFT OUTER JOIN news_text ON news.id=news_text.id
LEFT OUTER JOIN news_images ON news_images.id = news.id
ORDER BY position") as $row){
echo "{$row['id']}";
echo "{$row['position']}";
echo "{$row['date']}";
echo "{$row['text']}";
echo "{$row['image']}";
Here i get for each blog-entry the corresponding comments..
}
Thank you.
Misch
You should retrieve comments in another one query (not in loop) using the same conditions as for retrieving news. So that after executing both queries you will have to arrays: news and comments.
Then you just need to take appropriate comments for each news item.
To do that you might need to rebuild comments array so that comment news_id will be an index of array where comments are stored.
Finally it'd look like this:
$news = [
[
'id' => ...,
'position' => ...
<so on>
]
];
$comments = [
$news[0]['id'] = [<comment array>],
<so on>
];
That's the way most framework use to have data bound in result set.
Solved it with the help of Alex(thx) and MulipleIterator. This is how it worked for me (maybe someone has a better solution).
foreach($db->query("SELECT news.id, news.position, news_text.date, news_text.text, news_images.image
FROM news
LEFT OUTER JOIN news_text ON news.id=news_text.id
LEFT OUTER JOIN news_images ON news_images.id = news.id
ORDER BY position") as $row)
{
$news_id[] = $row['id'];
$news_text[] = $row['news_text'];
$news_images[] = $row['news_images'];
$values = new MultipleIterator();
$values->attachIterator(new ArrayIterator($news_id));
$values->attachIterator(new ArrayIterator($news_text));
$values->attachIterator(new ArrayIterator($news_images));
}
foreach($values as $value)
{
list($news_text, $news_images, $news_id) = $value;
{
echo '$news_text';
echo '$news_images';
foreach($db->query("SELECT comment FROM news_comment WHERE id = $news_id ") as $row)
{
echo "{$row['comment']}";
}
}
}
I have three tables like below:
[Category]->[SubCategory]->[Leaf]
-> means one-to-many relation;
Is it possible to make nested list with just one query?
Actual Query:
Select * from Category
join SubCategory
join Leaf
Intended Output:
Category
--Subcategory
----Leaf
----Leaf
--Subcategory
----Leaf
----Leaf
I know about composite design pattern; but lets say I have the result set from query and I want to manipulate the list above with minimal attempt.
Update
My question is without for loop and sub query, can it be done with the one query above, or what is the best way to generate 3 nested list like above from three tables?
Update:
What is the most optimized way to create a nested list like above from three tables in any database and in any language?
Use this query:
SELECT *
FROM Category c
JOIN SubCategory s ON s.CategoryID = c.ID
JOIN Leaf l ON l.SubCategoryID = s.ID
ORDER BY c.ID, s.ID, l.ID
Then in your PHP loop, print the Category name whenever it changes, print the SubCategory name whenever it changes, and print every Leaf name.
$last_cat = null;
while ($row = fetch()) {
if ($row['CategoryName'] !== $last_cat) {
echo $row['CategoryName'] . "\n";
$last_cat = $row['CategoryName'];
$last_subcat = null;
}
if ($row['SubCategoryName'] != $last_subcat) {
echo "--{$row['SubCategoryName']}\n";
$last_subcat = $row['SubCategoryName'];
}
echo "----{$row['LeafName']\n";
}
This is a hypothetical question. If I have 3 arrays from 3 separate sql db queries that all relate to another. For example...
//db
schools
id | school_name
classes
id | class_name | school_id
students
id | student_name | class_id
And I want to display everything in a huge list like this...
//php
foreach(schools as school){
echo '<h1>' . $school->school_name . '<h1>';
foreach(classes as class){
if($class->school_id == $school->id){
echo '<h2>' . $class->class_name . '<h2>';
foreach(students as student){
if($student->class_id == $class->id){
echo '<h3>' . $student->student_name . '<h3>';
}
}
}
}
}
I have to make 3 database calls. Is there a way to grab all this information in a single db query? Like maybe an array in an array in an array and then somehow loop through? Or is this the best way to do this?
You can do a join which will allow you to have 1 for each. Are you wanting everything or any sort of filter ?
You can join those table, to get one big array with flattened data. When looping through this data, you can check if the id of the previous record still matches the id of the current record. If not, you can output a new header. It is important, though, that the resultset is properly sorted for this.
SELECT
s.id AS school_id,
s.school_name,
c.id AS class_id,
c.class_name,
st.id AS student_id,
st.student_name
FROM
schools s
INNER JOIN classes c ON c.school_id = s.id
INNER JOIN students st ON st.class_id = c.id
ORDER BY
s.id,
c.id,
st.id
If you have it all in a flattened structure, you can even make it into a nested array structure again something like this:
foreach ($resultset as $row)
{
$schools[$row->school_id]->school_name =
$row->school_name;
$schools[$row->school_id]->classes[$row->class_id]->class_name =
$row->class_name;
$schools[$row->school_id]->classes[$row->class_id]->students[$row->student_id]->student_name =
$row->student_name;
}
var_dump($schools);
After that, you can still use the nested for loops to process the array, but it will be in a more efficient way, since the data is already sorted out: classes are already added to the school they belong to, and student are already added to the right class.
<?php
try {
$pdo = new PDO("mysql:host=127.0.0.1;dbname=school", "username");
} catch (PDOException $e) {
echo "PDO Connection failed: " . $e->getMessage();
exit(1);
}
$sql = <<<SQL
SELECT schools.school_name, classes.class_name, students.student_name
FROM
schools INNER JOIN classes ON (schools.id = classes.school_id)
INNER JOIN students ON (classes.id = students.class_id)
ORDER BY 1, 2;
SQL;
$result = $pdo->query($sql);
if ($result == false) {
die("query failed?!");
}
$school = "";
$class = "";
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
if ($school != $row['school_name']) {
$school = $row['school_name'];
echo "\nSchool: $school\n\n";
}
if ($class != $row['class_name']) {
$class = $row['class_name'];
echo " Class: $class\n\n";
echo " Student list:\n";
}
echo " {$row['student_name']}\n";
}
$res = mysql_query('SELECT school_name, class_name, student_name, sc.id AS scid, c.id AS cid, st.id AS stid FROM schools sc LEFT JOIN classes c ON (sc.id = c.school_id) LEFT JOIN students st ON (c.id = st.class_id) ');
$arr = array();
while ($v = mysql_fetch_assoc($res)) {
$arr[$v['school_name']][$v['class_name']][$v['stid']] = $v['student_name'];
}
print_r($arr);
You could do it all in one SQL query that might look something like:
SELECT schools.schoolname, classes.class_name, students.student_name
FROM
schools INNER JOIN classes ON (schools.id = classes.school_id)
INNER JOIN students ON (classes.id = students.class_id)
ORDER BY 1, 2;
Then you could walk the result set in one loop, but you'd probably want to add some logic to only display the school name and class name once for each time it changes.
I have this sql statement joining 3 tables:
SELECT * FROM `int_news`
LEFT JOIN tl_member ON int_news.member_id = tl_member.id
LEFT JOIN tl_news ON int_news.news_id = tl_news.id
The 3 Tables are like this:
Table 1 (int_news)
ID, member_id, news_id
Table 2 (tl_member)
id, firstname, lastname
Table 3 (tl_news)
id, headline
So far so good, but it seems i have a big blackhole in my head making me unable to solve how to output the result like this
For each "headline" i want ALL lastnames e.g.
headline 1 Jonny
Walker
Jim
headline 2 Knopf
Jon
Doe
It sounds your looking something like a pivot so if you group your query by headline, it will display each lastname as a column.
I found this good tutorial on pivots for mysql that might help you http://www.artfulsoftware.com/infotree/queries.php#78
headline 1 Jonny Walker Jim
headline 2 Knopf Jon Doe
Here is a loop that would do that *Forgive my php it's been a while.
$curHeadline = "";
while ( $db_field = mysql_fetch_assoc($result) ) {
if($curHeadline != $db_field['headline'])
{
$curHeadline = $db_field['headline'];
print $curHeadline . $db_field['ID']
}
print $db_field['lastName'] . "<BR>";
}
Try this for mysql :
SELECT tlnews.headline, GROUP_CONCAT(tl_member.last_name)
FROM `int_news` LEFT JOIN tl_member ON int_news.member_id = tl_member.id
LEFT JOIN tl_news ON int_news.news_id = tl_news.id
GROUP BY 1;
Expected Output:
headline1 Johnny,Walker,Jim
headline2 Knopf,Jon,Doe
...
Would something like this vaguely reflect your situation?
<?php
$sql = "
SELECT
`int_news`.`ID` AS `int_news_ID`,
`int_news`.`member_id`,
`int_news`.`news_id`,
`t1_member`.`id` AS `t1_member_id`,
`t1_member`.`firstname`,
`t1_member`.`lastname`, /* desired */
`t1_news`.`id` AS `t1_news_id`,
`t1_news`.`headline` /* desired */
FROM
`int_news`
LEFT JOIN `tl_member` ON `int_news`.`member_id` = `tl_member`.`id`
LEFT JOIN `tl_news` ON `int_news`.`news_id` = `tl_news`.`id`
";
$res = mysql_query($query);
$arrHeadings = array();
while($row = mysql_fetch_assoc($res)) {
// We want to output the results in groups of headings
$arrHeadings[$row['heading']][] = $row;
}
// Don't forget to cleanse for html output (unlike below)
// Loop through the headers
foreach($arrHeadings as $heading=>$arrRow) {
echo '<dl>';
echo '<dt>'.$heading.'</dt>';
echo '<dd>';
// Loop through rows with the same header
foreach($arrRow as $index=>$dbRow) {
echo $dbRow['lastname'].'<br />';
}
echo '</dd>';
echo '</dl>';
}
?>
I'm coding in PHP/MySQL and have the following query to fetch products and product group data:
SELECT products.id,products.name,product_groups.id,product_groups.name
FROM products
INNER JOIN product_groups
ON products.id=product_groups.id
WHERE products.name LIKE '%foobar%'
ORDER by product_groups.id ASC
So this query fetches products and orders them by product group. What I would like to have is to display product_groups.name just once for each product grouping. So even if I have ten shoe products, the group name "Shoes" is only displayed once.
I'm using the following PHP to print out the results:
while ($data = mysql_fetch_array($result))
If you want it done in the MySQL query, it is honestly more trouble than it's worth. For one, the syntax is really wonky (as I recall) to have a group name listed at the top of each grouping. And the results are still treated as rows, so the group name will be treated like a row with all the other columns as Null, so you won't really save any time or effort in the PHP script as it has to do an if statement to catch when it hits a group name instead of the group data.
If you want it done by the PHP while loop, Johan is on the right track. I use the following for a similar situation:
$result = $sql->query($query);
$prev_group = "";
while($data = $result->fetch_assoc()){
$curr_group = $data['group'];
if ($curr_group !== $prev_group) {
echo "<h1>$curr_group</h1>";
$prev_group = $curr_group;
}
else {
echo $data;
.....
}
Obviously the echo data would be set up to echo the parts of the data the way you want. But the $prev_group/$curr_group is set up so that the only time they won't match is when you are on a new group and thus want to print a header of some sort.
while($data = mysql_fetch_assoc($result)){
if($data['product_groups.name'] != $groupname){
echo "Groupname: ".$data['product_groups.name']."<br />";
$groupname = $data['product_groups.name'];
}
echo "ID: ".$data['products.id']."<br />";
echo "Name: ".$data['products.name']."<br />";
}
Maybe you can do like this!?