Yii2 Dynamic Relational Query Junction with Sort 使用 2 个查询而不是 JOIN,为什么?

Yii2 Dynamic Relational Query Junction with Sort uses 2 queries instead of a JOIN, why?

我正在使用 Yii2 关系数据库/活动查询模型,我 运行 遇到了一个问题,试图在尝试时使用魔术方法 getModelName()$this->hasMany()->viaTable() 来设置关系按我的连接 table.

中的 sort 列排序

首先,我尝试将 ->orderBy() 子句添加到主查询中:

    return $this->hasMany(Category::class,
        ['id' => 'categoryId'])
            ->viaTable('{{kits_x_categories}}',
                ['kitId' => 'id'])
            ->orderBy('{{kits_x_categories}}.sort asc');

这没有按预期工作,在进一步挖掘后我发现这会导致两个单独的查询,第一个将我的类别 ID 选择到一个数组中,然后将所述数组用于 WHERE IN() 子句在主要(第二个)查询中获取相关的实际模型。

我的第一个想法是使用 ->viaTable() 调用的第三个 function($query) {} 回调参数并将我的 $query->orderBy() 子句放在那里:

    return $this->hasMany(Category::class,
        ['id' => 'categoryId'])
            ->viaTable('{{kits_x_categories}}',
                ['kitId' => 'id'],
                function($query) {
                    return $query->orderBy('{{kits_x_categories}}.sort asc');
                }
            );

然而,所做的只是 return 类别 ID 按我想要的顺序排列,但最终对执行 IN() 条件的主查询没有影响,因为 ID 的顺序在 IN() 条件下没有任何影响。

最后,我得到了这个让它做它想做的事,但随后强制我使用 IN() 条件加入到主查询中,这样我就可以让主查询按我的联结排序table 排序列。这按预期工作:

    return $this->hasMany(Category::class,
        ['id' => 'categoryId'])
            ->viaTable('{{kits_x_categories}}',
                ['kitId' => 'id'])
        ->leftJoin('{{kits_x_categories}}', '{{kits_x_categories}}.categoryId = {{categories}}.id')
        ->where(['{{kits_x_categories}}.kitId' => $this->id])
        ->orderBy('{{kits_x_categories}}.sort asc');

这导致 2 个查询。

首先,查询从连接 table:

中获取类别 ID
SELECT * FROM `kits_x_categories` WHERE `kitId`='49';

然后是 IN() 条件的主查询和我的强制连接排序:

SELECT `categories`.* FROM `categories` 
    LEFT JOIN `kits_x_categories` ON `kits_x_categories`.categoryId = `categories`.id 
    WHERE (`kits_x_categories`.`kitId`='49') AND (`categories`.`id` IN ('11', '7', '9')) 
    ORDER BY `kits_x_categories`.`sort`

所以这是我的实际问题...这对我来说似乎效率很低,但我绝不是 database/SQL 神所以也许我只是不完全理解。我要的是明白。

Yii 为什么要这样做?首先进行一个查询以获取 ID,然后进行另一个查询以根据关系的 ID 获取对象有什么意义?在这里进行常规连接不是更有效率吗?然后,在我看来,按联结排序列排序将是直观的而不是违反直觉的。

我唯一能想到的就是延迟加载数据和急切加载数据有关,也许在延迟加载中只先获取 ID,然后在需要加载数据时使用 IN()?如果我使用 joinWith() 而不是 viaTable() 会有什么不同吗?我没有深入研究这个,因为我只是在打字时想到了这一点。

最后,在这种情况下,每个套件只会有几个类别,因此效率不是什么大问题,但我很好奇如果我要使用它,我的工作解决方案是否会对性能产生任何影响将来在可能有数千种关系的不同模型集上?

第 3 方软件通常旨在帮助您开始使用数据库。但是当应用程序增长时,它们就会分崩离析。这意味着除了层的细节之外,你还需要学习底层数据库的细节

可能可以通过使用此处的提示改进多对多 table 上的索引来解决此特定问题:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table 当然,这取决于该层是否允许您调整它为您创建的架构。

如果有一种写“原始”的方法SQL,那可能会让您摆脱两步过程,但您仍然需要改进 table 上的索引。

Yii 2 这样做:

  1. 支持延迟加载。
  2. 支持跨数据库关系如MySQL -> Redis.
  3. 显着减少边缘情况的数量,使内部 AR 代码变得不那么复杂。