【问题标题】:Order By before Group By using Eloquent (Laravel)使用 Eloquent (Laravel) 在 Group By 之前排序
【发布时间】:2015-10-29 17:33:29
【问题描述】:

我有一个包含以下列的“消息”表

CREATE TABLE `messages` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `fromId` int(11) NOT NULL,
  `toId` int(11) NOT NULL,
  `message` text NOT NULL,
  `status` int(11) NOT NULL,
  `device` varchar(100) NOT NULL,
  `createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=57 DEFAULT CHARSET=latin1;

我正在尝试获取 'toId' = $id 并按 fromId 分组的所有消息。问题是结果上显示的“消息”是第一个,而不是最新的。我尝试通过 createdAt 订购,但它不起作用。

如何在查询和分组结果之前按“createdAt”排序?我想使用 Eloquent 以 laravel 的方式做到这一点。

我的查询:

$chats = Message::with('sender','recipient')
        ->where('toId',$id)
        ->orderBy('createdAt')
        ->groupBy('fromId')
        ->paginate(10)

【问题讨论】:

  • 您能否向我们展示一下您在 Eloquent 中的查询现在是什么样子的?谢谢!
  • 是的,我更新了问题。

标签: php laravel eloquent


【解决方案1】:

我只需要对消息模型做类似的事情。对我有用的是在返回的 eloquent 集合上应用 unique 方法。

Model::where('toId', $id)
    ->orderBy('createdAt', 'desc')
    ->get()
    ->unique('fromId');

查询将返回按createdAt 排序的所有消息,unique 方法会将其减少为每个fromId 的一条消息。这显然不如直接使用数据库的性能好,但就我而言,我对查询有进一步的限制。

此外,还有更多有用的方法可用于处理这些集合:https://laravel.com/docs/5.2/collections#available-methods

【讨论】:

  • 你刚刚救了我!
  • 太好了。对我很有帮助
  • 感谢。 . . .
  • 如果输出受限则无用。在这种情况下,结果计数会有所不同。
【解决方案2】:

我找到了一种方法!基本上,创建一个子查询并在之前运行它,以便结果按预期排序并在之后分组。

代码如下:

$sub = Message::orderBy('createdAt','DESC');

$chats = DB::table(DB::raw("({$sub->toSql()}) as sub"))
    ->where('toId',$id)
    ->groupBy('fromId')
    ->get();

【讨论】:

    【解决方案3】:

    试试这个查询:

    $chats = Message::with('sender','recipient')
    ->where('toId',$id)
    ->whereRaw('id IN (select MAX(id) FROM messages GROUP BY fromId)')
    ->orderBy('createdAt','desc')
    ->paginate(10)
    

    【讨论】:

      【解决方案4】:

      应该是这样的:

      Message::whereToId($id)->groupBy('fromId')->latest('createdAt')->first();
      

      更新

      看到您添加的查询后,您可能只需要向orderBy 函数添加一个方向,如下所示:

      $chats = Message::with('sender','recipient')
          ->select(DB::raw('*, max(createdAt) as createdAt'))
          ->where('toId',$id)
          ->orderBy('createdAt', 'desc')
          ->groupBy('fromId')
          ->paginate(10)
      

      【讨论】:

      • 感谢您的帮助,但是当我这样做时,消息字段会显示该组的第一条消息。我想显示最新的(按 createdAt 或 ID DESC 排序)。
      • 我根据您添加到问题中的查询再次更新了我的答案。
      • 那真的不行。 orderBy 在组之后运行,因此它将对组进行排序,而不是对组内的记录进行排序。你明白了吗?我想我需要先orderby,然后再分组,但是laravel似乎不允许我选择这样做。
      • 啊,是的,这更多地限制了 MySQL orderBy 和 groupBy 函数的工作方式。要解决此问题,您需要选择要排序的列的最大值。我已经更新了查询。希望对您有所帮助。
      • 这就是 group by 的工作方式。每个fromId 都有多个createdAts,因此按该列排序是不可靠的。代替orderBy,试试orderByRaw('max(createdAt) desc')
      【解决方案5】:

      这对我有用: (如果 createAt 随 id 增加,可以通过 createdAt 删除订单)

      DB::select(DB::raw('select * from (select * from messages group by fromId desc) m order by m.createdAt'));
      

      【讨论】:

        【解决方案6】:
         $data = MCCAddMilkEntry::where('coming_from','=','society')
                ->whereRaw('id = (select max(`id`) from mcc_add_milk_entry)')->get();
        

        【讨论】:

        • 试试这个,为我工作
        • 虽然此代码可以解决 OP 的问题,但最好包含关于您的代码如何解决 OP 问题的说明。通过这种方式,未来的访问者可以从您的帖子中学习,并将其应用到他们自己的代码中。 SO 不是编码服务,而是知识资源。此外,高质量、完整的答案更有可能得到支持。这些功能,以及所有帖子都是独立的要求,是 SO 作为一个平台的一些优势,使其与论坛区分开来。您可以编辑以添加其他信息和/或使用源文档补充您的解释。
        【解决方案7】:

        @Sergio 你的回答很好,但是如果有人想选择一个相关的模型以及......?

        Answer : 我已经测试了每一个函数,但我没有获取正确的数据。 unique() 提供带有关联数组的数据。 groupBy() 提供更好但选择第一条消息。

        最新收件箱消息的解决方案。我的情况:

        $data = ChatMessage::where('incoming_msg_id', auth()->user()->id)->orWhere('outgoing_msg_id', auth()->user()->id)->latest('msg_id')->with('messageable')->get();
            $inbox = [];
            $itemIds = [];
            foreach ($data as $key => $value) {
                if (!in_array($value['messageable_id'].$value['msg_for'], $itemIds)) {
                    array_push($inbox,$value);
                    array_push($itemIds,$value['messageable_id'].$value['msg_for']);
                }
            }
            return $this->sendResponse($inbox,'Inbox');
        

        你的情况:

        $chats = Message::where('toId',$id)->orWhere('fromId', $id)->latest('id')->with('sender','recipient')->get();
            $inbox = [];
            $fromIds = [];
            foreach ($chats as $key => $value) {
                if (!in_array($value['fromId'], $fromIds)) {
                    array_push($inbox,$value);
                    array_push($fromIds,$value['fromId']);
                }
            }
            return $this->sendResponse($inbox,'Inbox');
        

        【讨论】:

          猜你喜欢
          • 2011-11-18
          • 2022-01-10
          • 1970-01-01
          • 2019-09-02
          • 2017-07-31
          • 2018-06-27
          • 1970-01-01
          • 2018-03-29
          • 1970-01-01
          相关资源
          最近更新 更多