Tony Tony - 5 years ago 167
SQL Question

MySQLi append more/deeper results

I'm having the hardest time figuring this out and it's probably because I'm not using the correct terms. If someone could point me in the right direction, that would be amazing. I'm going to use a hypothetical situation to make things easier:

I have a database with two tables:
tableA contains records for a house sale (house ID,address, price, current owner ID, etc)
tableB contains records for realtors who have shown a house (house ID, realtor ID, time and date, notes, etc).

I would like to have a query that can search a current owner ID and pull down all of their houses with information on everyone who showed the house. What I would like to retrieve is a JSON array that has the info from each tableB record appended/attached/added to a single record from tableA.

For example, if I search the the houses that are owned by ownerX (who owns two houses), I would like it to return two main items with sub items for each related entry in tableB. In the example below, ownerX has two houses. The first house on 1234 Fake St had 2 different realtors make a total of 3 visits. The second house on 555 Nowhere St had 1 realtor visit twice.

Here's how I'd like to retrieve the info:

tableA - Result 1 (House at address 1234 Fake St)
tableB - Result 1 (Realtor ID 1234, etc)
tableB - Result 2 (Realtor ID 1234, etc)
tableB - Result 3 (Realtor ID 2222, etc)

tableA - Result 2 (House at address 555 Nowhere St)
tableB - Result 1 (Realtor ID 1111, etc)
tableB - Result 2 (Realtor ID 1111, etc)


Instead, what I'm getting is this:

tableA - Result 1 (House at address 1234 Fake St),tableB(Realtor ID 1234, etc)
tableA - Result 2 (House at address 1234 Fake St),tableB(Realtor ID 1234, etc)
tableA - Result 3 (House at address 1234 Fake St),tableB(Realtor ID 2222, etc)
tableA - Result 4 (House at address 555 Nowhere St),tableB(Realtor ID 2222, etc)
tableA - Result 5 (House at address 555 Nowhere St),tableB(Realtor ID 2222, etc)


I don't don't want to retrieve tableA information each time. I only need that once, then each sub-result from tableB. This is important because I'm returning the data to an app that creates a new list. I'm currently using mysqli_multi_query

$sql = "SELECT * FROM tableA WHERE ownerID = "ownerX";";
$sql. = "SELECT tableB.*, tableA.houseID FROM tableB,tableA WHERE tableB.houseID = tableA.houseID;";


Again, the actual content is just a hypothetical. I'm looking for more of a, "You're an idiot, you should be using _____" and not, "You misspelled realtor and that's probably causing the problem.".

Also, please note that I'm not asking for the results to be formatted with the dashes and parentheses as they are above. I'm just simply writing it that way so it's easier to understand. I'm looking for a way to have sub-objects in a JSON array.

Any help pointing me in the correct direction would be much appreciated! Thanks to whoever takes the time to take a stab at this!
Tony

Additional information:
Here's the code I'm using to run the query:

$sql = "SELECT * FROM clocks WHERE user_key='".$userkey."';";
$sql .= "SELECT * FROM milestones WHERE (SELECT clock_key FROM clocks WHERE user_key='".$userkey."') = milestones.clock_key";


if (mysqli_multi_query($con,$sql))
{
do
{
if ($result=mysqli_store_result($con)) {
while ($row=mysqli_fetch_row($result))
{
$myArray[] = $row;
}
echo json_encode($myArray);
mysqli_free_result($result);
}
}
while(mysqli_more_results($con) && mysqli_next_result($con));
}


UPDATE WITH ANSWER:



Thanks for @vmachan's post below, I ended up getting all of my data at once, then ran through some loops to adjust the array. I'm going to use the house/relator example from above.

I used his code to get my results ($house_id is a variable input id):

$sql = "SELECT * FROM tableA INNER JOIN tableB ON tableA.houseID = tableB.houseID WHERE tableA.houseID='".$house_id."';";


I was given an array with 5 items because tableB had 5 entries. Since there are only 2 house entries in tableA, it looked like this:

