我有以下查询:
SELECT *
FROM document d
INNER JOIN users_documents ud ON d.id = ud.document_id
WHERE (d.document_state_id=2
AND ud.sharing_type_id=1
AND ud.user_id=:id
AND CURRENT_TIMESTAMP() BETWEEN d.upload_datetime AND d.approval_end_time
AND ud.approval=2)
OR(d.document_state_id=1
AND ud.sharing_type_id=2
AND ud.user_id=:id
AND CURRENT_TIMESTAMP() BETWEEN d.active_start_time AND d.active_end_time
AND ud.approval=2)
ORDER BY ud.application_date;
问题是,它需要大约 800 毫秒。当我将查询结尾更改为:
ORDER BY ud.uploadDatetime;
我添加了索引:upload_datetime(表文档),现在查询需要大约 70 毫秒。问题是我真的需要根据 ud.application_date 对数据进行排序。 我尝试添加索引:application_date(在表 users_documents 上),但它没有帮助(仍然需要 800 毫秒)。这次应该用哪个指标来降低呢?或者我应该做什么?我正在使用 MySQL。
非常感谢您的帮助!
编辑: 查询说明:查询返回具有某个 id 的给定用户的新文档。它返回“批准”(sharing_type=1)和阅读(sharing_type=2)的文档,approval=2(用户尚未看到该文档),approval=1(用户批准了该文档),0=未批准; upload_datetime=文档上传到系统的时间戳; applyr_end_time=文档在此日期之前可以被用户批准; activeStartTime=批准后,文档将从该日期到日期(=activeEndTime)向其他用户显示。 结构
-- -----------------------------------------------------
-- Table `po_db`.`document_state`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `po_db`.`document_state` (
`id` BIGINT(20) NOT NULL,
`state` VARCHAR(255) NULL DEFAULT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci;
-- -----------------------------------------------------
-- Table `po_db`.`document`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `po_db`.`document` (
`id` BIGINT(20) NOT NULL,
`active_end_time` DATETIME NOT NULL,
`active_start_time` DATETIME NOT NULL,
`approval_end_time` DATETIME NOT NULL,
`description` VARCHAR(255) NOT NULL,
`name` VARCHAR(60) NOT NULL,
`resource_path` VARCHAR(255) NOT NULL,
`title` VARCHAR(15) NOT NULL,
`upload_datetime` DATETIME NOT NULL,
`document_state_id` BIGINT(20) NOT NULL,
`user_id` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `UK_36vs45u76s1n950kwxfa5lyhc` (`name` ASC),
INDEX `FK1rwpvwxw2bvldt30kwfcbf57l` (`document_state_id` ASC),
INDEX `FKjhdxdv9sijhujiynqbb5jc010` (`user_id` ASC),
INDEX `title` (`title` ASC),
INDEX `upload` USING BTREE (`upload_datetime`),
CONSTRAINT `FK1rwpvwxw2bvldt30kwfcbf57l`
FOREIGN KEY (`document_state_id`)
REFERENCES `po_db`.`document_state` (`id`),
CONSTRAINT `FKjhdxdv9sijhujiynqbb5jc010`
FOREIGN KEY (`user_id`)
REFERENCES `po_db`.`user` (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci;
-- -----------------------------------------------------
-- Table `po_db`.`sharing_type`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `po_db`.`sharing_type` (
`id` BIGINT(20) NOT NULL,
`name` VARCHAR(255) NULL DEFAULT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci;
和
-- -----------------------------------------------------
-- Table `po_db`.`users_documents`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `po_db`.`users_documents` (
`document_id` BIGINT(20) NOT NULL,
`sharing_type_id` BIGINT(20) NOT NULL,
`user_id` BIGINT(20) NOT NULL,
`application_date` DATETIME NOT NULL,
`approval` INT(11) NOT NULL,
`email_sent` BIT(1) NOT NULL,
PRIMARY KEY (`document_id`, `sharing_type_id`, `user_id`),
INDEX `FK4mt7odsubst5269c4djnj4jip` (`sharing_type_id` ASC),
INDEX `FKtn1i27tltfpy3n4pe306vtwu9` (`user_id` ASC),
INDEX `historie_dok` (`approval` ASC, `sharing_type_id` ASC, `user_id` ASC, `application_date` ASC),
CONSTRAINT `FK4mt7odsubst5269c4djnj4jip`
FOREIGN KEY (`sharing_type_id`)
REFERENCES `po_db`.`sharing_type` (`id`),
CONSTRAINT `FK6abpoybb0f2ydy6ufla1wo80x`
FOREIGN KEY (`document_id`)
REFERENCES `po_db`.`document` (`id`),
CONSTRAINT `FKtn1i27tltfpy3n4pe306vtwu9`
FOREIGN KEY (`user_id`)
REFERENCES `po_db`.`user` (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci;
编辑2: 我将查询的结尾更改为:
ORDER BY CASE WHEN d.document_state_id=2 THEN d.upload_datetime ELSE d.active_start_time END;
它与 application_date 基本相同,但仍然很慢(300 毫秒,但比 800 毫秒要好)。
我尝试添加索引(表文档):document_state_id、upload_datetime、active_start_time
但它再次没有任何帮助......:/
最佳答案
A 计划:将 OR
转换为 UNION
:
( SELECT *
FROM document d
INNER JOIN users_documents ud ON d.id = ud.document_id
WHERE d.document_state_id=2
AND ud.sharing_type_id=1
AND ud.user_id=:id
AND CURRENT_TIMESTAMP() BETWEEN d.upload_datetime
AND d.approval_end_time
AND ud.approval=2
)
UNION ALL
( SELECT *
FROM document d
INNER JOIN users_documents ud ON d.id = ud.document_id
WHERE d.document_state_id=1
AND ud.sharing_type_id=2
AND ud.user_id=:id
AND CURRENT_TIMESTAMP() BETWEEN d.active_start_time
AND d.active_end_time
AND ud.approval=2
)
ORDER BY application_date;
这样,每个 SELECT 都可以充分利用以approval ASC、sharing_type_id ASC、user_id ASC 开头的索引。 (第四列没有帮助。)
B 计划:使用“覆盖索引”:
SELECT d2.*, ud2.*
FROM (
( SELECT id, document_id, sharing_type_id, user_id
FROM document d
INNER JOIN users_documents ud ON d.id = ud.document_id
WHERE d.document_state_id=2
AND ud.sharing_type_id=1
AND ud.user_id=:id
AND CURRENT_TIMESTAMP() BETWEEN d.upload_datetime
AND d.approval_end_time
AND ud.approval=2
)
UNION ALL
( SELECT id, document_id, sharing_type_id, user_id
FROM document d
INNER JOIN users_documents ud ON d.id = ud.document_id
WHERE d.document_state_id=1
AND ud.sharing_type_id=2
AND ud.user_id=:id
AND CURRENT_TIMESTAMP() BETWEEN d.active_start_time
AND d.active_end_time
AND ud.approval=2
)
)
JOIN document d2 USING(id)
JOIN users_documents ud2 USING(document_id, sharing_type_id, user_id)
ORDER BY ud2.application_date;
加上这些新索引;顺序很重要:
UD: INDEX(approval, sharing_type_id, user_id, document_id) -- document_id last
D: INDEX(document_state_id, approval_end_time, approval_start_time, document_id)
D: INDEX(document_state_id, active_end_time, active_start_time, document_id)
关于mysql - 查询时间太长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59446860/