在 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 程序(或其他程序)来获得相同的结果?即使索引 referencesassociations 不能是 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