【问题标题】:how to efficiently convert Raw sql query to Eloquent如何有效地将原始 sql 查询转换为 Eloquent
【发布时间】:2021-09-02 09:17:52
【问题描述】:

我有一个原始 SQL 查询

SELECT cc.name, pp.population
FROM population_table pp
WHERE pp.country_name IN (
    SELECT cc.name 
    FROM `countries` cc
)
GROUP BY pp.country_name

上述查询给出了countries 表中的国家/地区列表以及population 值。

我正在尝试通过将查询分成两个来将此查询转换为 eloquent。我在数据库级别使用这个雄辩的。

public function getCountriesWithPopulation() {
    $countryTable = Country::$TABLE_NAME;
    $populationTable = Population::$TABLE_NAME;

    $query1 = // eloquent query
    $query2 = // eloquent query
}

如何通过拆分查询将原始查询转换为 eloquent?


编辑:

SQL 查询

SELECT country, region, area, pp.population
FROM population_table pp
FROM (
    SELECT cc.country, cc.region, cc.area 
    FROM `countries` cc
) 
GROUP BY country

【问题讨论】:

    标签: php laravel eloquent


    【解决方案1】:

    您不需要运行两个查询。只需将其视为直接 JOIN:

    SELECT cc.name, pp.population
    FROM population_table AS pp
    JOIN `countries` AS cc 
        ON (cc.name = pp.country_name)
    GROUP BY pp.country_name
    

    Eloquent 内置了对 JOIN 的支持。在这种情况下,GROUP BY 也是不必要的,因为每个country 只检索到一个国家/地区的name。

    但实际上,如果数据库处于合理状态(countries 和 population 之间存在 1:1 对应关系),您甚至根本不需要cc。国家name 不太可能在pp 中可用(它应该是countries 的外键),所以就目前情况而言,您可能只是这样做:

    SELECT country_name, population
    FROM population_table;
    

    除非您将population 表进行了存储。那么对于JOIN 来说事情很容易,对于 Eloquent 来说就更难了,population 有可能错过人口普查年:

    SELECT cc.name, pp.population
    FROM population_table AS pp
    JOIN `countries` AS cc
        ON (
            cc.name = pp.country_name AND 
            pp.year=$selectedYear
        )
    GROUP BY pp.country_name
    

    【讨论】:

    • 感谢您的简报。这也适用于编辑部分中提到的查询吗?
    • 是的,当然。它的工作方式是,它将选择所有国家,然后选择该国家已知的那些人口。这相当于人口与国家的加入。实际上,对于现代 MySQL,JOIN 更好,因为新的默认值 (ONLY_FULL_GROUP_BY) 可能不接受“GROUP BY pp.country_name”但需要“GROUP BY cc.name, pp.population”,除非聚合使用函数。直接 JOIN 不需要 GROUP BY,所以不会受到影响。根据表结构、基数和索引,它也可能更快。
    【解决方案2】:

    根据您的查询,

    SELECT cc.name, pp.population
    FROM population_table pp
    WHERE pp.country_name IN (SELECT cc.name FROM `countries` cc) 
    GROUP BY pp.country_name 
    

    你可以用 Eloquent 来达到同样的效果,比如

    public function getCountriesWithPopulation()
    {
        // get country names as array
        $countryNames = Country::pluck('name');
    
        // get your result
        $result = Population::whereIn('country_name', $countryNames)->groupBy('country_name');
    }
    

    编辑:

    matiaslauriti 的评论是对的,您可以直接使用pluck('name') 而不是all()->pluck('name') 以提高效率和性能。

    Country::pluck('name') 将返回 country 表中所有name 列数据,以便您稍后使用whereIn() 过滤人口数据

    Population::whereIn('country_name', $countryNames)->groupBy('country_name') 将返回人口表中的所有数据,其中列country_name 在$countryNames 数组中。查询将按country_name 列分组,这样就不会出现国家/地区名称重复。

    【讨论】:

    • 感谢您的简报,您能解释一下编辑中提到的查询吗?
    • 永远不要做all()->pluck('name');,总是直接做pluck('name');,因为all会得到所有行(可能很多)然后你只能从他们每个人那里得到name ...浪费了大量内存,PHP 正在将所有数据模型转换为每个模型的名称...
    猜你喜欢
    • 2021-09-11
    • 2019-05-31
    • 2018-09-12
    • 2019-04-04
    • 2019-04-29
    • 2020-02-07
    • 1970-01-01
    • 2018-01-04
    • 1970-01-01
    相关资源
    最近更新 更多