pl/SQL 过程从不同表中的值生成一个表中的值

Posted

技术标签:

【中文标题】pl/SQL 过程从不同表中的值生成一个表中的值【英文标题】:pl/SQL Procedure to generate values in one table from values in a different table 【发布时间】:2019-03-27 16:40:36 【问题描述】:

我正在学习 PL/SQL,但不知道从哪里开始编写程序。

我有两张表,一张有一个部门名称,id 范围从和 id 范围到。还有另一个需要在这些范围内包含所有值的表。

-- table_a
CREATE TABLE table_a (
    D_ID number not null primary key,
    DEPT VARCHAR(10),
    ID_FROM VARCHAR(7),
    ID_TO VARCHAR(7),
    IS_POP varchar(1) check(IS_POP IN ('Y', 'N'))
    );

--values in table a
INSERT INTO table_a values (1, 'abc', 'A10', 'A100', 'Y');
INSERT INTO table_a values (2, 'def', 'B10', 'B50', 'N');
INSERT INTO table_a values (3, 'ghi', 'C01', 'C25', 'N');

--table_b
CREATE TABLE table_b (
    D_id number,
    ID_NUM VARCHAR(7) primary key,
    STATUS VARCHAR(8) CHECK (status IN ('Free','Taken')),
    CONSTRAINT fk_interval FOREIGN KEY (D_id) REFERENCES table_a (D_ID)
    );

我正在尝试编写一个过程来使用从 table_a 生成的值填充 table_b,其中 IS_POP 字段具有“N”,并在生成值时将其更改为“Y”。 所以 table_b 应该有 66 行类似

的条目
D_id ID_NUM STATUS
2     B10   Free
2     B11   Free
2     B12   Free
2     B13   Free
...
3     C24   Free
3     C25   Free

【问题讨论】:

谢谢siretep。在示例数据中,ID_FROM 和 ID_TO 始终共享相同的字母组。 A10 - A100, B10 - B50 等等。这是否一致,或者是否存在 ID_FROM 和 ID_TO 的字母组不同的情况,例如 B30 - D-50。 不,他们是同一组。表格修改了,我已经写了一个触发器来检查值,规则是前面的字母相同,数字从小到大。我还应该说我正在考虑将它与 substr 拆分并将这些值提供给 for 循环。但我不知道该怎么做。 谢谢siretep。这有助于了解。我添加了一个使用正则表达式值替换而不是长度/子字符串的答案(我不确定组键是否总是单字符或可能是多字符等),但如果您的数据格式一致,我相信 substr 也可以使用。 谢谢亚历克斯吉布斯。我的例子只是一个例子,但真实的表格是相似的。现在我知道从哪里开始了。是的,其中一个选项是可以更新 id_from 和 id_to 值,并且 table_b 中的条目不应该丢失(如果该间隔中有任何条目)。关于零填充,唯一的规则是至少有 1 个和最多 4 个数字,所以我真的不知道 A0001 是否应该是可能的。 【参考方案1】:

这是一个示例过程,它在此示例数据中生成 66 条记录,尽管我不确定在 TABLE_B 中是否需要零填充。

这也使用了迭代 DML,因此如果大规模使用可能会提供较差的性能,并且不适合处理 TABLE_A 在已填充到 TABLE_B 后更新的情况。可以根据您的情况对这些内容进行增强。

CREATE OR REPLACE PROCEDURE POPULATE_TAB_B
IS
BEGIN
    FOR POINTER IN
        (SELECT D_ID,
             REGEXP_REPLACE(ID_FROM , '[0-9].*' , NULL)             AS PREFIX,
             LEAST(LENGTH(REGEXP_REPLACE(ID_FROM , '[^0-9]' , NULL)) ,
                   LENGTH(REGEXP_REPLACE(ID_TO , '[^0-9]' , NULL))) AS PAD_LENGTH,
             TO_NUMBER(REGEXP_REPLACE(ID_FROM , '[^0-9]' , NULL))   AS FROM_NUM,
             TO_NUMBER(REGEXP_REPLACE(ID_TO , '[^0-9]' , NULL))     AS TO_NUM
         FROM TABLE_A
         WHERE TABLE_A.IS_POP = 'N' FOR UPDATE NOWAIT)
        LOOP
            INSERT INTO TABLE_B(D_ID , ID_NUM , STATUS)
            SELECT POINTER.D_ID,
                (POINTER.PREFIX || LPAD(TO_CHAR(((LEVEL - 1) + POINTER.FROM_NUM)) , POINTER.PAD_LENGTH , '0')), 'Free'
            FROM DUAL
            CONNECT BY LEVEL <= ((POINTER.TO_NUM + 1) - POINTER.FROM_NUM);

            UPDATE TABLE_A
            SET TABLE_A.IS_POP = 'Y'
            WHERE TABLE_A.D_ID = POINTER.D_ID;

        END LOOP;

END;
/

Procedure created.

BEGIN
    POPULATE_TAB_B();
END;
/

PL/SQL procedure successfully completed.

SELECT *
FROM TABLE_B
ORDER BY 1 ASC, 2 ASC;

 D_ID ID_NUM   STATUS
     2 B10      Free
     2 B11      Free
     2 B12      Free
     2 B13      Free
...
     3 C08      Free
     3 C09      Free
     3 C10      Free
     3 C11      Free
...
     3 C24      Free
     3 C25      Free

【讨论】:

以上是关于pl/SQL 过程从不同表中的值生成一个表中的值的主要内容,如果未能解决你的问题,请参考以下文章

基于变化表中的值在插入之前触发 PL/SQL 触发器

将新列添加到现有表中并使用 PL/SQL 中游标中的值更新它们

pl/sql 过程中的排序表失败

使用集合中的值更新表中的列

将修改后的表中的游标用于相同的 PL/SQL 过程

如何为 PL/SQL 中的 dbms 输出打印的值提供别名?