MySQL 查询优化 - 不使用主键?

标签 mysql sql query-optimization

我有一个使用三个表的查询。这个查询是一个更大的查询的一部分,这个查询由于这个看似简单的查询导致对 Patients 表的全表扫描而降低了性能。

查询的目的是能够查看患者列表,其中包含医生姓名、治疗和给定日期的收费金额。

我已经在 Transactions 中为 PatientID 创建了索引,在 Patients 中为 DoctorID 创建了索引,但是 MySQL 坚持对患者进行全表扫描。

患者表(13,000 行)

CREATE TABLE `Patients` (
  `ID` int(10) NOT NULL,
  `DoctorID` int(10) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  KEY `DoctorID_Index` (`DoctorID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

医生表(42行)

CREATE TABLE `Doctors` (
  `ID` int(10) NOT NULL,
  `DoctorName` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

交易表(~500,000 行)

CREATE TABLE `Transactions` (
  `Description` text,
  `TransactionDate` datetime DEFAULT NULL,
  `Amount` decimal(19,4) DEFAULT NULL,
  `PatientID` int(10) DEFAULT NULL,
  `ID` int(10) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `PatientID_Index` (`PatientID`),
  KEY `TransactionDate_Index` (`TransactionDate`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

此查询执行一天大约需要 1.5 秒。这里会发生什么?它没有使用患者的主键索引?如何进一步优化此查询?

EXPLAIN SELECT P.ID, D.DoctorName, T.Description, T.Amount
FROM
    `Doctors` AS D
    INNER JOIN
        `Patients` AS P
        ON
            D.ID = P.DoctorID
    INNER JOIN
        `Transactions` AS T
        ON
            P.ID = T.PatientID
WHERE Date(T.TransactionDate) IN ('2017-03-30')

[
    {
        "id" : 1,
        "select_type" : "SIMPLE",
        "table" : "P",
        "partitions" : null,
        "type" : "ALL",
        "possible_keys" : "PRIMARY",
        "key" : null,
        "key_len" : null,
        "ref" : null,
        "rows" : 13748,
        "filtered" : 100.00,
        "Extra" : "Using where"
    },
    {
        "id" : 1,
        "select_type" : "SIMPLE",
        "table" : "D",
        "partitions" : null,
        "type" : "eq_ref",
        "possible_keys" : "PRIMARY",
        "key" : "PRIMARY",
        "key_len" : "4",
        "ref" : "P.DoctorID",
        "rows" : 1,
        "filtered" : 100.00,
        "Extra" : null
    },
    {
        "id" : 1,
        "select_type" : "SIMPLE",
        "table" : "T",
        "partitions" : null,
        "type" : "ref",
        "possible_keys" : "PatientID_Index",
        "key" : "PatientID_Index",
        "key_len" : "5",
        "ref" : "P.ID",
        "rows" : 34,
        "filtered" : 100.00,
        "Extra" : "Using where"
    }
]

最佳答案

我将从这样做开始:

<强>1。创建外键。

您需要医生与患者之间以及患者与交易之间的 FK。 MySQL(与其他数据库不同)会自动创建必要的索引并加快查询速度。

alter table `Patients` add (
  constraint fk_patient_doctor foreign key (`DoctorId`) 
    references `Doctors` (`ID`)
);

alter table `Transactions` add (
  constraint fk_tx_patient foreign key (`PatientID`) 
    references `Patients` (`ID`)
);

<强>2。根据日期创建交易索引。

create index ix_tx_date on `Transactions` (`TransactionDate`);

这将加快按日期搜索的速度,希望使用范围扫描而不是全表扫描。

<强>3。修复您的查询。

正如@UUeerdo 所说,而不是:

Date(T.TransactionDate) IN ('2017-03-30')

尝试:

T.TransactionDate BETWEEN '2017-03-30 00:00:00' AND '2017-03-30 23:59:59'

<强>4。更新 MySQL 统计数据。

analyze table `Transactions`;
analyze table `Patients`;
analyze table `Orders`;

关于MySQL 查询优化 - 不使用主键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49883213/

相关文章:

mysql - 两个 SQL 查询的组合

mysql - MySQL 中 CASE 语句中的 REGEXP

mysql - Zen Cart 对数据库的写入过多。可能的解决方案?

Mysql LEAST() 大于0

PHP PDO 防止数据重叠

mysql - 使用 ABS() 并应用带有 "AS"的标签

sql - RAISERROR 引发替换参数错误

query-optimization - 如何阻止 mysqldump 记录为慢速查询

mysql - 在具体化子查询上指定外部查询的条件

php - 确定经纬圆和球体上的圆是否重叠