使用 table 字段作为存储过程参数(将给定的 table 字段重新分配到其他 table 中)

Use table fields as stored procedure parameters (to redistribute a given table fields into other tables)

我有以下table的结构:

ITEMS:
╔═══════════╤══════════════╤══════╤═════╤═════════╤════════════════╗
║ FIELD     │ TYPE         │ NULL │ KEY │ DEFAULT │ EXTRA          ║
╠═══════════╪══════════════╪══════╪═════╪═════════╪════════════════╣
║ id        │ int          │ NO   │ PRI │         │ auto_increment ║
╟───────────┼──────────────┼──────┼─────┼─────────┼────────────────╢
║ image_url │ varchar(255) │ NO   │     │         │                ║
╚═══════════╧══════════════╧══════╧═════╧═════════╧════════════════╝

ITEM_TRANSLATIONS:
╔═════════════╤══════════════╤══════╤═════╤═════════╤════════════════╗
║ FIELD       │ TYPE         │ NULL │ KEY │ DEFAULT │ EXTRA          ║
╠═════════════╪══════════════╪══════╪═════╪═════════╪════════════════╣
║ id          │ int          │ NO   │ PRI │         │ auto_increment ║
╟─────────────┼──────────────┼──────┼─────┼─────────┼────────────────╢
║ description │ varchar(255) │ NO   │     │         │                ║
╟─────────────┼──────────────┼──────┼─────┼─────────┼────────────────╢
║ title       │ varchar(45)  │ NO   │     │         │                ║
╚═════════════╧══════════════╧══════╧═════╧═════════╧════════════════╝

我还有一个存储过程,可以通过这种方式将其参数重新分配给所需的 table:

DELIMITER //
    DROP PROCEDURE IF EXISTS addItem //
    CREATE PROCEDURE addItem (
        IN _item__image_url VARCHAR(255),
        IN _item_translations__title VARCHAR(45),
        IN _item_translations__description VARCHAR(255)
    )

    BEGIN
        START TRANSACTION;
            INSERT INTO item (
                image_url
            )
            VALUES (
                _item__image_url
            );


            INSERT INTO item_translations (
                item_id,
                title,
                `description`
            )
            VALUES (
                LAST_INSERT_ID(),
                _item_translations__title,
                _item_translations__description
            );
        COMMIT ;
    END //
DELIMITER ;

如果我这样调用这个过程:

CALL addBrand(
    "/images/items.png",
    "My Item",
    "An oversimplified item just for this question."
);

我得到了预期的结果:

ITEMS:
╔════╤═══════════════════╗
║ ID │ IMAGE_URL         ║
╠════╪═══════════════════╣
║ 19 │ /images/items.png ║
╚════╧═══════════════════╝

ITEM_TRANSLATIONS:
╔════╤═════════╤═════════╤════════════════════════════════════════════════╗
║ ID │ ITEM_ID │ TITLE   │ DESCRIPTION                                    ║
╠════╪═════════╪═════════╪════════════════════════════════════════════════╣
║ 7  │ 19      │ My Item │ An oversimplified item just for this question. ║
╚════╧═════════╧═════════╧════════════════════════════════════════════════╝

我有第三个 table,其中 N 行包含所有必填字段:

IMPORTED_TABLE
╔════╤══════════════╤══════════════════════════════════╤════════════════════════╗
║ ID │ TITLE        │ DESCRIPTION                      │ IMAGE_URL              ║
╠════╪══════════════╪══════════════════════════════════╪════════════════════════╣
║ 42 │ Another Item │ Yet another oversimplified item. │ /images/items_2.png    ║
╟────┼──────────────┼──────────────────────────────────┼────────────────────────╢
║ 43 │ This Item    │ A nice item                      │ /images/thanks.png     ║
╟────┼──────────────┼──────────────────────────────────┼────────────────────────╢
║ 44 │ Trixie Item  │ The great and powerful item!     │ /images/mlp/trixie.png ║
╚════╧══════════════╧══════════════════════════════════╧════════════════════════╝

如何使用此 table 内容作为存储过程的参数,以便能够根据需要填充所需的 table? 为了得到这个:

ITEMS:
╔════╤════════════════════════╗
║ ID │ IMAGE_URL              ║
╠════╪════════════════════════╣
║ 19 │ /images/items.png      ║
╟────┼────────────────────────╢
║ 20 │ /images/items_2.png    ║
╟────┼────────────────────────╢
║ 21 │ /images/thanks.png     ║
╟────┼────────────────────────╢
║ 22 │ /images/mlp/trixie.png ║
╚════╧════════════════════════╝

