为什么 MySQL 在 JOIN 加 ORDER 时不使用主键?

2024-01-02

这是给你的一个简洁的(显然是 MySQL):



# Setting things up
DROP DATABASE IF EXISTS index_test_gutza;
CREATE DATABASE index_test_gutza;
USE index_test_gutza;

CREATE TABLE customer_order (
    id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
    invoice MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
    PRIMARY KEY(id)
);
INSERT INTO customer_order
    (id, invoice)
    VALUES
    (1, 1),
    (2, 2),
    (3, 3),
    (4, 4),
    (5, 5);

CREATE TABLE customer_invoice (
    id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
    invoice_no MEDIUMINT UNSIGNED DEFAULT NULL,
    invoice_pdf LONGBLOB,
    PRIMARY KEY(id)
);
INSERT INTO customer_invoice
    (id, invoice_no)
    VALUES
    (1, 1),
    (2, 2),
    (3, 3),
    (4, 4),
    (5, 5);

# Ok, here's the beef
EXPLAIN
    SELECT co.id
    FROM customer_order AS co;

EXPLAIN
    SELECT co.id
    FROM customer_order AS co
    ORDER BY co.id;

EXPLAIN
    SELECT co.id, ci.invoice_no
    FROM customer_order AS co
    LEFT JOIN customer_invoice AS ci ON ci.id=co.invoice;

EXPLAIN
    SELECT co.id, ci.invoice_no
    FROM customer_order AS co
    LEFT JOIN customer_invoice AS ci ON ci.id=co.invoice
    ORDER BY co.id;
  

底部有四个 EXPLAIN 语句。前两个结果正是您所期望的:



+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | co    | index | NULL          | PRIMARY | 3       | NULL |    5 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
  

第三个已经很有趣了——注意 customer_order 中的主键如何不再被使用:



+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                         | rows | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+------+-------------+
|  1 | SIMPLE      | co    | ALL    | NULL          | NULL    | NULL    | NULL                        |    5 |             |
|  1 | SIMPLE      | ci    | eq_ref | PRIMARY       | PRIMARY | 3       | index_test_gutza.co.invoice |    1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+------+-------------+
  

然而,第四个是最有趣的——只需添加 ORDER BY在主键上导致对 customer_order 进行文件排序(这是预料之中的,因为上面已经令人困惑了):



+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+------+----------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                         | rows | Extra          |
+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+------+----------------+
|  1 | SIMPLE      | co    | ALL    | NULL          | NULL    | NULL    | NULL                        |    5 | Using filesort |
|  1 | SIMPLE      | ci    | eq_ref | PRIMARY       | PRIMARY | 3       | index_test_gutza.co.invoice |    1 | Using index    |
+----+-------------+-------+--------+---------------+---------+---------+-----------------------------+------+----------------+
  

文件排序!虽然我从来不使用除了 customer_order 表中的主键进行排序和 customer_invoice 表中的 JOIN 主键之外的任何内容。那么,以一切美好和正确的名义,为什么突然切换到文件排序?!更重要的是,我该如何避免这种情况?作为记录,我很乐意接受一个记录的答案,解释为什么会这样cannot避免(如果是这样的话。)

正如您现在可能怀疑的那样,这实际上发生在生产中,尽管表并不大(只有数百条记录),但当我运行时,发票表(包含 PDF 文件)上的文件排序正在杀死服务器类似于上面的查询(我需要这些查询才能知道哪些订单已开具发票,哪些没有)。

在你问之前,我设计了数据库,并且我认为我可以安全地将 PDF 文件存储在该表中,因为我永远不能需要对其进行任何搜索查询——我手边总是有它的主键!

更新(评论概要)

以下是下面评论中建议内容的概要,因此您不必阅读所有内容:

  • *您应该在 customer_order.invoice 上添加一个密钥* - 我实际上在生产中尝试过,它没有什么区别(因为它不应该)
  • 你应该使用USE INDEX——试过了,没用。我也尝试过FORCE INDEX-- 也没有结果(没有任何改变)
  • 您过于简化了用例,我们需要实际的生产查询-- 我可能在第一次迭代中剥离了太多,所以我更新了它(我刚刚添加了, ci.invoice_no in the SELECT对于最后几个查询)。作为记录,如果有人真的很好奇,这里是生产查询,完全按照原样(这会检索订单的最后一页):


