datajoe/postgresql-distinct-on

为 Laravel 提供对 PostgreSQL 特定的 DISTINCT ON 特性的支持。兼容版本 5 至 8。

1.3.0 2022-05-02 16:00 UTC

This package is auto-updated.

Last update: 2024-09-30 01:29:48 UTC


README

它做了什么

  • 为 Laravel 5-9 提供对 PostgreSQL 特定的 DISTINCT ON 特性的支持。

参考 PostgreSQL 文档

SELECT DISTINCT ON (表达式[, ...]) 只保留每个集合中给定表达式评估为相等的行的第一行。DISTINCT ON 表达式使用与 ORDER BY 相同的规则解释(见上文)。注意,除非使用 ORDER BY 确保所需行首先出现,否则每个集合中“第一行”是不可预测的。

例如

SELECT DISTINCT ON (location) location, time, report
   FROM weather_reports
   ORDER BY location, time DESC;

检索每个位置的最新天气报告。但如果我们没有使用 ORDER BY 来强制每个位置的时间值的降序排列,我们可能会得到每个位置的不可预测时间的报告。

DISTINCT ON 表达式必须与最左边的 ORDER BY 表达式匹配。ORDER BY 子句通常包含额外的表达式,用于确定每个 DISTINCT ON 组内行的所需优先级。

为什么需要它?

我专门构建这个,因为我想要一个描述性元数据表,允许我保留给定键的所有版本的数据。我需要能够轻松地检索存储值的最新版本。Postgres 通过支持 SELECT DISTINCT ON 使这变得非常容易,然而 Laravel/Eloquent 没有对此数据库特定功能提供基本支持。

如何使用它

  1. 在您的项目目录中运行: composer require datajoe/postgresql-distinct-on

  2. 在您的 app.php 中,将 Illuminate\Database\DatabaseServiceProvider::class, 替换为 DataJoe\Extensions\Illuminate\Database\DatabaseServiceProvider::class,

  3. 在您的代码中,您可以使用 ->distinctOn('field_name') 方法访问 DISTINCT ON 功能。请确保也在您的查询中包含 ->orderBy('field_name')

示例

这将返回每个 distinct meta_name 的单个 RecordMeta 行,其中只包含最新 update_at 时间戳,对于记录_id 为 1 的每个不同 meta_name

$fields = RecordMeta::select(['record_id', 'value', 'updated_at'])
                 ->distinctOn('meta_name')
                 ->where('record_id', 1)
                 ->orderBy('meta_name')
                 ->orderBy('updated_at', 'desc');

该命令等价于以下 SQL

SELECT DISTINCT ON ('meta_name') meta_name, record_id, value, updated_at
    FROM record_meta
    WHERE record_id = 1
    ORDER BY meta_name, updated_at DESC;

您还可以做的事情

要能够通过模型关系访问元数据(例如,Record -> RecordMeta),您可以在您的记录模型中添加以下内容以建立自定义关系

public function recordMeta()
{
    $relation = $this->hasMany(RecordMeta::class);

    $relation->getQuery()
             ->select(['record_id', 'value', 'updated_at'])
             ->distinctOn('meta_name')
             ->orderBy('updated_at', 'desc');

    return $relation;
}

然后,在您的代码中,您可以像这样访问元数据

$record = Record::with('recordMeta')->get();

然后您将得到所有记录,其中每个记录的每个 meta_name 都只包含最近设置的值。