如何在 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 by
与 distinct
不同。 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');
对 php 的 in_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 中获取非键列字段的不同值?的主要内容,如果未能解决你的问题,请参考以下文章