SELECT
    corder.id,
    corder.public_id,
    CONCAT(buyer.fname," ",buyer.lname) AS buyer_name,
    corder.status,
    corder.payment,
    corder.reserved AS R,
    corder.tracking_id!="" as A,
    corder.payment_received as pay_date,
    invoice.invoice_no AS inv,
    invoice.receipt_no AS rec,
    invoice.public AS pub_inv,
    proforma.proforma_no AS prof,
    proforma.public AS pub_pf,
    corder.rating,
    corder.rating_comments!="" AS got_comment
FROM
    corder
LEFT JOIN user as buyer ON buyer.id=corder.buyer
LEFT JOIN invoice as invoice ON invoice.id=corder.invoice
LEFT JOIN invoice as proforma ON proforma.id=corder.proforma
ORDER BY
    id DESC 
LIMIT 400, 20;
  

上面的查询(同样,这正是我在生产中运行的查询)大约需要 14 秒才能运行。这是在生产中执行的简化查询,如上面的用例所示:



SELECT
    corder.id,
    invoice.invoice_no
FROM
    corder
LEFT JOIN invoice ON invoice.id=corder.invoice
ORDER BY
    corder.id DESC 
LIMIT 400, 20;
  

这个运行需要 13 秒。请注意,LIMIT 没有任何区别只要我们谈论最后一页结果(我们就是)。也就是说,当涉及文件排序时,检索最后 12 个结果或所有 412 个结果之间绝对没有显着差异。

结论

ypercube 的答案不仅是正确的,而且不幸的是它似乎是唯一合法的答案。我尝试进一步将条件与字段分开,因为SELECT * FROM corder如果 corder 本身包含 LONGBLOB(并且在子查询中复制主查询中的字段是不优雅的),则子查询最终可能会涉及大量数据,但不幸的是它似乎不起作用:



SELECT
    corder.id,
    corder.public_id,
    CONCAT(buyer.fname," ",buyer.lname) AS buyer_name,
    corder.status,
    corder.payment,
    corder.reserved AS R,
    corder.tracking_id != "" AS A,
    corder.payment_received AS pay_date,
    invoice.invoice_no AS inv,
    invoice.receipt_no AS rec,
    invoice.public AS pub_inv,
    proforma.proforma_no AS prof,
    proforma.public AS pub_pf,
    corder.rating,
    corder.rating_comments!="" AS got_comment
FROM
    corder
LEFT JOIN user as buyer ON buyer.id = corder.buyer
LEFT JOIN invoice AS invoice ON invoice.id = corder.invoice
LEFT JOIN invoice AS proforma ON proforma.id = corder.proforma
WHERE corder.id IN (
    SELECT id
    FROM corder
    ORDER BY id DESC
    LIMIT 400,20
)
ORDER BY
    corder.id DESC;
  

此操作失败,并显示以下错误消息:



ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
  

我使用的是 MySQL 5.1.61,它是 5.1 系列中最新的版本(显然 5.5.x 也不支持它)。


你能尝试这个版本吗(它基本上首先获取了 420 行)corder表,保留其中的 20 个,然后执行 3 个外连接):

SELECT
    corder.id,
    corder.public_id,
    CONCAT(buyer.fname," ",buyer.lname) AS buyer_name,
    corder.status,
    corder.payment,
    corder.reserved AS R,
    corder.tracking_id != "" AS A,
    corder.payment_received AS pay_date,
    invoice.invoice_no AS inv,
    invoice.receipt_no AS rec,
    invoice.public AS pub_inv,
    proforma.proforma_no AS prof,
    proforma.public AS pub_pf,
    corder.rating,
    corder.rating_comments!="" AS got_comment
FROM
    ( SELECT * 
      FROM corder
      ORDER BY
        id DESC 
      LIMIT 400, 20
    )
    AS corder
