Mysql存储过程的查询优化

Posted

技术标签:

【中文标题】Mysql存储过程的查询优化【英文标题】:Query optimization of Mysql stored-procedure 【发布时间】:2017-09-12 12:26:54 【问题描述】:

我在一个 RDBMS (MYSQL) 工作,我们有大约 15 到 20 个 个表,主表有超过 4 个缺少行和 70 个列 在每个表中。在大多数情况下检索数据时,我必须使用导致操作延迟的 left join。我们使用存储过程,请建议任何快速操作的最佳方法。

CREATE TABLE `patient_data` (
    `p_id` INT(11) NOT NULL AUTO_INCREMENT,
    `entry_date` DATETIME NOT NULL COMMENT 'Registration Date',
    `hr_id` INT(11) NOT NULL,
    `ua_id` INT(11) NOT NULL,
    `mrn1` VARCHAR(20) NOT NULL COMMENT 'mrn initial',
    `mrn2` INT(20) NOT NULL DEFAULT '0' COMMENT 'mrn counter',
    `title` VARBINARY(50) NULL DEFAULT NULL,
    `fname` VARBINARY(50) NOT NULL,
    `lname` VARBINARY(50) NOT NULL,
    `mname` VARBINARY(50) NULL DEFAULT NULL,
    `suffix` VARBINARY(50) NULL DEFAULT NULL,
    `dob` VARBINARY(50) NOT NULL,
    `pat_photo` VARBINARY(50) NULL DEFAULT NULL,
    `blood_group` VARBINARY(50) NULL DEFAULT NULL,
    `street` VARBINARY(255) NULL DEFAULT NULL,
    `postal_code` VARBINARY(50) NULL DEFAULT NULL,
    `city` VARBINARY(50) NULL DEFAULT NULL,
    `state` VARBINARY(50) NULL DEFAULT NULL,
    `country` VARBINARY(50) NULL DEFAULT NULL,
    `drivers_license` VARBINARY(50) NULL DEFAULT NULL,
    `ss` VARBINARY(20) NULL DEFAULT NULL COMMENT 'adhar no',
    `occupation` VARBINARY(50) NULL DEFAULT NULL,
    `home_phone` VARBINARY(50) NULL DEFAULT NULL,
    `work_phone` VARBINARY(50) NULL DEFAULT NULL,
    `mobile_no` VARBINARY(50) NULL DEFAULT NULL,
    `emergency_no` VARBINARY(50) NULL DEFAULT NULL,
    `m_status` VARBINARY(50) NULL DEFAULT NULL,
    `emergency_contact` VARBINARY(50) NULL DEFAULT NULL,
    `sex` VARBINARY(50) NOT NULL,
    `email` VARBINARY(50) NULL DEFAULT NULL,
    `alternate_email` VARBINARY(50) NULL DEFAULT NULL,
    `race` VARBINARY(50) NULL DEFAULT NULL,
    `financial` VARBINARY(50) NULL DEFAULT NULL,
    `ethnicity` VARBINARY(50) NULL DEFAULT NULL,
    `interpreter` VARBINARY(50) NULL DEFAULT NULL,
    `migrantseasonal` VARBINARY(50) NULL DEFAULT NULL,
    `family_size` VARBINARY(50) NULL DEFAULT NULL,
    `monthly_income` VARBINARY(50) NULL DEFAULT NULL,
    `homeless` VARBINARY(50) NULL DEFAULT NULL,
    `financial_review` VARBINARY(50) NULL DEFAULT NULL,
    `referral_source` VARBINARY(30) NULL DEFAULT NULL,
    `vfc` VARBINARY(50) NULL DEFAULT NULL,
    `admit_flag` INT(2) NOT NULL DEFAULT '0' COMMENT '0-Not Admit,1-admitted',
    `select_reason` VARCHAR(20) NULL DEFAULT NULL,
    `delete_reason` VARCHAR(150) NULL DEFAULT NULL,
    `relation_with_patient` VARBINARY(50) NULL DEFAULT NULL,
    `relative_name` VARBINARY(100) NULL DEFAULT NULL,
    `referred_by` VARBINARY(50) NULL DEFAULT NULL,
    `referred_no` VARBINARY(20) NULL DEFAULT NULL,
    `flag` VARCHAR(2) NOT NULL DEFAULT 'c',
    `update_date` DATETIME NULL DEFAULT NULL COMMENT 'Last Updation of Date',
    `update_ua_id` INT(11) NOT NULL DEFAULT '0',
    `tpa` VARCHAR(50) NULL DEFAULT NULL,
    `age` VARBINARY(50) NULL DEFAULT NULL,
    `opd_no` VARBINARY(50) NULL DEFAULT NULL,
    `duplicate_flag` VARBINARY(50) NULL DEFAULT NULL,
    `department` VARBINARY(50) NULL DEFAULT NULL,
    `patient_type` VARBINARY(50) NULL DEFAULT NULL,
    `revisit` INT(2) NULL DEFAULT '0',
    `simul_flag` INT(2) NULL DEFAULT '0' COMMENT '1= duplicate(simulation)',
    `tags` VARCHAR(50) NULL DEFAULT NULL,
    `balance_amount` FLOAT NULL DEFAULT NULL,
    `opd_visit_counter` INT(50) NULL DEFAULT NULL,
    `patient_camera_pic` VARCHAR(255) NULL DEFAULT NULL,
    `baby_birth_time` VARBINARY(50) NULL DEFAULT NULL,
    `location` VARBINARY(50) NULL DEFAULT NULL,
    `aadhaar_no` VARBINARY(50) NULL DEFAULT NULL,
    `old_uhid` VARCHAR(50) NULL DEFAULT NULL,
    `er_id` INT(11) NULL DEFAULT NULL COMMENT 'For current er id',
    `patient_pancardno` VARBINARY(50) NULL DEFAULT NULL,
    `district` VARBINARY(50) NULL DEFAULT NULL,
    `religion` VARBINARY(50) NULL DEFAULT NULL,
    `vulnerable_type` INT(11) NULL DEFAULT '0',
    `vulnerable_data` VARCHAR(255) NULL DEFAULT NULL,
    `weight` FLOAT NULL DEFAULT NULL,
    `insurance_type` INT(11) NULL DEFAULT NULL,
    PRIMARY KEY (`p_id`),
    INDEX `hr_id` (`hr_id`),
    INDEX `u_id` (`ua_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB

;

这是我存储的例程

BEGIN
SELECT pd.p_id, er.er_id, pd.flag, pd.delete_reason, pd.select_reason,
       AES_DECRYPT(pd.fname, encryptkey) AS fname,
       AES_DECRYPT(pd.age, encryptkey) AS age,
       AES_DECRYPT(pd.lname, encryptkey) AS lname,
       AES_DECRYPT(pd.home_phone, encryptkey) AS home_phone,
       AES_DECRYPT(pd.mobile_no, encryptkey) AS mobile_phone,
       AES_DECRYPT(pd.relation_with_patient, encryptkey) AS relation,
       AES_DECRYPT(pd.relative_name, encryptkey) AS relative_name,
       AES_DECRYPT(pd.street, encryptkey) AS street,
       AES_DECRYPT(pd.title, encryptkey) AS title,
       cl.city_name AS city,
       sl.state AS state,
       AES_DECRYPT(pd.sex, encryptkey) AS gender,
       AES_DECRYPT(pd.dob, encryptkey) AS dob,
       AES_DECRYPT(pd.email, encryptkey) AS email, pd.admit_flag, pd.entry_date, pd.mrn1,
       pd.mrn2, id.insurance_type, fcm.f_cm_name
FROM   patient_data AS pd
LEFT JOIN insurance_data AS id ON id.p_id = pd.p_id
LEFT   JOIN state_list AS sl ON sl.sl_id = AES_DECRYPT(pd.state,encryptkey)
LEFT   JOIN city_list AS cl ON cl.cl_id = AES_DECRYPT(pd.city,encryptkey)
LEFT   JOIN ehr_reg AS er ON er.p_id = pd.p_id
LEFT   JOIN facility_category_master AS fcm ON id.insurance_type = fcm.fc_m_id
WHERE  pd.hr_id = proc_hrid
AND    pd.flag <> '0'
GROUP BY pd.p_id
ORDER  BY pd.entry_date DESC, pd.p_id DESC ;
END

【问题讨论】:

使用索引。除此之外,如果没有更多信息,我们将无能为力。您可以尝试在此处发布一些示例查询以及您的数据库架构,或者您可以在 dba.stackexchange.com 上做同样的事情。 我已经添加了架构 【参考方案1】:

您希望从该查询中获得多少行?如果只有一个,我不明白为什么会很慢。

如果您获得数千行,那么请接受获取数千行需要时间。

这个索引可能有帮助:

INDEX(hr_id, flag, p_id)

或者,也许所有LEFT JOINs 都是许多:1?也就是说,对于给定的p_id,是否只有一个保险、州、城市、ehr 和设施?如果是这样,您不需要GROUP BY。这将绕过一些浪费的步骤。

你可以替换

sl.state AS state,
LEFT   JOIN state_list AS sl ON sl.sl_id = AES_DECRYPT(pd.state,encryptkey)

( SELECT state FROM state_list
       WHERE sl_id = AES_DECRYPT(pd.state,encryptkey) ) AS state,

我更愿意使用单个联接而不是单独的规范化将诸如城市和州之类的事物组推到另一个表中。

我无法想象二进制标志(例如性别)是最不安全的。甚至state 也可能通过查看相关人群来快速破解。

我建议您收集不需要搜索的列并将它们放入单个JSON 字符串,然后对其进行加密。这样会更安全。

AES 例程很容易被破解,以至于除了微不足道的用途外,它们是否被禁止使用。你的应用看起来比这更严重。

很抱歉,性能需要让位于安全性。聘请安全顾问。

【讨论】:

实际上是它的离线应用,所以安全不是我关心的问题,但性能是

以上是关于Mysql存储过程的查询优化的主要内容,如果未能解决你的问题,请参考以下文章

mysql的 视图触发器事务存储过程函数索引与慢查询优化

MySQL 慢查询优化

mysql查询性能优化

MySQL-视图-触发器-事务-存储过程-函数-流程控制-索引与慢查询优化-06

MySQL架构原理及优化

MySQL监控及优化