Jump to content

GROUP BY not working properly when used with multiple joined tables


Go to solution Solved by imgrooot,

Recommended Posts

Back with a new problem. 

I have 8 tables interconnected. 

Table#1 - Users
user_id | name

Table#2 - user_categories
id | user_id | category_id

Table#3 - user_cities
id | user_id | city_id

Table#4 - user_dates
id | user_id | dates_available

Table#5 - categories
category_id | category_name

Table#6 - cities
city_id | city_name

Table#7 - provinces
province_id | province_name

Table#8 - categories
country_id | country_name

Each user will have multiple categories, cities and available dates listed in these tables. I simply want to retrieve and list each user and their data on a page. 

Here's my query.

$url_city = 1;
$url_category = 2;
$url_date = '2021-07-19';

$find_records = $db->prepare("SELECT user_categories.*, categories.*, user_cities.*, cities.*, provinces.*, countries.*, user_dates.*, users.* FROM users
LEFT JOIN user_categories ON users.user_id = user_categories.user_id
LEFT JOIN user_cities ON users.user_id = user_cities.user_id
LEFT JOIN user_dates ON users.user_id = user_dates.user_id
LEFT JOIN categories ON user_categories.category_id = user_categories.category_id
LEFT JOIN cities ON user_cities.city_id = cities.city_id
LEFT JOIN provinces ON cities.province_id = provinces.province_id
LEFT JOIN countries ON provinces.country_id = countries.country_id
WHERE user_cities.city_id = :city_id AND user_categories.category_id = :category_id AND user_dates.date_available = :date_available GROUP BY users.user_id");
$find_records->bindParam(':city_id', $url_city);
$find_records->bindParam(':category_id', $url_category);
$find_records->bindParam(':date_available', $url_date);
$find_records->execute();
$result_records = $find_records->fetchAll(PDO::FETCH_ASSOC);
if(count($result_records) > 0) {
	foreach($result_records as $row) {

		$user_id    	  =	$row['user_id'];
        $name    		  =	$row['name'];

        $country_id       =	$row['country_id'];
        $country_code     =	$row['country_code'];
        $country_name     =	$row['country_name'];

        $province_id      =	$row['province_id'];
        $province_code    =	$row['province_code'];
        $province_name    =	$row['province_name'];

        $city_id          =	$row['city_id'];
        $city_name        =	$row['city_name'];

        $category_id      =	$row['category_id'];
        $category_name    =	$row['category_name'];

     }
}

There are no errors but the above query would only return a single row with only 1 "user" despite having multiple users in the "users" table. If I remove the GROUP BY, then it'll return multiple rows of the same user instead of all the relevant users.

So what do you think I am doing wrong with my query? 

Edited by imgrooot

GROUP BY some_col consolidates all the rows having the same some_col value into a single row, so, for each user, there would only be one row in the result set. if you want the rows for each user adjacent to each other in the result set, add an ORDER BY term (almost every select query should have one) and/or index/pivot the data when you retrieve it using the user_id as the main array index, to produce an array of sub-arrays for each user.

as to why you are only getting the data for a single (the last user), your code inside the loop is needlessly copying variables to other variables, overwriting any previous data, until finally, after the end of the loop, you are left with only the last row of data. once you have fetched all the data into an appropriately named variable, $result_records, simply loop over that variable to produce the output that you want, no need to create 12 lines of code coping data from one variable to another for nothing.

Edited by mac_gyver
  • Solution
19 minutes ago, mac_gyver said:

GROUP BY some_col consolidates all the rows having the same some_col value into a single row, so, for each user, there would only be one row in the result set. if you want the rows for each user adjacent to each other in the result set, add an ORDER BY term (almost every select query should have one) and/or index/pivot the data when you retrieve it using the user_id as the main array index, to produce an array of sub-arrays for each user.

as to why you are only getting the data for a single (the last user), your code inside the loop is needlessly copying variables to other variables, overwriting any previous data, until finally, after the end of the loop, you are left with only the last row of data. once you have fetched all the data into an appropriately named variable, $result_records, simply loop over that variable to produce the output that you want, no need to create 12 lines of code coping data from one variable to another for nothing.

Actually I found the issue. My query works fine. The issue was that only 1 user in my database matched all three parameters.  Which is  why it was only showing a single result. So it's all good now.

Having said that if you would like to modify my query to show your example, that'd be great.

4 hours ago, imgrooot said:

So what do you think I am doing wrong with my query?

Answer: The bits you got wrong are...

  1. The SELECT clause
  2. The WHERE clause
  3. Probably the JOIN clauses too (but without knowing what you expect as output if a user has no city or category data, it's impossible to be sure)
This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.