Please help with a query

I have a database with 2 tables, in a one-to-many relationship. Table1 has id and name fields. Table2 has id, table1_id, trait_name, description.

I need to get every record from Table1, and all of the associated records from Table2. There might be zero, 1, or more records in Table2 associated with a record from Table1.

My SELECT statement currently looks like this:

SELECT Table1., Table2. FROM Table1 LEFT JOIN Table2 ON Table1.id = Table2.table1_id ORDER BY Table1.name ASC

The problem is, it is only returning the first matching row from Table2. I have tried several different variations using GROUP_CONCAT, GROUP BY, etc., but still haven’t been able to get the result I need.

In addition, what is the correct way to reference the results of this query using PHP? I am currently using fetchAll, but once I have all of the associated records from Table2 being included, how do I reference each one separately?

Any help provided will be greatly appreciated!

For the best answer, post your code and an sql dump of your db with sample data.

Hi Kevin,

Here is a sample of my code:

[php]<?php
require_once(‘config/config.php’);

$db = new PDO('mysql:host='. DB_HOST .';dbname='. DB_NAME . ';charset=utf8', DB_USER, DB_PASS);

if ($db) {
	$query_get_monsters = $db->prepare('SELECT monsters.*, monster_traits.* FROM monsters LEFT JOIN monster_traits ON monsters.id = monster_traits.monster_id GROUP BY monsters.id ORDER BY monsters.name ASC LIMIT 3');
	$query_get_monsters->execute();
	$monsters = $query_get_monsters->fetchAll();
	print_r($monsters);
}

?>[/php]

And I have attached a partial data dump of my tables.

Once I figure out the SQL statement, I will be using the data to display monster stats (this is for a D&D project). I need to be able to display all of the data from the monsters table, and then for each trait that is associated with the monster, display the trait name, and the description.

Please let me know if I can provide you with anything else, and I really appreciate the help!


db_partial_export.txt (4.65 KB)

Outside of this result I am not sure what you want. Show me an example of the expected output.

[php]SELECT
m., t.
FROM
monsters AS m
LEFT JOIN monster_traits AS t ON t.monster_id = m.id
ORDER BY
m.name ASC[/php]


Ok, so it appears that I am now getting all of the traits for each monster. How would I go about iterating through all of the data? I am trying to first display a table with each monster listed (name, type, cr). When a button is clicked beside a specific monster, it opens up a modal (I’m using bootstrap to format the pages), and displays the full “stat block” for the selected monster. It will display all of the details from the monster table, along with all of the trait names and descriptions from the monster_traits table.

What is the best way to fetch the results of the query, and how do I reference each of the traits for each monster?

I had all of this working, with the data in an xml file. However, I was running into limitations on some things I want to do with the xml file, so I decided to port it over to a MySQL database. If you would like to see what I’m trying to replicate, you can view it at dndbits.com/monsters.php. Please don’t judge me on my page design skills (or lack thereof!). I’m obviously not a professional developer. I’m just trying to build this for my D&D group, and I love the challenge of learning to code. If you are interested in seeing my code for the existing page, I would be happy to send it.

Thanks!

Yes, I would like to see the code. I also want to know if your using PDO or at the least Mysqli. If your not, get that handled FIRST, then we can move on. I highly recommend you go with PDO as any experienced coder will tell you also.

Your most of the way there already with the page you did.

Yes, I’m using PDO.

I’ll post my code for the original page up in a bit (had to run meet my wife for lunch :slight_smile: ).

Thanks again for your help!

Hey Kevin,

I have attached the entire php code of my original page, as well as a portion of the xml file that it reads from (both renamed, so they can be attached).

As I said, for this part of my project, I’m really just trying to duplicate what I already have, but using the MySQL database instead of the xml file. I’m sure there are better ways to do some of the things I have done, so feel free to point out any obvious noob issues! ;D

Thanks!


monsters_php.txt (13 KB)

monsters_xml.txt (246 KB)

I’ve been looking at this some more, and I understand how I would iterate through each row of the result. The part I’m struggling with is how I grab each trait for a monster. For example, monster_id 1 has 3 traits. I get a separate row, with all of the main stats repeated, for each trait.

So if I want to display all of the monster stats, followed by each trait name and description, how would I grab all of the rows for that monster? In the XML file it was easy, because all of the traits were underneath the monster element. Now, I need to be able to tell it “for every row that contains the same monster_id, read it and grab the trait_name and trait_description, and add that to the monster stats.”

I just can’t work out in my brain how to go about doing that. Any thoughts?

BTW, I also downloaded your pdo_bumpstart code, and am already learning some things from there. Even though I was already using PDO, I still have a lot to learn about it. Thanks!

You could do something like this to iterate over the rows to build a multi dimensional array.

[php]// select spesific column instead of all (*), choose a name for the joined id column so we get both the monster id and the trait id instead of just one
$query = ‘SELECT monsters.id, monsters.name, monster_traits.id as trait_id, monster_traits.trait_name, monster_traits.description
FROM monsters
LEFT JOIN monster_traits ON monsters.id = monster_traits.monster_id
GROUP BY monsters.id
ORDER BY monsters.name ASC LIMIT 3’

$stmt = $db->prepare($query);
$stmt->execute();

$monsters = array();
while($row = $stmt->fetch()) { // each row is a monster + a trait
if(!isset($monsters[$row->id])) {
// we haven’t added this monster yet, let’s add the basic data
$monsters[$row->id] = array(
‘id’ => $row->id,
‘name’ => $monster->name,
‘traits’ => array()
);
}

// add the row trait data to a new array element in the current monsters traits
$monsters[$row->id][‘traits’][] = array(
‘id’ => $row->trait_id,
‘name’ => $row->trait_name,
‘description’ => $row->description
);
}

echo ‘

’;
print_r($monsters);
echo ‘
’;[/php]

[hr]

Then you can/should refactor the code so your controller code is as tidy as possible. So instead of all of the above you just need to do something like $monstersService->findLatestMonstersWithTraits(), and that service handles the messy code.

[hr]

You could alternatively use an ORM, like Doctrine2, Active Record or others. These support fetching child entities in an easier manner. In Doctrine2 the above code could look like this:

[php]// doctrine query language, here we reference the entity classes instead of the database tables
$dql = ‘SELECT m, t
FROM Monster m
JOIN m.traits t
GROUP BY m.id
ORDER BY m.name ASC LIMIT 3’;

// em, or entity manager is the class you use in Doctrine2 to pass entities (objects) to and from the database
$query = $em->createQuery();

$monsters = $query->getResult();[/php]

If the entities (php classes reflecting each table in your db - not counting join tables) are correctly set up with relations, this should yield the same result as the code above, without us having to manually create the multi dimensional array. Since the relations are in the entities we do not need to reference those here, we can simply say we want the monsters traits (m.traits), and each monster will know which traits belong to it.

Doctrine2 is used by the Symfony framework + others, I highly suggest having a look. It

[ul][li]supports multiple databases[/li]
[li]generates and updates the db scheme for you (join tables and all)[/li]
[li]allows you to lazy/eager load associated entites without any fuzz[/li]
[li]has lifecycle events for entities (hash password before create/update, etc)[/li]
[li]has a query builder to build queries instead of writing sql/dql[/li]
[li]has built in support for caching “compiled” entities, sql queries, query results to various cache stores[/li]
[li]etc[/li][/ul]

Sponsor our Newsletter | Privacy Policy | Terms of Service