ITEM_TRANSLATIONS
╔════╤═════════╤══════════════╤════════════════════════════════════════════════╗
║ ID │ ITEM_ID │ TITLE        │ DESCRIPTION                                    ║
╠════╪═════════╪══════════════╪════════════════════════════════════════════════╣
║ 7  │ 19      │ My Item      │ An oversimplified item just for this question. ║
╟────┼─────────┼──────────────┼────────────────────────────────────────────────╢
║ 8  │ 20      │ Another Item │ Yet another oversimplified item.               ║
╟────┼─────────┼──────────────┼────────────────────────────────────────────────╢
║ 9  │ 21      │ This Item    │ A nice item                                    ║
╟────┼─────────┼──────────────┼────────────────────────────────────────────────╢
║ 10 │ 22      │ Trixie Item  │ The great and powerful item!                   ║
╚════╧═════════╧══════════════╧════════════════════════════════════════════════╝

显然这是一个过于简单化的示例。在存储过程中,所有参数都有不同的数据处理方式,所以我不必重新创建存储过程。

IMPORTED_TABLE 中的 ID 值是否应该用于 ITEMS 中的 ID

如果是这样,那么您可以这样做:

START TRANSACTION;

INSERT INTO ITEMS (ID, IMAGE_URL) 
 SELECT ID, IMAGE_URL FROM IMPORTED_TABLE;

INSERT INTO ITEM_TRANSLATIONS (ITEM_ID, TITLE, DESCRIPTION)
 SELECT ID, TITLE, DESCRIPTION FROM IMPORTED_TABLE;

COMMIT;

这将对 ITEMS.IDITEM_TRANSLATIONS.ITEM_ID 逐字使用 ID 值。

但是,如果您想插入 URL 并忽略导入数据中的 ID 值,并让 ITEMS table 生成新的 ID 值,那么您可以这样做它在一个批次中,并假设该批次是一组连续的值。

START TRANSACTION;

INSERT INTO ITEMS (IMAGE_URL) 
 SELECT IMAGE_URL FROM IMPORTED_TABLE;

SET @START_ID = LAST_INSERT_ID() - 1;

INSERT INTO ITEM_TRANSLATIONS (ITEM_ID, TITLE, DESCRIPTION)
 SELECT (@START_ID := @START_ID+1), TITLE, DESCRIPTION FROM IMPORTED_TABLE;

COMMIT;

假设这些值是连续的是否安全?默认情况下,是的,它是安全的。例如,MySQL 的 JDBC 驱动程序在您进行批量插入时做出此假设,因此它可以 return 生成的 ID 值集。

例外情况是,如果您在 MySQL 实例上设置了 innodb_autoinc_lock_mode=2 选项,则不保证这些值是连续的。这不是默认设置,因此它可能不适用于您的情况。

(阅读 https://dev.mysql.com/doc/refman/8.0/en/innodb-auto-increment-handling.html 了解详情)

(代表问题作者发布了答案改进,以便将其移动到答案space).

感谢接受的答案,我能够解决这个挑战。然而,auto_increment 值增加了行数 inserted/read(可能是因为 MySQL 中的错误,如评论中所述)。

所以我需要创建另一个存储过程来获取最大 ID 值并使用它来更改所有涉及的 table。此存储过程如下:

DELIMITER //
DROP PROCEDURE IF EXISTS tableMaxID //
CREATE PROCEDURE tableMaxID(IN nameOfTable VARCHAR(20))
    BEGIN
        SET @qry=CONCAT('SELECT MAX(id) INTO @maxIdValue FROM ', nameOfTable);
        PREPARE st FROM @qry;
        EXECUTE st;
        DEALLOCATE PREPARE st;
        
        SET @maxIdValue := @maxIdValue + 1;
    END //
DELIMITER ;

然后,我将此修复程序添加到每个涉及的原始存储过程中 table:

DELIMITER //
    DROP PROCEDURE IF EXISTS addItem //
    CREATE PROCEDURE addItem ()

    BEGIN
        START TRANSACTION;
            /* INSERT INTO the corresponding tables */
            INSERT INTO items (image_url) 
                SELECT CONCAT(`items.image_url`, ' testing') FROM imported_table;
            
            SET @START_ID = LAST_INSERT_ID() - 1;
            
            INSERT INTO items_translations (item_id, title, `description`)
                SELECT (@START_ID := @START_ID+1), `items_translations.title`, `brands_translations.description` FROM imported_table;
            
            /***********/
            /* Fix brands auto_increment */
            CALL tableMaxID('items');                
            SET @qry=CONCAT('ALTER TABLE items AUTO_INCREMENT =', @maxIdValue);
            PREPARE st FROM @qry;
            EXECUTE st;
            DEALLOCATE PREPARE st;
            
            /* Fix items_translations auto_increment */
            CALL tableMaxID('items_translations');                
            SET @qry=CONCAT('ALTER TABLE items_translations AUTO_INCREMENT =', @maxIdValue);
            PREPARE st FROM @qry;
            EXECUTE st;
            DEALLOCATE PREPARE st;
        COMMIT;
    END //
DELIMITER ;

现在,只需调用存储过程,将给定的数据插入相应的 tables,用正确的 auto_increment 修复涉及的 tables值:

CALL addBrand();