【问题标题】:How to load related objects efficiently in php/mysql?如何在 php/mysql 中有效地加载相关对象?
【发布时间】:2019-11-25 16:28:14
【问题描述】:

情况

我将 PHP 和 PDO 与 mysql 一起使用,并想查询 1 个表以及它在另一个表中指向的行。

我有这样的课:

class Pet {
    public $id;
    public $name;
    public $type;
    public $owner;
}

还有一张桌子

pet
id | name | type | owner_id
0  | jim  | cat  | 87
1  | gale | dog  | 50
2  | foxy | cat  | 60

您可以看到Pet 指的是所有者。这个类是这样的:

class Person {
    public $id;
    public $name;
    public $pets;
}

还有桌子

person
id | name 
87 | Joshua
50 | Bob
60 | Cynthia

现在,我想获取所有猫,每个属性集,包括主人。


解决方案 1

如果我有这样的查询:

SELECT pet.*, person.* FROM pet
JOIN person
    ON person.id = pet.ownerId
WHERE pet.type LIKE 'cat'

返回关联数组后,这将在idname 列上产生冲突。

我有两个选择
1.) 使用返回的数字索引映射到 Pet 对象和相关的 Person 对象
2.) 为表列名添加前缀并使用返回的关联数组来实例化对象


解决方案 2

或者我可以进行如下查询:

SELECT * FROM pet WHERE pet.type LIKE 'cat'

并得到一个结果关联数组。我可以遍历并获取所有所有者 ID 的列表并执行以下操作:

SELECT * FROM person WHERE person.id IN (LIST_OF_IDS)

然后实例化Person 对象并将它们分配给我的Pet 对象的$owner 属性。


投诉

解决方案 1.1(数字索引)

这在技术上是可行的,但是我必须将数字条目映射到正确的属性名称,这似乎很容易出错,尤其是对数据库架构进行任何更改时。

解决方案 1.2(前缀)

这将很容易工作,但在阅读时,不鼓励在列名前加上前缀。另外...我真的不想在列名前加上前缀。

解决方案 2

这可行,但似乎性能不佳。

问题

我还缺少其他选项吗?

我尝试想办法在SELECT 语句中为列名添加前缀(这很好,因为它不会影响实际的数据库),但似乎不可能。

【问题讨论】:

    标签: php mysql performance join design-patterns


    【解决方案1】:

    据我所知,您不能在 MySQL 中一次更改或添加 all 列名 (person.* as person.*),只能更改其他答案中提到的单个列。由于您也提到了使用 PDO,因此请考虑其他方法,其中两种方法如下。

    1.3:使用PDO语句getColumnMeta()

    这将询问数据库有关数字列的其他元数据:

    $qry = "SELECT pet.*, person.* FROM pet JOIN person ON person.id = pet.owner_id WHERE pet.type LIKE 'cat'";
    $stmt = $dbh->prepare($qry);
    $stmt->execute();       
    while($row = $stmt->fetch(PDO::FETCH_NUM)) {
        foreach($row as $key => $value) {
            $meta = $stmt->getColumnMeta($key);
            echo $meta['table'] . '.' . $meta['name'] . ': ' . $value . PHP_EOL;
        }
    }
    
    /* Output: 
    pet.id: 2
    pet.name: foxy
    pet.type: cat
    pet.owner_id: 60
    person.id: 60
    person.name: Bob
    ..
    */
    

    1.4:使用 PDO 语句 fetch 和 fetch_style PDO::FETCH_NAMED

    这将使具有相同名称的字段成为一个数组。仍然注意排序:SELECT pet.*, person.*SELECT person.*, pet.*

    $qry = "SELECT pet.*, person.* FROM pet JOIN person ON person.id = pet.owner_id WHERE pet.type LIKE 'cat'";
    $stmt = $dbh->prepare($qry);
    $stmt->execute();       
    while($row = $stmt->fetch(PDO::FETCH_NAMED)) {
        echo $row['name'][1] . ' owns a ' . $row['type'] . ' named ' . $row['name'][0] . PHP_EOL;
    }
    
    /* Output:
    Bob owns a cat named foxy
    Joshua owns a cat named jim
    */
    

    【讨论】:

    • 谢谢。我遇到了getColumnMeta,但我猜我看的时间不够长。没有注意到PDO::FETCH_NAMED。我想我会用getColumnMeta 写一个小函数来按表名拆分结果,然后走那条路。
    • 祝你好运! :) 也许this user note 可能会派上用场。
    • 不错的答案!我完全忘记了 FETCH_NAMED
    【解决方案2】:

    您可以为一个或两个表中的列设置别名,这样它们就不会模棱两可,例如:

    SELECT pet.*, person.name AS owner_name
    FROM pet JOIN person ON person.id = pet.ownerId
    WHERE pet.type LIKE 'cat'
    

    (您不需要从 person 表中选择 id,因为它已经包含在 ownerId 中。)

    【讨论】:

    • 是的,这是一个...非常有效的选项。我希望不要给我所有的列加上别名……也不要明确命名它们。不过,我也读过 table.* 是不好的做法,所以我想我应该克服它(并且明确和别名)?大声笑
    • IMO 使用 * 如果您确实需要每一列,这不是一个坏习惯,人们似乎只是随意使用它,如果您只需要其中几列,则选择整行是浪费的。根据我对此类事情的经验,您通常不需要连接一侧的所有列。
    • 如果您要加入的表没有太多列,我认为这样做并没有太大的痛苦,只需在一侧对选定的列进行别名。如果表格确实有这么多列,这很痛苦,那可能表明另一个问题。
    • 非常好的点。可能会走这条别名路线......因为这样我就不必把我的模式或前缀弄乱了作为一般规则。我可以很容易地编写一个类似makeSelect(string $table, array $columns, string $prefix) 的函数并返回类似table.col1 as pref_col1, table.col2 as pref_col2 的内容。如果我愿意,然后再删除结果集中的这些前缀。
    【解决方案3】:

    我认为最好的做法是不要在不同的表中使用相同的列名,应该建议这样做。列名应该描述其实体。在这种情况下 Pet.id 应该是 Pet.pet_id 等等。

    希望这会有所帮助。

    谢谢,

    【讨论】:

    • 我对这种方法有复杂的感觉。用表名作为列名的前缀会......做我需要做的事情,但是是多余的,因为我只是重复表名作为列名的前缀
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-24
    相关资源
    最近更新 更多