如何在 Laravel 中获取非键列字段的不同值?

Posted

技术标签:

【中文标题】如何在 Laravel 中获取非键列字段的不同值?【英文标题】:How to get distinct values for non-key column fields in Laravel? 【发布时间】:2014-10-03 10:16:45 【问题描述】:

这可能很容易,但不知道怎么做。

我有一个表,可以为特定的非键列字段包含重复值。如何使用 Query Builder 或 Eloquent 编写 SQL 查询,以获取该列具有不同值的行?

请注意,我不是仅获取该列,它与其他列值结合使用,因此distinct() 可能无法真正起作用。所以这个问题基本上可以是在distinct() 不接受任何参数的情况下,如何在查询中指定我想要区分的列?

【问题讨论】:

【参考方案1】:

您应该使用groupby。在查询生成器中,您可以这样做:

$users = DB::table('users')
            ->select('id','name', 'email')
            ->groupBy('name')
            ->get();

【讨论】:

我有一个表“消息”(用于聊天),我想获取经过身份验证的用户从每个对话中收到的最新消息。使用 groupBy 我只能收到一条消息,但我收到了第一条消息,我需要最后一条消息。似乎 orderBy 不起作用。 如果您不想应用任何数学运算,例如 SUM、AVG、MAX 等“分组依据”不是正确答案(您可以使用它,但您不应该使用它)。你应该使用不同的。在 mysql 中,您可以将 DISTINCT 用于列并向结果集中添加更多列:“SELECT DISTINCT name, id, email FROM users”。使用 eloquent 你可以使用 $this->select(['name', 'id', 'email'])->distinct()->get(); 当我添加 where() 时它会中断,我该如何解决这个问题? 当我使用它时,它显示 'id' 和 'email' 不在 groupBy() 中,我需要使用 groupBy('id','name', 'email')。哪个没用。 注意事项:group bydistinct 不同。 group by 将改变 SQL 查询中的聚合函数(如 count())的行为。 distinct 不会改变这些函数的行为,所以你会得到不同的结果,这取决于你使用哪一个。【参考方案2】:

在 Eloquent 中你也可以这样查询:

$users = User::select('name')->distinct()->get();

【讨论】:

问题具体问Note that I am not fetching that column only, it is in conjunction with other column values @Hirnhamster:Okkei。但是,这个答案对于路过的其他人仍然有用... 对我没用,我专门看 OP 是什么。不容易找到。 $users = User::select('column1', 'column2', 'column3')->distinct()->get(); 如果你想和chunk一起使用,你还需要->orderBy('name')【参考方案3】:

在 eloquent 中你可以使用这个

$users = User::select('name')->groupBy('name')->get()->toArray() ;

groupBy 实际上是获取不同的值,实际上 groupBy 会对相同的值进行分类,以便我们可以对它们使用聚合函数。但在这种情况下,我们没有聚合函数,我们只是选择会导致结果具有不同值的值

【讨论】:

这是如何工作的? groupBy 实际上是在获取不同的用户名吗?您能否再解释一下这如何回答 op 的问题? 是的 groupBy 实际上是在获取不同的值,实际上 groupBy 会对相同的值进行分类,以便我们可以对它们使用聚合函数。但在这种情况下,我们没有聚合函数,我们只是选择会导致结果具有不同值的值。 我明白了.. 那么,这个答案与 Marcin 的答案有何不同?有不同的答案是可以的,只要你能解释它有什么不同以及它解决了什么缺陷:) 不要在 cmets 中回答,请直接用相关信息编辑你的问题。 结果是一样的,这取决于你的项目是使用ORM还是使用查询生成器,如果你使用其中之一,那么最好坚持使用那个。这就是为什么我以不同的方式回答你的问题。 好吧,我有这个问题,如果你现在想使用in_array() 函数检查一个项目是否存在,它永远不会工作。为了解决这个问题,我尝试了->lists()(版本 5.2)。所以,$users = User::select('name')->groupBy('name')->lists('name');phpin_array() 工作得很好;【参考方案4】:

虽然我回答这个问题迟了,但使用 Eloquent 获取不同记录的更好方法是

$user_names = User::distinct()->get(['name']);

【讨论】:

好的,您通过向我们展示了也可以将字段名称的参数传递给get() 方法(我也测试过first() 方法)来增加价值,这是相当于使用select() 方法,如这里的几个答案所示。虽然不知何故groupBy 似乎仍然得分最高。但是,如果这是我选择的唯一列,这将是真正不同的。 性能方面,distinct 将超过 groupBy,因为记录将首先在 SQL 引擎中被选择,这与 Group By 引擎不同,引擎首先选择所有记录,然后再分组。 $user_names = User::distinct()->count(['name']); 也可以【参考方案5】:

如果数据库规则不允许任何选择字段位于聚合函数之外,则分组依据将不起作用。请改用laravel collections。

$users = DB::table('users')
        ->select('id','name', 'email')
        ->get();

foreach($users->unique('name') as $user)
  //....

有人指出,这对于大型集合的性能可能不是很好。我建议在集合中添加一个密钥。使用的方法称为keyBy。这是简单的方法。

     $users = DB::table('users')
        ->select('id','name', 'email')
        ->get()
        ->keyBy('name');

keyBy 还允许您为更复杂的事情添加回调函数...

     $users = DB::table('users')
        ->select('id','name', 'email')
        ->get()
        ->keyBy(function($user)
              return $user->name . '-' . $user->id;
         );

如果您必须对大型集合进行迭代,则为其添加一个键可以解决性能问题。

【讨论】:

