【问题标题】:MySQL/MariaDB/SQLite: DISTINCT CONCATMySQL/MariaDB/SQLite:DISTINCT CONCAT
【发布时间】:2018-09-04 11:43:49
【问题描述】:

这个问题不一定只是与 Laravel 相关,但我正在尝试获取记录,这些记录因连接字段而异。出于测试目的,我需要它与 MySQL/MariaDBSQLite 一起使用。

在进行研究时,我发现 SQLite 没有 CONCAT 函数 - 相反,您使用 || 运算符来连接项目。另一方面,MySQL 不会以相同的方式解释 ||,但我总是可以使用条件语句来涵盖这两种情况。

但是,我仍然无法获得我想要的记录 - 我的表格包括:

| id | tagable_id | tagable_type       | name | title | description | url               | image                | hits |
| 1  | 1          | App\Models\Article | a..  | A..   | A.. descr.. | https://localhost | https://localhost... | 0    |
| 2  | 1          | App\Models\Article | b..  | B..   | B.. descr.. | https://localhost | https://localhost... | 2    |
| 3  | 1          | App\Models\Article | c..  | C..   | C.. descr.. | https://localhost | https://localhost... | 3    |
| 4  | 1          | App\Models\Page    | a..  | A..   | C.. descr.. | https://localhost | https://localhost... | 0    |

我只需要获得 4 条按命中次数按 ASC 排序且使用 CONCAT(table_id, tagable_type) 唯一的记录。

在这种情况下,语句应该返回 id 为 14 的记录 - 因为 23 具有相同的 tagable_idtagable_type 与 id 为 1 的记录,命中次数最少 - 实际上只返回 2 条记录:

| id | tagable_id | tagable_type       | name | title | description | url               | image                | hits |
| 1  | 1          | App\Models\Article | a..  | A..   | A.. descr.. | https://localhost | https://localhost... | 0    |    
| 4  | 1          | App\Models\Page    | a..  | A..   | C.. descr.. | https://localhost | https://localhost... | 0    |

我已经试过了:

DB::table('tags')
->selectRaw("DISTINCT CONCAT(`tagable_id`, '-', `tagable_type`), `id`, `name`, `title`, `description`, `url`, `image`")
->whereIn('name', $tags->toArray())
->orderBy('hits');

然而,这不会返回不同的记录 - 它会返回所有记录而不管不同的连接 - 在 MySQL / MariaDB 中 - 在 SQLite 中它会告诉我no such function: CONCAT

我也试过了:

DB::table('tags')
->selectRaw("CONCAT(`tagable_id`, '-', `tagable_type`) as `identifier`, `id`, `name`, `title`, `description`, `url`, `image`")
->whereIn('name', $tags->toArray())
->groupBy('identifier')
->orderBy('hits');

这一次 MySQL/MariaDB 告诉我,我还需要在组中包含其他字段 tags.id' isn't in GROUP BY,但是当我将它与 SQLite 一起使用并用 (tagable_id || '-' || tagable_type) as identifier 替换 CONCAT 函数时 - 它似乎有效。

所以在这个阶段我是:MySQL: 0 | SQLite: 1

任何帮助将不胜感激。

更新 经过数小时的尝试解决后,我决定添加一个新列 identifier 到表中以克服不可用的concat 函数的问题 - 我的代码现在看起来像这样:

Tag::with('tagable')->whereIn('id', function($query) use ($tags) {
    $query->selectRaw('min(`id`) from `tags`')
        ->whereIn('name', $tags->toArray())
        ->groupBy('identifier');
})
->orderBy('hits')
->take(4)
->get();

这仍然不是我所追求的,因为它依赖于给定标识符的最低 id min(id) 并且如果具有相同标识符的最低 id 的记录具有更高的命中数,则其兄弟姐妹然后兄弟姐妹不会被退回。

【问题讨论】:

  • 更新您的问题添加适当的数据样本和预期结果
  • 刚刚添加了示例数据和应返回内容的说明。

标签: mysql laravel laravel-5 sqlite


【解决方案1】:

您可以使用DISTINCT 获得不同的组合,但不能获得整行:

DB::table('tags')->distinct()->get(['tagable_id', 'tagable_type']);

您必须为此使用更复杂的查询:

$join = DB::table('tags')
    ->select('tagable_id', 'tagable_type')->selectRaw('MIN(hits) as hits')
    ->whereIn('name', $tags->toArray())
    ->groupBy('tagable_id', 'tagable_type');
$sql = '(' . $join->toSql() . ') as grouped';
$tags = DB::table('tags')
    ->join(DB::raw($sql), function($join) {
        $join->on('tags.tagable_id', '=', 'grouped.tagable_id')
            ->on('tags.tagable_type', '=', 'grouped.tagable_type')
            ->on('tags.hits', '=', 'grouped.hits');
    })->mergeBindings($join)
    ->whereIn('name', $tags->toArray())
    ->get();

保证每个唯一组合一条记录的解决方案:

$join = DB::table('tags')
    ->select('tagable_id', 'tagable_type')->selectRaw('MIN(hits) as hits')
    ->whereIn('name', $tags->toArray())
    ->groupBy('tagable_id', 'tagable_type');
$sql = '(' . $join->toSql() . ') as grouped';
$ids = DB::table('tags')
    ->selectRaw('MIN(id) as id')
    ->join(DB::raw($sql), function($join) {
        $join->on('tags.tagable_id', '=', 'grouped.tagable_id')
            ->on('tags.tagable_type', '=', 'grouped.tagable_type')
            ->on('tags.hits', '=', 'grouped.hits');
    })->mergeBindings($join)
    ->whereIn('name', $tags->toArray())
    ->groupBy('tags.tagable_id', 'tags.tagable_type')
    ->pluck('id');
$tags = DB::table('tags')->whereIn('id', $ids)->get();

【讨论】:

  • 谢谢 - 在相同的 tagable_typetagable_id 的记录具有相同的点击次数(即 0)的情况下,这将如何表现?我相信它会返回相同tagable 类型的多条记录,因为join 通过这3 个字段连接,这3 个字段可能对多条记录具有相同的值。
  • 是的,在这种情况下它将返回多条记录。是否需要查询只返回一条记录?
  • 否 - 我需要使用 take 方法指定的记录数,但每条记录需要表示 tagable_idtagable_type 的唯一组合。我现在什至添加了一个额外的列来存储表示这两个字段连接的值 - 以消除 SQLite 不支持 concat 的问题,但我仍然无法弄清楚如何通过这个连接字段获取唯一记录。跨度>
  • 我在我的答案中添加了一个解决方案,当多条记录具有相同的命中数时,通过使用最低 id 来保证每个唯一组合一个记录。
  • 感谢@Jonas - 感谢您的宝贵时间。看起来它需要 3 个数据库查询,我宁愿避免。我整天都在玩它,并想出了一个单一的查询解决方案,它只能部分解决问题 - 所以不是我想要的 - 我已经更新了问题。
猜你喜欢
  • 2021-11-24
  • 2019-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-13
  • 2016-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多