使用动态更新查询更新 Ledger_stat 表中的天数列的值

Posted

技术标签:

【中文标题】使用动态更新查询更新 Ledger_stat 表中的天数列的值【英文标题】:Updating day number columns' values in Ledger_stat table using dynamic update query 【发布时间】:2019-07-15 16:15:12 【问题描述】:

我有一个表 LEDGER_STAT_DLY,其中我们有一个月中每一天的列DAY_01 是第 1 天,DAY_02,....,DAY_31)。目前,我正在使用 case 语句来更新 Day 列的值,如下所示。


CASE DAY(V_DATE)
                WHEN 1 THEN
                UPDATE /*+ index(a LEDGER_STAT_DLY_IDX02_IN) */ LEDGER_STAT_DLY A
                    SET DAY_01 =  NVL(DAY_01,0) + NVL(V_AMOUNT,0)
                    WHERE IDENTITY_CODE =  NVL(V_IDENTITY_CODE,0)
                            AND YEAR_S =  NVL(V_YEAR_S,0)
                            AND MONTH_NO = NVL(V_MONTH,0)
                            AND CONSOLIDATION_CD =  NVL(V_CONSOLIDATION_CD,0)
                            AND FINANCIAL_ELEM_ID = NVL(V_FINANCIAL_ELEM_ID,0)
                            AND ORG_UNIT_ID   = NVL(V_ORG_UNIT_ID,0)
                            AND GL_ACCOUNT_ID = NVL(V_GL_ACCOUNT_ID,0)
                            AND COMMON_COA_ID = NVL(V_COMMON_COA_ID,0)
                            AND PRODUCT_1_ID  = NVL(V_PRODUCT_1_ID,0)
                            AND PRODUCT_ID    = NVL(V_PRODUCT_ID,0)
                            AND PRODUCT_3_ID  = NVL(V_PRODUCT_3_ID,0)
                            AND COST_TYPE_ID    = NVL(V_MEMO_GL_ACCOUNT_ID,0)
                            AND BALANCE_TYPE_CD = NVL(V_BALANCE_TYPE_CD,0);

                WHEN 2 THEN
                UPDATE /*+ index(a LEDGER_STAT_DLY_IDX02_IN) */ LEDGER_STAT_DLY A
                    SET DAY_02 =  NVL(DAY_02,0) + NVL(V_AMOUNT,0)
                    WHERE IDENTITY_CODE =  NVL(V_IDENTITY_CODE,0)
                            AND YEAR_S =  NVL(V_YEAR_S,0)
                            AND MONTH_NO = NVL(V_MONTH,0)
                            AND CONSOLIDATION_CD =  NVL(V_CONSOLIDATION_CD,0)
                            AND FINANCIAL_ELEM_ID = NVL(V_FINANCIAL_ELEM_ID,0)
                            AND ORG_UNIT_ID   = NVL(V_ORG_UNIT_ID,0)
                            AND GL_ACCOUNT_ID = NVL(V_GL_ACCOUNT_ID,0)
                            AND COMMON_COA_ID = NVL(V_COMMON_COA_ID,0)
                            AND PRODUCT_1_ID  = NVL(V_PRODUCT_1_ID,0)
                            AND PRODUCT_ID    = NVL(V_PRODUCT_ID,0)
                            AND PRODUCT_3_ID  = NVL(V_PRODUCT_3_ID,0)
                            AND COST_TYPE_ID    = NVL(V_MEMO_GL_ACCOUNT_ID,0)
                            AND BALANCE_TYPE_CD = NVL(V_BALANCE_TYPE_CD,0);

-- and so forth, I have written 31 blocks

虽然运行良好,但程序中的代码超长。您能否建议我如何使用动态查询对其进行更新,以便此代码减少到更少的行/单个块,而不是 31 个单独的案例语句。 提前感谢您的帮助!

PS:使用day() 函数从V_DATE 中提取日期。逻辑是,每当day(v_date) 匹配LEDGER_STAT_DLY 表的day_number(01, 02,03...) 列时,相应地更新该列的值。

【问题讨论】:

【参考方案1】:

