【问题标题】:Flyway: create view as WITH (common table expression CTE)Flyway:创建视图为 WITH(公用表表达式 CTE)
【发布时间】:2019-12-09 00:38:09
【问题描述】:

如何克服这个错误?

Java version: 1.8.0_131, vendor: Oracle Corporation, runtime: C:\Program Files\Java\jdk1.8.0_131\jre    
[DEBUG]    com.oracle:ojdbc8:jar:12.2.0.1.0:provided    
[INFO] Flyway Community Edition 5.2.4 by Boxfuse
[INFO] Database: jdbc:oracle:thin:@bdlg3400.na.pg.com:1525:ioptd101 (Oracle 12.2)
[DEBUG] Driver  : Oracle JDBC driver 12.2.0.1.0


[ERROR] Migration R__SOME_VIEW_VW.sql failed
[ERROR] --------------------------------------
[ERROR] SQL State  : 42000
[ERROR] Error Code : 933
[ERROR] Message    : ORA-00933: (non-english description)
[ERROR] Location   : sql\Views\R__SOME_VIEW_VW.sql (...\sql\Views\R__SOME_VIEW_VW.sql)
[ERROR] Line       : 7
[ERROR] Statement  : CREATE OR REPLACE VIEW some_view_vw as
[ERROR] WITH
[ERROR] abc AS
[ERROR] (
[ERROR]     SELECT
[ERROR]         iglp.p_skid,
[ERROR]         LISTAGG(g.g_code, ',') WITHIN GROUP (ORDER BY g.g_code) AS lokd_gate_lst
[ERROR]     FROM ig_l_prod iglp
[ERROR]     JOIN ig_prc ig ON ig.ig_skid = iglp.ig_skid
[ERROR]     JOIN g g ON g.g_skid = ig.g_skid
[ERROR]     WHERE iglp.lock_ind = 'Y'
[ERROR]     GROUP BY
[ERROR]         iglp.p_skid
[ERROR] )
[ERROR] SELECT
[ERROR]     pr.p_skid AS scr_prod_skid,
[ERROR]     lg.lokd_gate_lst,
[ERROR]     pr.*
[ERROR] FROM p pr
[ERROR] LEFT JOIN lokd_gate lg ON lg.p_skid = pr.p_skid
[ERROR] where exists(select 1 from PP_PRC pipo WHERE pipo.PI_P_SKID = pr.P_SKID);
[ERROR]
[ERROR] -> [Help 1]

当我将 WITH 子句作为子查询移动到 FROM 子句时,脚本成功。但是这样重构可能会导致其他视图效率低下。

【问题讨论】:

  • 在视图中使用 CTE 是有效的。您确定您的整体陈述中没有其他错误/遗漏吗?如果您通过 SQL*Plus 或其他客户端运行相同的脚本,它会起作用吗?仅从错误消息中的这 4 行很难诊断。发布完整视图可能不可取,但您可以创建一个minimal reproducible example 来演示该问题吗? (也不确定为什么您认为它作为子查询的效率会降低。)
  • @AlexPoole 我们在项目中有很多视图,其中许多使用了多个 CTE。
  • 我的猜测是,Flyway 解析该 SQL 脚本不正确,并且只将其中的一部分发送到服务器。你能告诉 Flyway “按原样”使用 SQL 脚本吗?我不知道 Flyway,但例如Liquibase 对这类事情有一个“splitStatements”属性,以防止工具不期望的 SQL 语法出现任何问题
  • 我会怀疑日志中语句末尾显示的分号。那是一个语句分隔符(并由客户端解释),而不是语句的一部分。它会在 JDBC 和动态 SQL 调用等中导致此类错误。删除该分号是否可以解决问题? (作为脚本处理的一部分,Flyway 可能会很好,当然;我也不使用它......)
  • @AlexPoole,您有使用 CTE 的脚本(创建视图)示例吗?

标签: oracle flyway


【解决方案1】:

根本原因在于 Flyway 的 Oracle 解析器:

OracleParser.java

private static final Pattern PLSQL_VIEW_REGEX = Pattern.compile(
        "^CREATE(\\sOR\\sREPLACE)?(\\s(NON)?EDITIONABLE)?\\sVIEW\\s.*\\sAS\\sWITH\\s(PROCEDURE|FUNCTION)");
private static final StatementType PLSQL_VIEW_STATEMENT = new StatementType();

【讨论】:

【解决方案2】:

错误消息是ORA-00933 sql command not properly ended,这可能意味着您的查询中有一些禁止或冲突的子句,但根据我的经验,这通常意味着某处缺少逗号或有错字。

首先是一些虚拟表:

create table ig_l_prod(
  p_skid number,
  ig_skid number,
  lock_ind varchar2(1)
);
create table ig_prc(
  ig_skid number,
  g_skid number
);
create table g(
  g_skid number,
  g_code varchar2(1)
);
create table p(
  p_skid number,
  name varchar2(10)
);
create table PP_PRC(
PI_P_SKID number
);

这是来自您的日志的查询,有一处修改:

CREATE OR REPLACE VIEW some_view_vw as
WITH
lokd_gate AS -- *** Replaced "abc" with "lokd_gate" ***
(
    SELECT
        iglp.p_skid,
        LISTAGG(g.g_code, ',') WITHIN GROUP (ORDER BY g.g_code) AS lokd_gate_lst
    FROM ig_l_prod iglp
    JOIN ig_prc ig ON ig.ig_skid = iglp.ig_skid
    JOIN g g ON g.g_skid = ig.g_skid
    WHERE iglp.lock_ind = 'Y'
    GROUP BY
        iglp.p_skid
)
SELECT
    pr.p_skid AS scr_prod_skid,
    lg.lokd_gate_lst,
    pr.*
FROM p pr
LEFT JOIN lokd_gate lg ON lg.p_skid = pr.p_skid
where exists(select 1 from PP_PRC pipo WHERE pipo.PI_P_SKID = pr.P_SKID);

CTE 别名为abc,而连接是lokd_gate——这可能只是清理要在此处发布的视图的人工制品,但如果它在实际 SQL 中,则查询有问题.

使用虚拟表,此 SQL 在 Oracle 12c 中没有错误地执行。

尝试从 SQL*Plus 执行如上所示的 SQL,看看它是否有效。如果是这样,那么这是框架没有很好地解析 CTE 的错误(并非闻所未闻)。

【讨论】:

  • Tad,感谢您的回答,但我在准备问题时忘记在 FROM 子句中更改 lokd_gate 的名称。那不是问题。这个声明在我们的项目中,我们在每次迁移时执行,没有问题。
  • 那么最后的说法是正确的:“这是框架没有很好地解析CTE的错误”,看来您已经找到了证据。这很烦人,但不是第一次工具未能正确接受完全合法的 WITH 子句。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-25
  • 1970-01-01
  • 1970-01-01
  • 2011-06-12
相关资源
最近更新 更多