从MySQL链接表的SELECT查询中创建PHP多维关联数组

6

我希望你能帮助我解决这个问题。

假设我有3个数据库表:

Users:
user_id, user_name
100, John
101, Jessica

Cars:
car_id, car_name
30, Corvette
31, BMW


UsersCars:
user_id, car_id, car_colour
100, 30, Red
101, 30, Green
101, 31, Green

(so John got a red corvette and Jessica has a green Corvette and a BMW)

我希望有一段代码能够返回一个类似下面这样的多维PHP数组:
Array
(
    [100] => Array
    (
        [user_id] => 100
        [user_name] => John
        [cars] => Array
        (
            [car_id]=>30,
            [car_name]=>'Corvette',
            [car_colour]=>'Red'

        )            
    )
    [101] => Array
    (
        [user_id] => 101
        [user_name] => Jessica
        [cars] => Array
        (
            [0] => Array
            (
                [car_id]=>30,
                [car_name]=>'Corvette',
                [car_colour]=>'Green'
            ),
            [1] => Array
            (
                [car_id]=>31,
                [car_name]=>'BMW',
                [car_colour]=>'Green'
            )
        )            
    )
)

我有以下SQL语句:

SELECT u.*, c.* FROM Users u
LEFT JOIN UsersCars uc ON u.user_id = uc.user_id
LEFT JOIN Cars c ON uc.car_id = c.car_id

还有PHP

$result = mysqli_query($db, $q);
while ($row = mysqli_fetch_assoc($result)) {
    $users_with_cars[$row['user_id']] = $row;
}

但这是不正确的。有人知道如何解决这个问题并得到上述数组(考虑性能)吗?我不想硬编码“cars”是可能会多次发生的异常情况。我更希望有一些东西只需查看 $row 和 $users_with_cars,当看到一些新值时,通过将旧值转换为数组来添加它。也许已经有一个原生的 PHP 函数可以实现这个?或者更好的是,我的 MySQL 或整个方法是错误的?任何帮助或提示都将不胜感激。问候。

更新已解决
以下是更新,也许我最终可以帮助别人如何解决它。
我最终总是使用一个或多个汽车的数组,并调整表格以始终具有“id”作为列名。这样您就可以轻松扩展它。请参见以下示例;
Users:
id, name
100, John
101, Jessica

Cars:
id, name
30, Corvette
31, BMW


UsersCars:
user_id, car_id, car_colour
100, 30, Red
101, 30, Green
101, 31, Green



$q = 'SELECT u.*, c.id as car_id, c.name as car_name, uc.colour as car_colour FROM Users u
LEFT JOIN UsersCars uc ON u.id = uc.user_id
LEFT JOIN Cars c ON uc.car_id = c.id';

$result = mysqli_query($db, $q);
while ($row = mysqli_fetch_assoc($result)) {
    $users_with_cars[] = $row;
}
$joins = array('cars' => array('car_id'=>'id','car_name'=>'name','car_colour'=>'colour'));
$users_with_cars = create_join_array($users_with_cars, $joins);

print_r($users_with_cars);



function create_join_array($rows, $joins){
    /* build associative multidimensional array with joined tables from query rows */

    foreach((array)$rows as $row){
        if (!isset($out[$row['id']])) {
            $out[$row['id']] = $row;
        }

        foreach($joins as $name => $item){
            unset($newitem);
            foreach($item as $field => $newfield){
                unset($out[$row['id']][$field]);
                if (!empty($row[$field]))
                    $newitem[$newfield] = $row[$field];
            }
            if (!empty($newitem))
                $out[$row['id']][$name][$newitem[key($newitem)]] = $newitem;
        }
    }

    return $out;
}

这一切都导致了美丽的数组:
Array
(
    [100] => Array
    (
        [id] => 100
        [name] => John
        [cars] => Array
        (
            [30] => Array
            (
                [id]=>30
                [name]=>'Corvette',
                [colour]=>'Red'
            )
        )            
    )
    [101] => Array
    (
        [id] => 101
        [name] => Jessica
        [cars] => Array
        (
            [30] => Array
            (
                [id]=>30,
                [name]=>'Corvette',
                [colour]=>'Green'
            ),
            [31] => Array
            (
                [id]=>31,
                [name]=>'BMW',
                [colour]=>'Green'
            )
        )            
    )
)

假设用户也可以拥有多辆自行车。那么,你将有多个连接数组,你可以轻松地通过左连接绑定它们,并将其添加到连接数组中。

$q = 'SELECT u.*, c.id as car_id, c.name as car_name, uc.colour as car_colour, b.id as bike_id, b.name as bike_name FROM Users u
LEFT JOIN UsersCars uc ON u.user_id = uc.user_id
LEFT JOIN Cars c ON uc.car_id = c.id
LEFT JOIN UsersBikes ub ON u.user_id = ub.user_id
LEFT JOIN Bikes b ON ub.bike_id = b.id';

$result = mysqli_query($db, $q);
while ($row = mysqli_fetch_assoc($result)) {
    $users_with_cars_bikes[] = $row;
}

$joins = array('cars' => array('car_id'=>'id', 'car_name'=>'name', 'car_colour'=>'colour'), 
               'bikes' => array('bike_id'=>'id', 'bike_name'=>'name'));
$users_with_cars_bikes = create_join_array($users_with_cars_bikes, $joins);

print_r($users_with_cars_bikes);