我同意 Bob Jarvis 的回答中表达的观点,但如果重构基础表不是您的选择,则可以使用动态 SQL 来完成。

在您提供的示例中,看起来有一个变量(DAY_01/DAY_02/DAY_03... 字段)需要在两个地方使用。所以这个字段需要替换到静态语句的其余部分。

下面是一个例子。

首先,添加一个变量来保存目标字段名称。 (这并不是真正需要的,只是为了便于阅读)。

由于在现有的第 1、2、3 天数和字段DAY_01DAY_02DAY_03 之间存在有序转换,这可以在直接赋值中确定。

然后在当前正在使用开关的过程主体中,您可以使用EXECUTE IMMEDIATE,将语句中的列名替换(通过连接或UTL_LMS之类的字符串格式化程序)用目标DAY_01 ,DAY_02 等,其中列名在语句中。

CREATE OR REPLACE PROCEDURE UPDATE_LEDGER_STAT_DLY(V_IDENTITY_CODE NUMBER,
                                                   V_CONSOLIDATION_CD NUMBER,
                                                   V_FINANCIAL_ELEM_ID NUMBER,
                                                   V_ORG_UNIT_ID NUMBER,
                                                   V_GL_ACCOUNT_ID NUMBER,
                                                   V_COMMON_COA_ID NUMBER,
                                                   V_PRODUCT_1_ID NUMBER,
                                                   V_PRODUCT_ID NUMBER,
                                                   V_PRODUCT_3_ID NUMBER,
                                                   V_DATE DATE,
                                                   V_AMOUNT NUMBER,
                                                   V_MEMO_GL_ACCOUNT_ID NUMBER DEFAULT 0,
                                                   V_POSTINGTYPE CHAR DEFAULT 'N',
                                                   V_BALANCE_TYPE_CD NUMBER DEFAULT 0)
    IS

    V_CNT    NUMBER;
    V_D      NUMBER;
    V_DAY    CHAR(6);
    V_MONTH  CHAR(2);
    V_MO     NUMBER;
    V_YEAR_S NUMBER;

