【问题标题】:Slow MySQL with join query written in Laravel/Eloquent for Yajra DataTables用 Laravel/Eloquent 为 Yajra DataTables 编写连接查询的慢 MySQL
【发布时间】:2021-06-20 06:06:53
【问题描述】:

我试图通过加入数据从两个表中提取数据,但是执行的 sql 查询非常慢。想法是提取所有users,然后与points 表中的created_at 日期结合。拉出所有用户或所有点相当快,但在编写正确的连接 sql 时会遇到问题。我确实尝试将索引添加到适当的列(例如,points.created_at),但那些使查询变得更慢。

这是生成查询的代码:

return $this->user
            ->query()
            ->select(['users.id', 'users.email', 'users.role', 'users.created_at', 'users.updated_at', 'pt.created_at AS last_transaction'])
            ->leftJoin(DB::raw('(SELECT points.user_id, points.created_at FROM points ORDER BY points.created_at DESC) AS pt'), 'pt.user_id', '=', 'users.id')
            ->where('users.role', 'consument')
            ->groupBy('users.id');

生成此查询:

select `users`.`id`, `users`.`email`, `users`.`role`, `users`.`created_at`, `users`.`updated_at`, `pt`.`created_at` as `last_transaction`
from `users`
left join (SELECT points.user_id, points.created_at FROM points ORDER BY points.created_at DESC) AS pt on `pt`.`user_id` = `users`.`id`
where `users`.`role` = ? and `users`.`deleted_at` is null
group by `users`.`id`
order by `id` asc

用户表:

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `remember_token` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `role` varchar(15) COLLATE utf8_unicode_ci DEFAULT 'consument',
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `updated_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `deleted_at` timestamp NULL DEFAULT NULL,
  `email_verified_at` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `email_verify_token` text COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=84345 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

积分表:

CREATE TABLE `points` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned NOT NULL,
  `tablet_id` int(10) unsigned DEFAULT NULL,
  `parent_company` int(10) unsigned NOT NULL,
  `company_id` int(10) unsigned NOT NULL,
  `points` int(10) unsigned NOT NULL,
  `mutation_type` tinyint(3) unsigned NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `updated_at` timestamp NOT NULL DEFAULT current_timestamp(),
  PRIMARY KEY (`id`),
  KEY `points_user_id_foreign` (`user_id`),
  KEY `points_company_id_foreign` (`company_id`),
  KEY `points_parent_company_index` (`parent_company`),
  KEY `points_tablet_id_index` (`tablet_id`),
  KEY `points_mutation_type_company_id_created_at_index` (`mutation_type`,`company_id`,`created_at`),
  KEY `created_at_user_id` (`created_at`,`user_id`),
  CONSTRAINT `points_company_id_foreign` FOREIGN KEY (`company_id`) REFERENCES `companies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `points_parent_company_foreign` FOREIGN KEY (`parent_company`) REFERENCES `parent_company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `points_tablet_id_foreign` FOREIGN KEY (`tablet_id`) REFERENCES `tablets` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT `points_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1798627 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

用户有 84,263 行,点数有 1,636,119 行。如果我通过 phpMyAdmin 手动执行查询,执行大约需要 150 秒。如果我通过 Laravel 运行它,页面会在 180 秒后超时。

我可以添加或删除索引并更改 sql 查询,但我无法更改数据库结构,因此非常感谢任何有关优化 sql 查询的帮助。

【问题讨论】:

    标签: mysql eloquent laravel-6 yajra-datatable


    【解决方案1】:

    如果积分表中每个用户只有一行,请执行以下操作:

    如果有用户没有帖子,您可以使用:

    select `users`.`id`, `users`.`email`, `users`.`role`, `users`.`created_at`, 
    `users`.`updated_at`, `pt`.`created_at` as `last_transaction`
    from `users`
    left join points AS pt on `pt`.`user_id` = `users`.`id`
    where `users`.`role` = ? and `users`.`deleted_at` is null
    order by `id` ASC
    

    如果用户表中的每个用户在积分表中始终有一个帖子,您可以跳过左连接,只需:

    select `users`.`id`, `users`.`email`, `users`.`role`, `users`.`created_at`, 
    `users`.`updated_at`, `pt`.`created_at` as `last_transaction`
    from `users`
    join points AS pt on `pt`.`user_id` = `users`.`id`
    where `users`.`role` = ? and `users`.`deleted_at` is null
    order by `id` asc
    

    【讨论】:

    • 我明白我不够清楚,对此感到抱歉。每个用户在points 表中可以有多行,这就是为什么有这么多行。我需要在每个用户的points 表中获取最新的进入日期,同时列出所有用户。
    • 好的。我会做出新的回答。
    【解决方案2】:

    这将仅返回一个用户和基于点列“created_at”的最新点行。

    SELECT  `u`.`id`, 
            `u`.`email`, 
            `u`.`role`, 
            `u`.`created_at`,
            `u`.`updated_at`, 
            `pt`.`created_at` as `last_transaction`
    from `users` u
    LEFT join points AS pt on `pt`.`user_id` = `u`.`id`
    LEFT JOIN (
        SELECT user_id, MAX(created_at) AS mm FROM points GROUP BY user_id
    ) AS m ON m.user_id = pt.user_id
    where `u`.`role` = ? and `u`.`deleted_at` is NULL AND m.mm = pt.created_at
    order by `id` ASC;
    

    【讨论】:

      猜你喜欢
      • 2019-03-02
      • 2015-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-21
      • 1970-01-01
      • 2020-01-23
      • 1970-01-01
      相关资源
      最近更新 更多