在 MySQL 中,可以使用过程将查询的结果集包含到另一个查询中吗?
In MySQL it is possible to use a procedure to include resultset of a query into another?
我有两个表按照下面的方案
接触
ID
Name
1
Mark
2
Matthew
3
Luke
4
John
contact_relation
contact_id
related_contact_id
type
1
2
reference
1
2
association
1
3
association
2
3
reference
3
4
association
我需要得到这样的结果集:
[
{
"id": 1,
"name": "Mark",
"references": [
{
"id": 2,
"name": "Matthew"
}
],
"associations": [
{
"id": 2,
"name": "Matthew"
},
{
"id": 3,
"name": "Luke"
}
]
},
{
"id": 2,
"name": "Matthew",
"references": [
{
"id": 3,
"name": "Luke"
}
],
"associations": []
},
{
"id": 3,
"name": "Luke",
"references": [],
"associations": [
{
"id": 4,
"name": "John"
}
]
}
]
我用PHP写了代码,如果联系人很少,它也能正常工作。但完整列表有超过 31,000 个联系人,响应时间超过 20 分钟。
是否可以编写一个 mysql 程序(或其他程序)来获得相同的结果?即使索引 references 和 associations 不能是 JSON 而是字符串格式的 JSON 也可以。
感谢支持
MySQL 脚本(在MySQL Workbench 中生成)
-- MySQL Workbench Forward Engineering
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
DROP SCHEMA IF EXISTS `mydb` ;
-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 ;
USE `mydb` ;
-- -----------------------------------------------------
-- Table `mydb`.`contact`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`contact` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` TINYTEXT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`contact_relation`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`contact_relation` (
`contact_id` BIGINT NOT NULL,
`related_contact_id` BIGINT NOT NULL,
`type` TINYTEXT NOT NULL,
INDEX `fk_contact_relation_contact_idx` (`contact_id` ASC),
INDEX `fk_contact_relation_contact1_idx` (`related_contact_id` ASC),
CONSTRAINT `fk_contact_relation_contact`
FOREIGN KEY (`contact_id`)
REFERENCES `mydb`.`contact` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_contact_relation_contact1`
FOREIGN KEY (`related_contact_id`)
REFERENCES `mydb`.`contact` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Data for table `mydb`.`contact`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`contact` (`id`, `name`) VALUES (1, 'Mark');
INSERT INTO `mydb`.`contact` (`id`, `name`) VALUES (2, 'Matthew');
INSERT INTO `mydb`.`contact` (`id`, `name`) VALUES (3, 'Luke');
INSERT INTO `mydb`.`contact` (`id`, `name`) VALUES (4, 'John');
COMMIT;
-- -----------------------------------------------------
-- Data for table `mydb`.`contact_relation`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (1, 2, 'reference');
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (1, 2, 'association');
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (1, 3, 'association');
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (2, 3, 'reference');
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (3, 4, 'association');
COMMIT;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
WITH
cte1 AS ( SELECT cr.contact_id,
cr.related_contact_id,
cr.type,
JSON_OBJECT('id', c.id, 'name', c.name) rel_obj
FROM contact_relation cr
JOIN contact c ON cr.related_contact_id = c.id ),
cte2 AS ( SELECT DISTINCT type
FROM contact_relation ),
cte3 AS ( SELECT DISTINCT contact_id
FROM contact_relation ),
cte4 AS ( SELECT type,
contact_id,
contact.name,
JSON_ARRAYAGG(rel_obj) rel_obj
FROM cte2
CROSS JOIN cte3
NATURAL LEFT JOIN cte1
JOIN contact ON cte3.contact_id = contact.id
GROUP BY type,
contact_id,
contact.name ),
cte5 AS ( SELECT contact_id id,
name,
MAX(CASE WHEN type = 'reference' THEN rel_obj END) reference,
MAX(CASE WHEN type = 'association' THEN rel_obj END) association
FROM cte4
GROUP BY contact_id,
name )
SELECT REPLACE(JSON_ARRAYAGG(JSON_OBJECT('id', id,
'name', name,
'references', reference,
'associations', association)), '[null]', '[]' )output
FROM cte5;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=748c70678b4f7a17b468ebacb096ebdc
我有两个表按照下面的方案
接触
ID | Name |
---|---|
1 | Mark |
2 | Matthew |
3 | Luke |
4 | John |
contact_relation
contact_id | related_contact_id | type |
---|---|---|
1 | 2 | reference |
1 | 2 | association |
1 | 3 | association |
2 | 3 | reference |
3 | 4 | association |
我需要得到这样的结果集:
[
{
"id": 1,
"name": "Mark",
"references": [
{
"id": 2,
"name": "Matthew"
}
],
"associations": [
{
"id": 2,
"name": "Matthew"
},
{
"id": 3,
"name": "Luke"
}
]
},
{
"id": 2,
"name": "Matthew",
"references": [
{
"id": 3,
"name": "Luke"
}
],
"associations": []
},
{
"id": 3,
"name": "Luke",
"references": [],
"associations": [
{
"id": 4,
"name": "John"
}
]
}
]
我用PHP写了代码,如果联系人很少,它也能正常工作。但完整列表有超过 31,000 个联系人,响应时间超过 20 分钟。
是否可以编写一个 mysql 程序(或其他程序)来获得相同的结果?即使索引 references 和 associations 不能是 JSON 而是字符串格式的 JSON 也可以。
感谢支持
MySQL 脚本(在MySQL Workbench 中生成)
-- MySQL Workbench Forward Engineering
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
DROP SCHEMA IF EXISTS `mydb` ;
-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 ;
USE `mydb` ;
-- -----------------------------------------------------
-- Table `mydb`.`contact`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`contact` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` TINYTEXT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `mydb`.`contact_relation`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`contact_relation` (
`contact_id` BIGINT NOT NULL,
`related_contact_id` BIGINT NOT NULL,
`type` TINYTEXT NOT NULL,
INDEX `fk_contact_relation_contact_idx` (`contact_id` ASC),
INDEX `fk_contact_relation_contact1_idx` (`related_contact_id` ASC),
CONSTRAINT `fk_contact_relation_contact`
FOREIGN KEY (`contact_id`)
REFERENCES `mydb`.`contact` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_contact_relation_contact1`
FOREIGN KEY (`related_contact_id`)
REFERENCES `mydb`.`contact` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Data for table `mydb`.`contact`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`contact` (`id`, `name`) VALUES (1, 'Mark');
INSERT INTO `mydb`.`contact` (`id`, `name`) VALUES (2, 'Matthew');
INSERT INTO `mydb`.`contact` (`id`, `name`) VALUES (3, 'Luke');
INSERT INTO `mydb`.`contact` (`id`, `name`) VALUES (4, 'John');
COMMIT;
-- -----------------------------------------------------
-- Data for table `mydb`.`contact_relation`
-- -----------------------------------------------------
START TRANSACTION;
USE `mydb`;
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (1, 2, 'reference');
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (1, 2, 'association');
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (1, 3, 'association');
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (2, 3, 'reference');
INSERT INTO `mydb`.`contact_relation` (`contact_id`, `related_contact_id`, `type`) VALUES (3, 4, 'association');
COMMIT;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
WITH
cte1 AS ( SELECT cr.contact_id,
cr.related_contact_id,
cr.type,
JSON_OBJECT('id', c.id, 'name', c.name) rel_obj
FROM contact_relation cr
JOIN contact c ON cr.related_contact_id = c.id ),
cte2 AS ( SELECT DISTINCT type
FROM contact_relation ),
cte3 AS ( SELECT DISTINCT contact_id
FROM contact_relation ),
cte4 AS ( SELECT type,
contact_id,
contact.name,
JSON_ARRAYAGG(rel_obj) rel_obj
FROM cte2
CROSS JOIN cte3
NATURAL LEFT JOIN cte1
JOIN contact ON cte3.contact_id = contact.id
GROUP BY type,
contact_id,
contact.name ),
cte5 AS ( SELECT contact_id id,
name,
MAX(CASE WHEN type = 'reference' THEN rel_obj END) reference,
MAX(CASE WHEN type = 'association' THEN rel_obj END) association
FROM cte4
GROUP BY contact_id,
name )
SELECT REPLACE(JSON_ARRAYAGG(JSON_OBJECT('id', id,
'name', name,
'references', reference,
'associations', association)), '[null]', '[]' )output
FROM cte5;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=748c70678b4f7a17b468ebacb096ebdc