BEGIN

    IF V_POSTINGTYPE = 'N' THEN

        IF NVL(V_AMOUNT, 0) <> 0 THEN

            V_MO := (MONTH(V_DATE));
            V_MONTH := LPAD(V_MO, 2, '0');
            V_YEAR_S := (YEAR(V_DATE));
            V_D := (DAY(V_DATE));
            V_DAY := 'DAY_' || lpad(V_D, 2, '0');


            EXECUTE IMMEDIATE UTL_LMS.FORMAT_MESSAGE('UPDATE /*+ index(a LEDGER_STAT_DLY_IDX02_IN) */ LEDGER_STAT_DLY A
                           SET %s =  NVL(%s,0) + NVL(:THE_AMOUNT,0)
                           WHERE IDENTITY_CODE =  NVL(:THE_IDENTITY_CODE,0)
                              AND YEAR_S =  NVL(:THE_YEAR_S,0)
                              AND MONTH_NO = NVL(:THE_MONTH,0)
                              AND CONSOLIDATION_CD =  NVL(:THE_CONSOLIDATION_CD,0)
                              AND FINANCIAL_ELEM_ID = NVL(:THE_FINANCIAL_ELEM_ID,0)
                              AND ORG_UNIT_ID   = NVL(:ORG_UNIT_ID,0)
                              AND GL_ACCOUNT_ID = NVL(:THE_GL_ACCOUNT_ID,0)
                              AND COMMON_COA_ID = NVL(:THE_COMMON_COA_ID,0)
                              AND PRODUCT_1_ID  = NVL(:THE_PRODUCT_1_ID,0)
                              AND PRODUCT_ID    = NVL(:THE_PRODUCT_ID,0)
                              AND PRODUCT_3_ID  = NVL(:THE_PRODUCT_3_ID,0)
                              AND COST_TYPE_ID    = NVL(:THE_MEMO_GL_ACCOUNT_ID,0)
                              AND BALANCE_TYPE_CD = NVL(:THE_BALANCE_TYPE_CD,0)', V_DAY, V_DAY)
                USING V_AMOUNT, V_IDENTITY_CODE, V_YEAR_S, V_MONTH, V_CONSOLIDATION_CD,
                V_FINANCIAL_ELEM_ID, V_ORG_UNIT_ID, V_GL_ACCOUNT_ID,
                V_COMMON_COA_ID, V_PRODUCT_1_ID, V_PRODUCT_ID, V_PRODUCT_3_ID, V_MEMO_GL_ACCOUNT_ID, V_BALANCE_TYPE_CD;

        END IF;

    END IF;

END;
/

【讨论】:

我收到错误 SQL 命令未正确结束错误。您能否确认/发现错误,如果有的话。我也在测试它。 谢谢@SachinSharma。这可能是动态语句中的尾随分号(我在示例中的错误剪切/粘贴)。它已在此处的示例中删除。你能告诉我这是否可以为你纠正错误吗? 现在抛出 'V_BALANCE_TYPE_CD' is invalid identifier 错误。我在过程中传递它,然后在这个查询中使用它。作为参考,我在这里发布了完整的过程https://***.com/questions/57045175/ora-01747-invalid-user-table-column-table-column-or-column-specification-erro 谢谢@SachinSharma。接受的答案给我的印象是,您决定按照 Bob Jarvis 的建议进行重构并在这里搁置程序?如果没有活动的问题,我会避免进一步的故障排除。这仍然是一个持续存在的问题吗? @SachinSharma,好的,这里的示例已与其他链接过程一样更新(尽管我没有包括其他插入等)。这是否解决了您现在看到的问题?谢谢【参考方案2】:

很抱歉,但最好的办法是规范化此表,去掉重复的DAY_01DAY_02 等字段。这将使您的工作变得非常简单。

显然你的桌子目前看起来像

CREATE TABLE LEDGER_STAT_DLY 
  (IDENTITY_CODE      NUMBER,
   YEAR_S             NUMBER,
   MONTH_NO           NUMBER,
   CONSOLIDATION_CD   NUMBER,
   FINANCIAL_ELEM_ID  NUMBER,
   ORG_UNIT_ID        NUMBER,
   GL_ACCOUNT_ID      NUMBER,
   COMMON_COA_ID      NUMBER,
   PRODUCT_ID         NUMBER,
   PRODUCT_1_ID       NUMBER,
   PRODUCT_3_ID       NUMBER,
   COST_TYPE_ID       NUMBER,
   BALANCE_TYPE_CD    NUMBER,
   DAY_01             NUMBER,
   DAY_02             NUMBER,
   ...
   DAY_31             NUMBER);

我建议将其替换为

CREATE TABLE LEDGER_STAT_DLY 
  (IDENTITY_CODE      NUMBER,
   YEAR_S             NUMBER,
   MONTH_NO           NUMBER,
   CONSOLIDATION_CD   NUMBER,
   FINANCIAL_ELEM_ID  NUMBER,
   ORG_UNIT_ID        NUMBER,
   GL_ACCOUNT_ID      NUMBER,
   COMMON_COA_ID      NUMBER,
   PRODUCT_ID         NUMBER,
   PRODUCT_1_ID       NUMBER,
   PRODUCT_3_ID       NUMBER,
   COST_TYPE_ID       NUMBER,
   BALANCE_TYPE_CD    NUMBER,
   DAY_NUMBER         NUMBER,
   DAY_VALUE          NUMBER);

DAY_NUMBER 成为月份中的第几天,所以它的值是 1 - 31。DAY_VALUE 被分配了过去进入 DAY_01、DAY_02 等的值。如果你考虑所有的地方在您编写重复代码块来处理所有单独的 DAY_xx 字段的地方,您会意识到这个标准化结构将变得多么容易处理。

【讨论】:

以上是关于使用动态更新查询更新 Ledger_stat 表中的天数列的值的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Graphql 动态更新一个表中的多行

从红移表中获取上次更新时间戳

如何使用填充旧表中的行的动态数据动态更新新MySQL表中的行?

将表单值传递给更新查询而不更新表中的空白字段

使用子查询使用同一表中的值更新表

如何使用计数查询来(自动)更新表中的列