LEFT JOIN user as buyer ON buyer.id = corder.buyer
LEFT JOIN invoice AS invoice ON invoice.id = corder.invoice
LEFT JOIN invoice AS proforma ON proforma.id = corder.proforma
ORDER BY
    corder.id DESC ;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 MySQL 在 JOIN 加 ORDER 时不使用主键? 的相关文章

  • 高级 MySQL:查找民意调查响应之间的相关性

    我有四个 MySQL 表 users 身份证号 姓名 polls ID 文本 options id poll id 文本 回应 id poll id 选项 id 用户 id 给定一个特定的民意调查和一个特定的选项 我想生成一个表格 显示其他
  • 使用 JdbcTemplate 进行动态查询

    我有一个关于使用 JdbcTemplate 进行动态查询的问题 我的代码如下 String insertQueries INSERT INTO tablename StringJoiner joiner new StringJoiner S
  • 如何从准备好的语句中获取标量结果?

    是否可以将准备好的语句的结果设置为变量 我正在尝试创建以下存储过程 但失败了 第 31 行出现错误 1064 42000 您的 SQL 语法有错误 检查与您的 MySQL 服务器版本相对应的手册 了解在 stmt USING m c a 附
  • MySQL 多索引与多列索引进行搜索

    在我正在编写的软件中 它能够搜索给定的表以获取信息 搜索表单有 5 个字段 当然所有字段都对应于表中的不同列 但所有字段都是可选的 我的问题是关于多列索引是否有效以及为其构建查询的正确方法 如果我有一个跨 5 列的索引 并且我构建了一个查询
  • 两个表之间可以有两种关系吗?

    有两个表 EMPLOYER 和 EMPLOYEE 由于每个 EMPLOYEE 都被分配给一个 EMPLOYER 因此他们之间存在 1 N 关系 简单的事情 但我也希望能够模拟一种情况 每个雇主都可以选择他的one最喜欢的员工 他最好什么也不
  • InnoDB vs. MyISAM 插入查询时间

    我有一个大型 MySQL 表 约 1000 万行 6 5G 用于读取和写入 它是MyISAM 由于MyISAM 的所有表写入锁 我获得了很多锁 我决定尝试迁移到 InnoDB 推荐用于读 写表 它只锁定写入时的特定行 转换后 我测试了插入语
  • GROUP_CONCAT 逗号分隔符 - MySQL

    我有一个疑问 我在哪里使用GROUP CONCAT和自定义分隔符 因为我的结果可能包含逗号 这一切都运行良好 但它仍然以逗号分隔 所以我的输出是 Result A Result B Result C 我怎样才能做到这一点 输出是 Resul
  • MySQL - 如何按相关性排序? INNODB表

    我在一个名为 cards 的 INNODB 表中有大约 20 000 行 所以 FULLTEXT 不是一个选项 请考虑这张表 id name description 1 John Smith Just some dude 2 Ted Joh
  • 使用存储过程并发访问MySQL数据库

    我有一个存储过程 它将读取然后增加数据库中的值 许多程序同时使用这个特定的过程 我担心并发问题 特别是读写器问题 有人可以建议我任何可能的解决方案吗 thanks 首先 正如另一篇文章中所述 使用 InnoDB 从 MySQL 5 5 开始
  • SQL Server到Mysql迁移(使用Mysql Workbench)数据传输错误

    我正在使用 Mysql Work bench 6 3 将数据库从 MS Sql server 2008 迁移到 Mysql 在 批量数据传输 期间出错并出现以下警告 这种情况仅发生在像 varchar char 这样的列类型上 当我尝试使用
  • 用于分页的php示例脚本[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 任何人都可以建议一个好的分页 php 脚本 其中人们想要分页显示数据库中的大量项目 以下链接可以帮助您
  • MySQL - 此版本的 MySQL 尚不支持“LIMIT 和 IN/ALL/ANY/SOME 子查询”

    这是php编码我正在使用的 Last Video db gt fetch all SELECT VID thumb FROM video WHERE VID IN SELECT VID FROM video WHERE title LIKE
  • 工厂模式数据库连接

    我正在尝试使用 MySQL 实现数据库连接上的工厂模式 SQL Server 面临奇怪的错误 你调用的对象是空的 在 SQL 命令对象上 internal class SqlServerDB IDatabase private SqlCon
  • mysql变量赋值:如何强制赋值顺序?

    由于mysql是一种声明性语言 我找不到强制赋值变量顺序的方法 采取这个查询 SET v1 0 SET v2 0 SELECT v1 v2 FROM MyTable table WHERE v1 v2 is not null AND v2
  • 一次将多个值插入MySQL [重复]

    这个问题在这里已经有答案了 谁能解释一下为什么这个 PHP MySQL 不起作用 基本上我需要从表单中一次插入大量行 因此会有多个名称字段 多个短 中 长字段等 我收到此错误 Notice Undefined variable Short1
  • WHERE 子句或 ON 子句中的 INNER JOIN 条件?

    我今天输错了一个查询 但它仍然有效并给出了预期的结果 我的意思是运行这个查询 SELECT e id FROM employees e JOIN users u ON u email e email WHERE u id 139840 但我
  • ON DUPLICATE KEY UPDATE 的自动增量过多

    我有一个包含列的基本表 id 主要是AI 名称 唯一 etc 如果唯一列不存在 则插入该行 否则更新该行 INSERT INTO pages name etc VALUES bob randomness ON DUPLICATE KEY U
  • 在无文本搜索查询中使用 sphinx 与 MySQL

    我有这样的疑问 假设我有一个大表 与一个较小的用户表有关系 这个想法是在那个真正的大表中搜索大于给定日期的日期并按分数 例如大整数 排序 并同时获取相关的用户信息 此查询的结果大约每 10 分钟就会更改一次 所以 没有文本搜索 但我有一个非
  • 左连接 SQL 求和

    我有两张桌子想要加入 比如说表 a 和表 b 表 b 有许多行指向表 a 表 b 包含价格 实际上是一个购物篮 所以我想要的是表a中的所有记录和表b中的价格之和 我努力了 select a sum b ach sell from booki
  • 无法在 Centos 上安装 php-mysqli 扩展

    我正在尝试将 mysqli 扩展安装到 php yum install php mysqli 我收到下一个错误 Transaction Check Error file usr share mysql charsets Index xml

随机推荐

  • 使用 VBScript 遍历 XML 文件中的所有节点

    我编写了一个 VBScript 它应该遍历 XML 文件中的所有节点 无论树的深度如何 这一点做得很好except不显示 2 级或以上深度的节点的节点名称 我需要节点名称和值 以便获得名称 值对以供其他程序进一步处理 任何人都可以帮我显示丢
  • 使用 Hibernate JPA (JPQL) 进行非多态查询

    我发布这个问题 答案作为扩展如何在 Hibernate 中执行非多态 HQL 查询 https stackoverflow com questions 2093025 how to perform a non polymorphic hql
  • 条件 Java 编译

    我是一名资深 C 程序员 刚接触 Java 我正在 Eclipse 中开发 Java Blackberry 项目 问题 有没有办法在项目中引入不同的配置集 然后根据这些配置集编译略有不同的代码 在Visual Studio中 我们有项目配置
  • 通过 FIFO 重定向 stdin

    我正在 GNU Linux 下运行一个服务器应用程序 用 Java 编写 它接收输入 我猜是来自标准输入 并解释它以运行一些命令 我不想在终端窗口内运行应用程序 我想运行守护程序 但我仍然希望能够随时输入命令 我想我也许可以使用 fifos
  • 使用相同的标记和偏移位访问和更新 2 路关联缓存

    我对如何在双向关联缓存上访问数据感到困惑 例如 C ABS C 32KB A 2 B 32bits S 256 offset lg B 5 index lg S 8 tag 32 offset index 19 假设我有以下地址 tag i
  • 如何根据关系获取一条记录的多条记录?

    我有两个表组织和员工具有一对多关系 即一个组织可以有多名员工 现在我想选择特定组织的所有信息以及该组织所有员工的名字 最好的方法是什么 我可以在单个记录集中获取所有这些内容吗 或者我将不得不根据否获取多行 员工人数 这是我想要的一些图形演示
  • 使用 24 小时制数据创建圆形图的方法是什么?

    我正在尝试使用围绕中心点绘制的一组数据的方式创建一个圆形图 我在网上找到的代码可以做到这一点 但是 Y 轴太大 以至于图形没有用 我想将 Y 轴限制为 95 120 但当我使用Y scale continuous limit c 95 12
  • 在 Ember.js 应用程序中显示在线和离线(例如飞机)模式

    Ember 应用程序可以了解网络状态吗 如果是 如果应用程序可以访问互联网 我如何获取信息 我想根据网络可访问性切换 GUI 元素 索引 html app js App Ember Application create
  • ASP.NET Core 流式传输 - 将块写入请求

    这是更新的问题 我的代码中曾经存在错误 我希望能够将数据块发送给客户端 任何事情都会受到赞赏 有没有办法为 ASP NET Core 提供对数据流传输方式的更多控制 我担心下面的代码如何扩展 有人可以建议如何通过 asp net core
  • 内部测试人员如何下载“Android”预发布的App版本?

    有人可以告诉我 内部测试人员如何下载 Android 预发布的应用程序版本吗 因为现在 TestFlight 仅适用于苹果收购的iOS 解决方案是否是将 Android 预发布应用程序上传到 Google Play 并邀请内部测试人员下载
  • 尝试使用 jQuery 模式弹出窗口通过 iframe 查看 pdf 文件

    我正在尝试使用 jQuery 模式弹出窗口通过 iframe 显示 pdf 文件 但是 它要求用户在页面加载时下载该文件 而不是在模式对话框打开时将 pdf 文件嵌入到 iframe 中 我该如何解决这个问题 我使用 MVC 输出 pdf
  • Android System.err 突然出现

    运行我的应用程序时 LogCat 突然显示一系列橙色消息 警告 这些消息似乎与我的应用程序完全无关 当然not具有相同的 pid 05 01 12 00 03 732 WARN System err 1836 java io FileNot
  • 在R中读取csv文件,其中货币列为数字

    我正在尝试读入 R 一个包含政治捐款信息的 csv 文件 据我了解 默认情况下 列会作为因子导入 但我需要将金额列 数据集中的 CTRIB AMT 作为数字列导入 这样我就可以运行各种不适用于的函数因素 该列的格式为带有 作为前缀的货币 我
  • 使用反引号时转义空格

    我进行了搜索 从我的角度来看 使用反引号是解决这个问题的唯一方法 我正在尝试致电mdlsPerl 命令为目录中的每个文件查找其上次访问时间 我遇到的问题是在我的文件名中find我有 bash 显然不喜欢的未转义空间 有没有一种简单的方法可以
  • 警告:mysql_real_escape_string() 期望参数 1 为字符串

    HTML代码 tr td td tr
  • Swift - 设置圆角以注释视图图像

    这是我之前的线程 我将图像设置为注释视图引脚 Swift 设置从数组到注释引脚的不同图像 https stackoverflow com questions 28033159 swift setting different images f
  • 使用 TorchText (PyTorch) 进行语言翻译

    我最近开始使用 PyTorch 进行 ML DL 下面的 pytorch 示例解释了我们如何训练一个简单的模型来将德语翻译成英语 https pytorch org tutorials beginner torchtext translat
  • 如何从 Access 数据库显示/检索或获取图像到 PictureBox?

    Private Sub UpdatePicture Dim str As String str Provider Microsoft ACE OLEDB 12 0 Data Source UsersDB accdb cn New OleDb
  • VBA XML 选择命名空间问题

    所以我确信这是可以解决的 但只是我通常不需要在 VBA XML 代码中处理 XML 命名空间 所以 我们有一个文件 它实际上是一个 SVG 文件 名为Flag of the United Kingdom svg这是文件内容
  • 为什么 MySQL 在 JOIN 加 ORDER 时不使用主键?

    这是给你的一个简洁的 显然是 MySQL Setting things up DROP DATABASE IF EXISTS index test gutza CREATE DATABASE index test gutza USE ind