会导致类似以下的结果。
Array(
    [100] => Array
    (
        [id] => 100
        [name] => John
        [cars] => Array
        (
            [30] => Array
            (
                [id]=>30
                [name]=>'Corvette',
                [colour]=>'Red'
            )
        )
        [bikes] => Array
        (
            [41] => Array
            (
                [id]=>41
                [name]=>'BMX'
            )
        )           
    )
)

感谢所有帮忙的人 : )

等等还有更多..


不,PHP中没有本地函数可以做到这一点。您需要自己构建数组。在存储基本用户信息及其所带的汽车之前,请检查是否已将用户保存在数组中。如果用户不存在,则添加基本用户信息并加入车辆数据。如果该用户已存在,则只需添加车辆数据即可。 - Marc B
(顺便说一句:对于您的 [cars] 数组)我只是对某些 Web 服务感到恼火,因为它们有时将特定变量作为单个字符串或数组返回,而在另一个结果集中则作为数组的数组返回。因此,我需要在客户端检查每个值是否有更多的子项。如果您拥有一个数据结构,它在不同的结果集中没有改变,那么再次阅读数据将会更容易。因此,您的 [cars] 数组应始终包含任何汽车的“父”数组,即使只有一个条目。 - feeela
@feeela 没错,同意,谢谢。我还不确定这个设计选择是否正确。当然,在使用它时也可以将其转换为数组:foreach ((array)$cars as $car){} 但这没有意义。 - Sanne
@Sanne 如果转换后的值是一个对象,则不是这样的。(array) new StdClass() 也会返回一个数组。但你得到的不是包含单个对象的数组 (array( new StdClass() )),而是对象成员的数组。 - feeela
2个回答

2

这是我能想到的方法。它将(可能)创建一个类似于您期望的输出的数组。不确定它是否有效,我没有测试就在这里写了。让我知道吧!:)

$result = mysqli_query($db, $q);
while ($row = mysqli_fetch_assoc($result)) 
{
    // Add user ID and name to the array
    $users_with_cars[$row['user_id']]['user_id'] = $row['user_id'];
    $users_with_cars[$row['user_id']]['user_name'] = $row['user_name'];
    // Check if this user has cars in the array. If not, this is the first car (see else)
    if(isset($users_with_cars[$row['user_id']]['cars']))
    {
        // Check if there is exactly 1 car in the array
        if(count($users_with_cars[$row['user_id']]['cars']) == 1)
        {
            // If yes, put that car in a 'sub array'
            $users_with_cars[$row['user_id']]['cars'] = array(0 => $users_with_cars[$row['user_id']]['cars']);
            // Then add the new car
            $users_with_cars[$row['user_id']]['cars'][] = array('car_id' => $row['car_id'], 'car_name' => $row['car_name'], 'car_color' => $row['car_color']);
        }
        else
        {
            // It already has more than one car in the array. Just add it
            $users_with_cars[$row['user_id']]['cars'][] = array('car_id' => $row['car_id'], 'car_name' => $row['car_name'], 'car_color' => $row['car_color']);
        }
    }
    else
    {
        // Add a single car without 'sub array'
        $users_with_cars[$row['user_id']]['cars']['car_id'] = $row['car_id'];
        $users_with_cars[$row['user_id']]['cars']['car_name'] = $row['car_name'];
        $users_with_cars[$row['user_id']]['cars']['car_color'] = $row['car_color'];
    }
}


编辑:

这是我在评论中提到的一个例子:

$result = mysqli_query($db, $q);
while ($row = mysqli_fetch_assoc($result)) 
{
    $count = count($users_with_cars[$row['user_id']]['cars']);
    foreach($row AS $key => $value)
    {
        if(substr($key, 0, 4) == "car_")
        {
            // Single:
            $users_with_cars[$row['user_id']]['cars'][$key] = $value;
            // Multiple:
            $users_with_cars[$row['user_id']]['cars'][$count][$key] = $value;
        }
    }
}

谢谢,是的,我认为这段代码是正确的,因为它是Sergey代码的一部分。但基本上,没有本地快速解决方案?这让我有点惊讶,因为很多设计都有链接表并需要将其转换为多关联数组.. :/ 这些解决方案也有点痛苦,因为如果您更改DB结构(例如添加到汽车引擎列),则还必须修改代码。 - Sanne
@Sanne 很不幸,没有本地函数可以做这样的事情。这可能很烦人,但这可能是最好的方法。如果你想要更动态的方式,你可以循环遍历 $row 并添加每一列。由于 cars 的数据以 car_ 开头,所以你可以检查一个列是否以 car_ 开头,如果是,就将它添加到数组中。这意味着如果你添加了一个规格,你不必改变代码。 - Deep Frozen
1
谢谢,这激发了我一个解决方案的灵感。 - Sanne

0
$result = mysqli_query($db, $q);
$users_with_cars = array();
while ($row = mysqli_fetch_assoc($result)) {
    if (!isset($users_with_cars[$row['user_id']])) {
        $user = array();
        $user['user_id'] = $row['user_id'];
        $user['user_name'] = $row['user_name'];
        $user['cars'] = array();
        $users_with_cars[$row['user_id']] = $user;
    }
    $car = array();
    $car['car_id'] = $row['car_id'];
    $car['car_name'] = $row['car_name'];
    $car['car_colour'] = $row['car_colour'];
    $users_with_cars[$row['user_id']]['cars'] = $car;
}

谢谢,这启发了我想到一个解决方案。 - Sanne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接