MYSQL:即使索引被正确添加,仍然使用临时的文件

问题描述 投票:1回答:1

我有这个查询,我认为我已正确索引它们。但仍然得到filesort和临时索引。

查询如下:

SELECT * FROM
    (SELECT PIH.timestamp, PIH.practice_id, PIH.timestamp as invoice_num, PIH.custom_invnum,
CEIL(PIH.total_invoice + PIH.tax + PIH.other_bill)  as grand_total, PIH.total_invoice, PIH.extra_charge_ph as extra_charge,
PIH.tax, PIH.other_bill, PIH.changed, PIH.source,
PIH.notes, PIH.is_active, PIH.paid as pay,
PIH.covered_amount, IF(PIH.is_active = 1, IF(PIH.total_invoice = 0 OR PIH.total_invoice + PIH.tax + PIH.other_bill - PIH.covered_amount <= PIH.paid, 1, IF(PIH.paid = 0, 0, 2)), '')  as invoice_st,
RPP.patient_id, RPP.first_name as pfname, RPP.last_name as plname, RPP.dob as p_dob, RPP.gender as p_gender, RPP.reff_id as p_reff_id, RPP.mobile_number as p_mobile, IF(PIH.group_doctors IS NOT NULL, NULL, D.doc_title) as doc_title, IF(PIH.group_doctors IS NOT NULL,
PIH.group_doctors, D.first_name) as doc_fname, IF(PIH.group_doctors IS NOT NULL, PIH.group_doctors, D.last_name) as doc_lname, IF(PIH.group_doctors IS NOT NULL, NULL, D.spc_dsg) as spc_dsg, PA.username, TL.timestamp as checkout_time, IP.name as ip_name, PMM.timestamp as mcu_id
            FROM  practice_invoice_header PIH
            INNER JOIN  practice_invoice_detail PID  ON PID.timestamp = PIH.timestamp
              AND  PID.practice_id = PIH.practice_id
            INNER JOIN  practice_queue_list PQL  ON PQL.encounter_id = PID.encounter_id
              AND  PQL.practice_place_id = PIH.practice_id
            INNER JOIN  temp_search_view D  ON D.id = PQL.doctor_id
              AND  D.pp_id = PQL.practice_place_id
            INNER JOIN  practice_place PP  ON PP.id = PIH.practice_id
            INNER JOIN  ref_practice_patient RPP  ON RPP.patient_id = PIH.patient_id
              AND  RPP.practice_id = PP.parent_id
            LEFT JOIN  practice_mcu_module PMM  ON PMM.id = PID.mcu_module_id
              AND  PMM.practice_id = PID.practice_id
            LEFT JOIN  transaction_log TL  ON TL.reff_id = PIH.timestamp
              AND  TL.practice_id = PIH.practice_id
              AND  TL.activity = "CHK"
            LEFT JOIN  practice_admin PA  ON PA.id = TL.admin_id
            LEFT JOIN  insurance_plan IP  ON IP.id = PIH.insurance_plan_id
            WHERE  PIH.source <> 'P'
              AND  PIH.practice_id = 28699
              AND  PIH.is_active = 1
              AND  PQL.cal_id >= 201807010
              AND  PQL.cal_id <= 201807312
            GROUP BY  PIH.timestamp, PIH.practice_id 
    ) AS U  LIMIT 0,20

注意:我只显示在此查询中使用的一些主表以及使用filesort / temporary排序的主表,当然如果我发布所有内容,那将是太多的信息。

查询是关于发票列表的,它有标题(practice_invoice_header)和详细信息(practice_invoice_detail)。此查询与practice_place表连接

