【问题标题】:In MySQL it is possible to use a procedure to include resultset of a query into another?在 MySQL 中,可以使用一个过程将查询的结果集包含到另一个中吗?
【发布时间】:2021-04-07 08:33:00
【问题描述】:

我有两个表按照以下方案

联系

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;

【问题讨论】:

  • 是否可以编写一个 mysql 程序(或其他程序)让我得到相同的结果? 当然。此外,不需要SP,一切都可以在一个查询中执行。 我有两个按照以下方案的表 提供 CREATE TABLE + INSERT INTO,而不是(仅)类似表的形式。确保所需的输出严格匹配提供的源数据。指定精确 MySQL 版本。
  • 感谢 @Akina 我为 MySQL 查询添加了脚本 - Luca Romano 56 分钟前

标签: mysql json stored-procedures relational-database


【解决方案1】:
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

【讨论】:

  • 非常感谢@Akina。你知道在 MySQL 5.7 中是否有办法做到这一点?
  • WITH cte AS (SELECT ..) SELECT ... FROM ... cte ... 简单地转换为适用于 5.7 的SELECT .. FROM .. (SELECT ..) AS cte ..
  • 谢谢@Akina,但在 MySQL 5.7 中存在一个问题:referencesassociations 具有相同的值,其中存在重复.
  • @LucaRomano 也许我在替换过程中不准确......准确地做同样的事情。
猜你喜欢
  • 2012-08-30
  • 1970-01-01
  • 2013-03-24
  • 2017-05-29
  • 1970-01-01
  • 2016-04-06
  • 1970-01-01
相关资源
最近更新 更多