你是对的,这正是我所面临的问题......但是等到执行查询并执行集合中的操作也不理想。特别是如果您依赖分页,您的操作将在分页计算之后,并且每页的项目将是错误的。此外,数据库更适合对大数据块进行操作,而不是在代码中检索和处理它。对于小数据块,这将不是问题 你说得很好。这种方法在大型集合上的性能可能不是很好,而且这不适用于分页。我认为有比我所拥有的更好的答案。【参考方案6】:

**

为 Laravel 5.8 测试

**

由于您想从表中获取所有列,您可以收集所有数据,然后使用名为 Unique 的 Collections 函数对其进行过滤

// Get all users with unique name
User::all()->unique('name')

// Get all & latest users with unique name 
User::latest()->get()->unique('name')

更多信息可以查看Laravel Collection Documentations

编辑:您可能会遇到性能问题,通过使用 Unique() 您将首先从 User 表中获取所有数据,然后 Laravel 将对其进行过滤。 如果您有大量用户数据,这种方式并不好。您可以使用查询生成器并调用您想要使用的每个字段,例如:

User::select('username','email','name')->distinct('name')->get();

【讨论】:

这效率不高,因为您首先从 db 获取所有数据 这就是为什么它被称为过滤,您可以使用 distinct() 作为查询构建器,但您无法获取其他字段(无论如何您仍然可以通过 select 调用每个字段)。通过使用 unique() 是获取所有字段而不调用每个字段的唯一方法(尽管我们知道这可能会导致性能问题)。【参考方案7】:

请注意,上面使用的 groupBy 不适用于 postgres。

使用distinct 可能是更好的选择 - 例如 $users = User::query()->distinct()->get();

如果您使用query,您可以按要求选择所有列。

【讨论】:

【参考方案8】:

$users = User::select('column1', 'column2', 'column3')->distinct()->get(); 检索表中不同行的所有三个列。您可以添加任意数量的列。

【讨论】:

【参考方案9】:

我发现这种方法(对我来说)工作得很好,可以生成唯一值的平面数组:

$uniqueNames = User::select('name')->distinct()->pluck('name')->toArray();

如果您在此查询构建器上运行 ->toSql(),您将看到它生成如下查询:

select distinct `name` from `users`

->pluck() 由Illumination\collection lib 处理(不是通过sql 查询)。

【讨论】:

【参考方案10】:

在尝试填充用户与其他用户拥有的所有唯一线程的列表时,我遇到了同样的问题。这对我有用

Message::where('from_user', $user->id)
        ->select(['from_user', 'to_user'])
        ->selectRaw('MAX(created_at) AS last_date')
        ->groupBy(['from_user', 'to_user'])
        ->orderBy('last_date', 'DESC')
        ->get()

【讨论】:

【参考方案11】:
// Get unique value for table 'add_new_videos' column name 'project_id'
$project_id = DB::table('add_new_videos')->distinct()->get(['project_id']);

【讨论】:

【参考方案12】:

对于那些喜欢我犯同样错误的人。这是详细的答案 在 Laravel 5.7 中测试

A.数据库中的记录

UserFile::orderBy('created_at','desc')->get()->toArray();

Array
(
    [0] => Array
        (
            [id] => 2073
            [type] => 'DL'
            [url] => 'https://i.picsum.photos/12/884/200/300.jpg'
            [created_at] => 2020-08-05 17:16:48
            [updated_at] => 2020-08-06 18:08:38
        )

    [1] => Array
        (
            [id] => 2074
            [type] => 'PROFILE'
            [url] => 'https://i.picsum.photos/13/884/200/300.jpg'
            [created_at] => 2020-08-05 17:20:06
            [updated_at] => 2020-08-06 18:08:38
        )

    [2] => Array
        (
            [id] => 2076
            [type] => 'PROFILE'
            [url] => 'https://i.picsum.photos/13/884/200/300.jpg'
            [created_at] => 2020-08-05 17:22:01
            [updated_at] => 2020-08-06 18:08:38
        )

    [3] => Array
        (
            [id] => 2086
            [type] => 'PROFILE'
            [url] => 'https://i.picsum.photos/13/884/200/300.jpg'
            [created_at] => 2020-08-05 19:22:41
            [updated_at] => 2020-08-06 18:08:38
        )
)

B.期望的分组结果

UserFile::select('type','url','updated_at)->distinct('type')->get()->toArray();

Array
(
    [0] => Array
        (
            [type] => 'DL'
            [url] => 'https://i.picsum.photos/12/884/200/300.jpg'
            [updated_at] => 2020-08-06 18:08:38 
        )

    [1] => Array
        (
            [type] => 'PROFILE'
            [url] => 'https://i.picsum.photos/13/884/200/300.jpg'
            [updated_at] => 2020-08-06 18:08:38
        )
)

所以只传递"select()" 中的那些列,它们的值是相同的。 例如:'type','url'。您可以添加更多列,前提是它们具有相同的值,例如 'updated_at'

如果您尝试在"select()" 中传递"created_at""id",那么您将获得与A 相同的记录。 因为它们对于 DB 中的每一行都是不同的。

【讨论】:

【参考方案13】:
$users = Users::all()->unique('name');

【讨论】:

请解释您的代码 sn-p 如何解决 OP 的问题。【参考方案14】:

以下是我测试过的 3 种方法,它们会给出相同的结果:

User::distinct()->get(['name'])->pluck('name');

User::select('name')->distinct()->pluck('name')->all();

DB::table('users')->select('name')->groupBy('name')->get()->pluck('name')->all();

【讨论】:

以上是关于如何在 Laravel 中获取非键列字段的不同值?的主要内容,如果未能解决你的问题,请参考以下文章

Fluent nHibernate - 如何在联结表上映射非键列?

具有多个非键列的继承连接策略

具有包含性列的索引

Create Index语句的Include作用

创建带包含列的索引 sqlserver

Entity Framework 4.1 - 非键列之间的关系