["houseID"=>"1","price"=>"50000", "owner" => "Mike G", "state"=>"CA", "realtor" => "Jane D", "visitDay"=>"Tuesday", "notes" => "They liked the house"],
["houseID"=>"1","price"=>"50000", "owner" => "Mike G", "state"=>"CA", "realtor" => "Jane D", "visitDay"=>"Wednesday", "notes" => "They loved the house"],
["houseID"=>"1","price"=>"50000", "owner" => "Mike G", "state"=>"CA", "realtor" => "Stephanie W", "visitDay"=>"Friday", "notes" => "They didn't like the house"],
["houseID"=>"2","price"=>"65000", "owner" => "Michelle K", "state"=>"AL", "realtor" => "Mark S", "visitDay"=>"Tuesday", "notes" => "They made an offer"],
["houseID"=>"2","price"=>"65000", "owner" => "Michelle K", "state"=>"AL", "realtor" => "Jim L", "visitDay"=>"Monday", "notes" => "They stole stuff"]


The first 5 elements are from tableA and don't change. So, I used a loop to basically check the houseID, if it's a new house, create a new house element, otherwise, add the details from tableB to the current house element:

<?php
//$house is an array will hold all of our indiviaul houses and their infomation.
$houseArray = array();

//Start the foreach loop
foreach($items as $item){

//$item["houseID"] is the houseID from our database that we got from the above code.
$houseID =$item["houseID"];

//$currentID is a varible that is set after the first iteration.
//This checks to see if we're still working with the same house, or a new house.
if($currentID!=$houseID){

//Create an array to hold all of the relator visit information arrays.
//This is created within the loop as it will erased if a new houseID is found in the array.
$relatorVisitArray = array();

//This is a secondary loop that checks the same array. This time, we are only working with the new houseID that from the condition above.
foreach($items as $rv){

//This cheecks to see if there is a match between the current houseID that we're working with and the other houseIDs in the array. Since we're going through the same array that we're already iterating, it will find itself (which is good).
if($houseID==$rv["houseID"]){

//Once is gets a match, it will create a temporary array to hold the "Relator Visit" information. The array is created within the loop as it needs to be cleared through each iteration.
$tempRealitorVisit = array(
'name' => $rv["name"],
'day' => $rv["day"],
'houseID' => $rv["houseID"],
'notes' => $rv["notes"]
);

//At the end of each iteation, we add the temporary to the main $relatorVisitArray.
$relatorVisitArray[] = $tempRealitorVisit;
}
}

//At this point, the subloop has ended and we're created an array ($relatorVisitArray) which contains all of the $tempRealitorVisit arrays.
//Remember, were are still within the first loop and have determined that this is a new house.
//Now we'll create a new house array based on the current houseID in this iteration.
//This array is created within the loop because we want it to cear at the next iteation when it's determined that it's a new house.
$house = array(
'houseID' => $item["houseID"],
'owner' => $item["owner"],
'price' => $item["price"],
'location' => $item["location"],
'relatorVisits' =>
//Here, we simply add the $relatorVisitArray to a key called, "relatorVisits" (ie an array within an array).
$relatorVisitArray
);

//We then add the $house to the $houseArray.
$houseArray[] = $house;

//Finally, we set $currentID to $item["houseID"]. At the next iteration, it will check this id against the next house ID. If they are the same, this entire code will skip until a new houseID appears from your database.
$currentID= $item["houseID"];

}

}

//This prints all of the information so it's easy to read.
echo '<pre>';
print_r($houseArray);
echo '</pre>';
}

?>


In the end, I'm left with one array that contains two sub arrays. The first sub array (House 1) contains 3 sub arrays (3 visits to that house). The second sub array (House 2) contains 2 sub arrays (2 visits to that house).

I hope this helps anyone that had the same issue as me. If anyone knows of a cleaner way to do this, please post it here! Thanks for the guidance!
Tony

Answer Source

I think you can combine the SQL statements as shown below to JOIN the clocks and milestones tables on the clock_key for a user-provided value i.e. $userkey. Then in your code you could loop thru the results and then check for consecutive house_ids.

$sql = "SELECT * FROM clocks INNER JOIN milestones ON clocks.clock_key = milestones.clock_key WHERE clocks.user_key='".$userkey."';";

You can then use the code similar to the one in ths SO posting. You would need to change it so that inside the loop you check if the previous 'house_id' is the same as the current one and if not, you would start a new parent array other wise keep adding to the existing array. At the end of the loop you could then call the encode to get your JSON format.

Hope this helps.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download