【问题标题】:MySQL: How do I retrieve n (number of) data from an array?MySQL:如何从数组中检索 n(数量)数据?
【发布时间】:2014-08-12 23:36:27
【问题描述】:

我有一个消息表。

+----+---------+----------+
| id | conv_id | body     |
+----+---------+----------+
|  1 |       1 |     haha |
|  2 |       1 |     blabl|
| ...|     ... |    ...   |
|  25|       2 |     hehe |
+----+---------+----------+

... = conv_id 为 2 或 1 或 3 或 n 的其余消息。

假设我有conv_id = array(2,1),并且我想在与conv_id 中的ID 数组匹配后获得10 条消息,所以我做到了

select * from `messages` where `conv_id` in (2, 1) order by `created_at` desc limit 10

上面的 sql 给了我 10 条消息匹配 conv_ids 并获得 所有组合消息。然而,这不是我想要的。相反,我希望 每个 conv_id 匹配 10 条消息。

我如何获得 10 条消息每条 conv_id 匹配?请不要使用 PHP for 循环。谢谢!

注意:数组 conv_id 可以轻松扩展以包含许多其他彼此唯一的值,而不仅仅是 2s 或 1s。


P.s.,奖励积分为 Laravel Eloquent 回答!以下是详细信息:

  1. 两个模型,Conversations 和由 Conversations 链接的 Messages hasMany Message 和 Message belongsTo a Conversation。
  2. 我上面的sql翻译自Messages::with('User')->whereIn('conv_id',$conv_id)->orderBy('created_at','desc')->take(10);

【问题讨论】:

  • 一种方法是在两个查询之间使用UNION,一个处理1s,另一个处理2s。
  • @JaredFarrish 对,等待您的答复。谢谢!
  • 如果你只使用 SQL Server 或 Oracle,一个简单的分区就可以了。
  • 这是an in-depth article 解释选项。请注意,没有一个解决方案特别优雅(都使用子查询、UNION 和/或变量),因为 SQL 中的 LIMIT 不能像这样工作。
  • 这看起来很可疑:sqlfiddle.com/#!2/aefd7a/16 每次随机获取前十个以外的东西可能会更棘手。如果它总是返回相同的 10 也是不确定的,所以如果值只能被查询一次以对每个值进行分组(可能是某种CROSS JOIN?),它可能会更一致地工作。

标签: php mysql laravel laravel-4 eloquent


【解决方案1】:

我认为 Jared 是对的,但如果您可以在表格中添加另一列,解决方案会更有效。添加一列,指示每个 conv_id 的消息编号(最早的将有 1,最新的将有会话的消息数)。之后,您可以通过使用HAVING 子句扫描表两次来实现您的目标。

SELECT * FROM messages JOIN
  (SELECT conv_id, MAX(msg_no) FROM messages WHERE conv_id IN (2,1) GROUP BY conv_id) as M 
ON messages.conv_id=M.conv_id HAVING messages.msg_no > M.msg_no-10

【讨论】:

  • 这个查询可以进一步简化吗?如果可能的话,我需要把它翻译成 Laravel 雄辩的函数。谢谢!
  • 对不起,我对 Laravel 不熟悉。但是,如果在 Laravel 中很难(或不可能?我真的不知道),我认为有时可以亲自动手并执行原始 SQL 语句。
  • 是的,这是可能的。我只是想让代码看起来更优雅。 :P
【解决方案2】:

另一种可能性。使用 group_concat - substring_index 技巧获取最后一个 conv_ids 及其第 10 个(消息)id 并重新加入消息表。

SELECT `messages`.*
FROM (
    SELECT 
    conv_id,
    substring_index(
        substring_index(
            group_concat(`messages`.id),
            ',',
            10
        ),
        ',',
        -1
    ) AS lastMessageId
    FROM `messages` 
    WHERE `conv_id` in (2, 1)
    GROUP BY `conv_id`
) AS msub
INNER JOIN `messages` ON `messages`.conv_id = msub.conv_id AND `messages`.id <= msub.lastMessageId

【讨论】:

  • 我不知道AND messages.id &lt;= msub.lastMessageId 会起作用;为什么不FIND_IN_SET(messages.id, msub.lastMessageId)?另外,顺便说一句,这将每次返回相同的 10 个条目(如果这是一个问题)。
  • 这是一个性能问题,但 group_concat 结果肯定可以与 FIND_IN_SET 一起使用。
【解决方案3】:

警告:这种方法可能是错误的。我正在尝试验证它,一旦得出结论就会更新它

昨天我刚刚从deczoGetting just the latest value on a joined table with Eloquent 的回答中学到了一些关于 Eloquent 关系的新知识。而且我认为我们也可以根据您的情况对其进行调整。

你实际上想要做的事情,我认为是:

  • 包含许多消息的对话模型。
  • 但我希望每次对话只收到 10 条最新消息。一切都迫不及待地加载了。

在 Eloquent 中,我可能会按照以下方式做一些事情:

Conversation::with('messages')->whereIn('conv_id', [1, 2])->get();

但我注意到你的问题有两点。

  • 我假设您没有另一个名为会话的表。
  • 上面的代码不限制每个对话的消息数量。

所以我决定这里应该有两个模型,一个叫做 Message,另一个叫做 Conversation。 两者都调用同一个表,但我们会告诉 Eloquent 使用不同的列作为主键。 所以为了得到一个对话模型,我在我的 newQuery 函数中添加了一个distinct(),并且只选择了conv_id,因为这是关于对话的唯一信息。

所以最后我得到了这个:

models/Message.php

class Message extends Eloquent
{
    protected $table = 'messages';
    protected $primaryKey = 'id';
    public $timestamps = false;
}

models/Conversation.php

class Conversation extends Eloquent
{
    protected $table = 'messages';
    protected $primaryKey = 'conv_id';
    public $timestamps = false;

    public function newQuery($excludeDeleted = true)
    {
        return parent::newQuery()
            ->select('conv_id')
            ->distinct();
    }

    public function messages()
    {
        return $this->hasMany('Message', 'conv_id')
            ->orderBy('id', 'desc')
            ->take(10);
    }
}

现在我可以做:

Conversation::with('messages')->whereIn('conv_id', [1, 2])->get();

这给了我与conv_id 1 和 2 的对话列表,以及他们每个人的 10 条最新消息。

这是我第一次做这样的事情。所以我进行了代码测试,它可以工作。


更新:我的答案中的代码只执行这两个查询(从 DB::getQueryLog() 检索):

select distinct `conv_id` from `messages` where `conv_id` in (?, ?);
select * from `messages` where `messages`.`conv_id` in (?, ?) order by `id` desc limit 10;

【讨论】:

  • 顺便说一句,Conversations 模型中的 newQuery 函数是做什么用的,它是如何使用的?我没有看到您在代码中使用它。谢谢!
  • 我的假设是“您没有另一个名为会话的表”。 newQuery() 是 Laravel 在开始为您构建查询时所调用的名称。所以我在它后面加上 select distinct conv_id 来模拟一个对话表。如果您已经有一个对话表,只需完全删除我的 newQuery() 部分。
  • 我很想看看原始查询是什么样的。
  • @JaredFarrish 用 DB::getQueryLog() 的结果更新了我的答案!
  • 嗯等等,这里有一股腥味。我会回来更新的。
猜你喜欢
  • 2015-12-03
  • 2022-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-09
  • 1970-01-01
  • 2016-06-02
相关资源
最近更新 更多