CREATE TABLE `practice_invoice_header` (
 `timestamp` bigint(20) NOT NULL,
 `practice_id` int(11) NOT NULL,
 `cal_id` int(11) NOT NULL,
 `patient_id` int(11) NOT NULL DEFAULT 0,
 `source` char(1) NOT NULL COMMENT 'E = ENCOUNTER; P = OTHER (PHARM / LAB)',
 `total_invoice` float(30,2) NOT NULL DEFAULT 0.00,
 `tax` float(30,2) NOT NULL DEFAULT 0.00,
 `other_bill` float(30,2) NOT NULL DEFAULT 0.00,
 `changed` float(30,2) NOT NULL DEFAULT 0.00,
 `paid` float(30,2) NOT NULL DEFAULT 0.00,
 `covered_amount` float(30,2) NOT NULL DEFAULT 0.00,
 `notes` varchar(300) DEFAULT NULL,
 `custom_invnum` varchar(30) DEFAULT NULL,
 `insurance_plan_id` varchar(20) DEFAULT NULL,
 `is_active` int(11) NOT NULL DEFAULT 1,
 `cancel_reason` varchar(200) DEFAULT NULL,
 PRIMARY KEY (`timestamp`,`practice_id`),
 KEY `custom_invnum` (`custom_invnum`),
 KEY `insurance_plan_id` (`insurance_plan_id`),
 KEY `practice_id_3` (`practice_id`,`xxx_reff_id`),
 KEY `ph_check_status` (`ph_checked_by`),
 KEY `cal_id` (`cal_id`),
 KEY `practice_id_5` (`practice_id`,`outpx_id`),
 KEY `practice_id_6` (`practice_id`,`cal_id`,`source`,`is_active`),
 KEY `total_invoice` (`total_invoice`),
 KEY `patient_id` (`patient_id`),
 CONSTRAINT `practice_invoice_header_ibfk_1` FOREIGN KEY (`practice_id`)
       REFERENCES `practice_place` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

CREATE TABLE `practice_invoice_detail` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `timestamp` bigint(20) NOT NULL,
 `practice_id` int(11) NOT NULL,
 `item_id` int(11) NOT NULL,
 `item_sub_id` int(11) DEFAULT NULL,
 `item_type` char(1) NOT NULL COMMENT 'D = DRUG; P = PROCEDURE; L = LAB',
 `item_qty` float NOT NULL,
 `item_price` float(22,2) NOT NULL,
 `discount` float NOT NULL DEFAULT 0,
 `is_active` int(11) NOT NULL DEFAULT 1,
 PRIMARY KEY (`id`),
 KEY `item_type` (`item_type`),
 KEY `timestamp` (`timestamp`,`practice_id`),
 KEY `practice_id` (`practice_id`),
 KEY `item_id_2` (`item_id`,`item_sub_id`,`item_type`),
 KEY `timestamp_2` (`timestamp`,`practice_id`,`item_id`,`item_sub_id`,`item_type`),
 KEY `practice_id_3` (`practice_id`,`item_type`),
 KEY `the_id` (`id`,`practice_id`) USING BTREE,
 KEY `timestamp_3` (`timestamp`,`practice_id`,`item_type`,`item_comission`,
      `item_comission_type`, `doctor_id`,`item_id`,`item_sub_id`,`id`) USING BTREE,
 KEY `timestamp_4` (`timestamp`,`practice_id`,`item_id`,`item_sub_id`,`item_type`,
      `item_comission_2`,`item_comission_2_type`,`doctor_id_2`,`id`) USING BTREE,
 KEY `request_id` (`request_id`,`request_practice`),
 KEY `timestamp_5` (`timestamp`,`practice_id`,`is_active`),
 KEY `practice_id_6` (`practice_id`,`encounter_id`,`is_active`),
 KEY `practice_id_7` (`practice_id`,`item_type`,`encounter_id`,`is_active`),
 CONSTRAINT `practice_invoice_detail_ibfk_1` FOREIGN KEY (`timestamp`)
     REFERENCES `practice_invoice_header` (`timestamp`) ON DELETE CASCADE,
 CONSTRAINT `practice_invoice_detail_ibfk_2` FOREIGN KEY (`practice_id`)
     REFERENCES `practice_place` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1447348 DEFAULT CHARSET=latin1

CREATE TABLE `ref_practice_patient` (
 `practice_id` int(11) NOT NULL,
 `patient_id` int(11) NOT NULL,
 `reff_id` varchar(35) DEFAULT NULL,
 `is_user` int(11) NOT NULL DEFAULT 0,
 `parent_user_id` int(11) NOT NULL DEFAULT 0
 PRIMARY KEY (`practice_id`,`patient_id`),
 KEY `patient_id` (`patient_id`),
 KEY `reff_id` (`reff_id`),
 KEY `practice_id` (`practice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

CREATE TABLE `practice_place` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(75) NOT NULL,
 `statement` text DEFAULT NULL,
 `address` varchar(200) NOT NULL,
 `phone` varchar(15) NOT NULL,
 `wa_number` varchar(15) DEFAULT NULL,
 `fax` varchar(15) NOT NULL,
 `email` varchar(50) NOT NULL,
 `is_branch` int(11) NOT NULL,
 `parent_id` int(11) NOT NULL,
 `editted_by` int(11) DEFAULT NULL,
 `editted_date` bigint(20) DEFAULT NULL,
 `status` int(11) NOT NULL DEFAULT 1,
 PRIMARY KEY (`id`),
 KEY `parent_id` (`parent_id`),
 KEY `reff_id` (`reff_id`),
) ENGINE=InnoDB AUTO_INCREMENT=29058 DEFAULT CHARSET=latin1

下面是查询的解释产生,我突出显示使用filsort的那个(第2号)

1 PRIMARY ALL NULL  NULL NULL NULL  14028
2 DERIVED PP  const PRIMARY,parent_id PRIMARY 4 const 1 Using temporary; Using filesort
2 DERIVED PIH ref   PRIMARY,practice_id_3,practice_id_5,practice_id_6,practice_id_8,pharm_read,lab_read,rad_read,patient_id
                   practice_id_5 4 const  7014 Using where 
2 DERIVED RPP eq_ref PRIMARY,patient_id,practice_id,practice_id_2,practice_id_3 
                   PRIMARY 8 const,k6064619_lokadok.PIH.patient_id  1
2 DERIVED PID ref   timestamp,practice_id,timestamp_2,practice_id_2,practice_id_3,timestamp_3,timestamp_4,practice_id_4,practice_id_5,timestamp_5,practice_id_6,practice_id_7
                   timestamp 12 k6064619_lokadok.PIH.timestamp,const  1 
2 DERIVED PMM eq_ref PRIMARY,id,practice_id
                   PRIMARY 4 k6064619_lokadok.PID.mcu_module_id  1 Using where 
2 DERIVED TL  ref   reff_id reff_id 12 k6064619_lokadok.PIH.timestamp,const  1 Using where 
2 DERIVED PA  eq_ref PRIMARY PRIMARY 4 k6064619_lokadok.TL.admin_id  1 Using where 
2 DERIVED IP  ref   PRIMARY,id PRIMARY 22 k6064619_lokadok.PIH.insurance_plan_id  1 Using where 
2 DERIVED PQL ref   PRIMARY,encounter_id,cal_id_2
                   encounter_id 5 k6064619_lokadok.PID.encounter_id  2 Using where; Using index
2 DERIVED D   ref   doc_id,pp_id,id_2,pp_doc doc_id 4 k6064619_lokadok.PQL.doctor_id  1 Using where

我相信我已经在practice_place表中索引了parent_id,并且在ref_practice_patient中,patient_id和practice_id也是主要的。

mysql sql performance indexing filesort
1个回答
0
投票

为什么有外部查询?优化器可以自由地调整内部查询的结果,从而让LIMIT选择一个你不期望的排序。至少添加ORDER BY,最好还抛出外部选择。

主要指数

让我们分析设计索引的可能位置:

        WHERE  PIH.source <> 'P'
          AND  PIH.practice_id = 28699
          AND  PIH.is_active = 1
          AND  PQL.cal_id >= 201807010
          AND  PQL.cal_id <= 201807312
        GROUP BY  PIH.timestamp, PIH.practice_id 

由于涉及表的混合,因此不可能有一个处理所有WHERE的索引。

由于测试不是全部=,因此不可能超越WHERE并包括GROUP BY的列。

所以,我看到两个索引:

PIH:  INDEX(practice_id, is_active,   -- in either order
            source)
PQL:  INDEX(cal_id)

由于我们无法进入GROUP BY,优化器别无选择,只能收集基于WHERE的所有行,进行一些分组,并做一个ORDER BY(正如我所说,这是缺失的,但是必要的)。

因此,GROUP BYORDER BY将需要一个或两个temps和filesorts。不,你无法摆脱它,至少不能以某种方式改变查询。 (请注意,“filesort”实际上可能在RAM中完成。)

额外的SELECT图层可能会添加额外的temp和filesort。

当有两种情况时,EXPLAIN没有指出。 EXPLAIN FORMAT=JSON有这样的细节。

其他问题...

timestamp中使用PRIMARY KEY是有风险的,除非您确定可以使用相同的时间戳发生两行,或者PK中有另一列以确保唯一性。

不要用FLOAT钱。这将导致额外的舍入错误,并且它不能存储超过大约7位有效数字(对于便士而言不到10万美元)。不要使用float(30,2),它更糟糕,因为你强迫额外的舍入。使用DECIMAL(30,2),但选择合理的东西,而不是30.它需要14个字节 - 大多是浪费空间。

每当你有INDEX(a,b),你就不需要INDEX(a);它是多余的,并且(略)INSERTs减速。

LEFT JOIN  transaction_log TL
           ON  TL.reff_id = PIH.timestamp
          AND  TL.practice_id = PIH.practice_id
          AND  TL.activity = "CHK"

需求

INDEX(reff_id, practice_id, activity)  -- in any order

        INNER JOIN  practice_invoice_detail PID  ON PID.timestamp = PIH.timestamp
          AND  PID.practice_id = PIH.practice_id

PIH:  INDEX(practice_id, timestamp)   -- not the opposite order
PIH:  INDEX(practice_id, is_active, timestamp)

        INNER JOIN  practice_queue_list PQL  ON PQL.encounter_id = PID.encounter_id
          AND  PQL.practice_place_id = PIH.practice_id

PQL:  INDEX(encounter_id, cal_id)
PQL:  INDEX(encounter_id, practice_place_id, cal_id)

一些讨论......

  • JOIN中,EXPLAIN显示了一个通过表格的顺序;如果它以其他方式通过表格,那么它就不会给你提供任何线索。
  • 如果首先使用PQL,或者如果首先使用PIH,我试图显示可能需要哪个索引 - 即使用WHERE的东西用于该表,然后
  • 我试图显示加入另一个表的最佳索引。
  • 可能优化器不会从WHERE子句中未提及的任何表开始,但这不是确定的。
  • 我没有列出获取其他每个表的最佳索引。
  • 更多讨论:http://mysql.rjweb.org/doc.php/index_cookbook_mysql
© www.soinside.com 2019 - 2024. All rights reserved.