将 Informix 查询转换为 Oracle?

Posted

技术标签:

【中文标题】将 Informix 查询转换为 Oracle?【英文标题】:Transforming an Informix query to Oracle? 【发布时间】:2019-08-26 09:53:03 【问题描述】:

我有一个不起作用的查询;你能帮我转换一下吗?

我要转换为 Oracle 的原始 Informix 查询。

SELECT DISTINCT table3.no_cev,   
         table1.literal,   
         table1.colid,   
         table2.repid,   
         table2.valor,   
         table2.indicador,   
         '',   
         '',   
         table2.origen,   
         table2.codi,  
         table2.no_cia,
         table2.num_dcca,
         table2.no_aprof,
         table2.no_compta
    FROM table1,   
         OUTER table2,   
         table3  
   WHERE ( table1.colid  = table2.colid) and  
         ( table1.grupid  = table2.grupid) and  
         ( table3.no_cev  = table2.no_cev) and  
         ( ( table1.grupid = 2) AND  
         ( table2.cod_exp = 99609 ) AND  
         ( table2.indicador = 'S' ) ) AND  
         ( table3.num_dcca = 1); 
         ( table3.codest = 76695);

我将查询从 Informix 转换为 Oracle — 但它看起来不起作用:

SELECT DISTINCT table3.no_cev,
  table1.literal,
  table1.colid,
  table2.repid,
  table2.valor,
  table2.indicador,
  '',
  '',
  table2.origen,
  table2.codi,
  table2.no_cia,
  table2.num_dcca,
  table2.no_aprof,
  table2.no_compta
FROM table1
LEFT OUTER JOIN (table2
RIGHT OUTER JOIN table3
ON table3.no_cev        = table2.no_cev)
ON (( table1.colid      = table2.colid)
AND ( table1.grupid     = table2.grupid))
WHERE ( ( table1.grupid = '2' )
AND ( table2.cod_exp    = '99609' )
AND ( table2.indicador  = 'S' ) )
AND ( table3.num_dcca   = '1')
AND ( table3.codest     = '76695');

【问题讨论】:

OT:忘记将其重写为“Oracle”。将其重写为 ISO SQL - Oracle 也支持。 您的原始查询以 ( table3.num_dcca = 1);( table3.codest = 76695); 行结束。尚不清楚是否应将第一个分号替换为 AND 或是否应删除 table3.codest 上的条件。您应该省略 SELECT 数据中的两个空/空字段;您还可以从table2 中删除一些列——被删除的候选包括查询中未以其他方式命名的所有列,例如repidvalororigencodino_ciano_aprofno_compta。保留其中之一;你真的不需要更多。大纲模式也会有所帮助。 Informix 旧式 OUTER 连接是一种复杂的生物,不一定能简单地转换为现代标准 SQL(因此转换为 Oracle 等)。您首先应该致力于在 Informix 中使用 INNER JOIN 和 OUTER JOIN 来复制修改后的查询(假设您没有使用不支持显式 OUTER JOIN 等的旧版 Informix)。 WHERE ( ( table1.grupid = '2' ) AND ( table2.cod_exp = '99609' ) AND ( table2.indicador = 'S' ) ) 中额外的括号级别不会改变查询 - 它们可以被删除。 为什么 Informix 的 WHERE 子句中的值在数字周围不带引号 (( ( table1.grupid = 2) AND ( table2.cod_exp = 99609 ) AND ( table2.indicador = 'S' ) ) AND ( table3.num_dcca = 1); ( table3.codest = 76695);),而在 Oracle 重写 (WHERE ( ( table1.grupid = '2' ) AND ( table2.cod_exp = '99609' ) AND ( table2.indicador = 'S' ) ) AND ( table3.num_dcca = '1') AND ( table3.codest = '76695');) 中则用引号括起来?这令人困惑;列的类型是什么? 样本数据和样本数据的预期结果也会有所帮助。 【参考方案1】:

您在代码中的错误位置加入了带有 ON 子句的表。

现在将您的代码更正如下:

SELECT DISTINCT
    TABLE3.NO_CEV,
    TABLE1.LITERAL,
    TABLE1.COLID,
    TABLE2.REPID,
    TABLE2.VALOR,
    TABLE2.INDICADOR,
    '',
    '',
    TABLE2.ORIGEN,
    TABLE2.CODI,
    TABLE2.NO_CIA,
    TABLE2.NUM_DCCA,
    TABLE2.NO_APROF,
    TABLE2.NO_COMPTA
FROM
    TABLE1
    LEFT OUTER JOIN 
    -- ( -- removed this bracket 
     TABLE2 ON ( ( TABLE1.COLID = TABLE2.COLID )
                                AND ( TABLE1.GRUPID = TABLE2.GRUPID ) ) -- added this ON here
    RIGHT OUTER JOIN TABLE3 ON TABLE3.NO_CEV = TABLE2.NO_CEV 
    -- ) -- removed this bracket
WHERE
     TABLE1.GRUPID = '2' 
    AND  TABLE2.COD_EXP = '99609' 
    AND  TABLE2.INDICADOR = 'S' 
    AND  TABLE3.NUM_DCCA = '1' 
    AND  TABLE3.CODEST = '76695' ; -- no need of extra brackets

干杯!!

【讨论】:

您熟悉旧 Informix 样式的 OUTER 联接的行为吗?它们不同于您遇到的任何其他人(如果您遇到过它们)。请参阅我的回答,了解原始 Informix 查询的结果是什么,以及如何在标准 SQL 中模拟它。 酷。我不知道这一点。感谢您的信息【参考方案2】:

如果您没有为查询中使用的表、一些示例数据和预期结果包含或多或少的最小大纲架构,那么对于希望帮助您的人来说,这会带来不必要的困难。此外,您似乎已将原始 Informix 查询中的数字(整数)转换为 Oracle 查询中的字符串。目前尚不清楚为什么。同样,模式将有助于解释发生了什么。

正如我在 cmets 中所指出的,您应该省略选择列表中的两个空/null 字段;您还可以从table2 中删除一些列——被删除的候选包括查询中未以其他方式命名的所有列,例如repidvalororigencodino_ciano_aprofno_compta。保留其中一两个;你真的不需要更多。但是,我保留了示例数据中的所有命名列。

架构和数据

这里有一些 Informix SQL,似乎与问题中显示的查询中的表和列相匹配。如有疑问,该列被制成 INTEGER 列。所有列都用 NOT NULL 限定。

DROP TABLE IF EXISTS table1;
DROP TABLE IF EXISTS table2;
DROP TABLE IF EXISTS table3;

CREATE TABLE table1
(
    grupid      INTEGER NOT NULL,       -- 2
    literal     VARCHAR(32) NOT NULL,
    colid       INTEGER NOT NULL
);

CREATE TABLE table2
(
    grupid      INTEGER NOT NULL,
    no_cev      INTEGER NOT NULL,
    colid       INTEGER NOT NULL,
    repid       INTEGER NOT NULL,
    valor       INTEGER NOT NULL,
    indicador   CHAR(1) NOT NULL,       -- 'S'
    origen      INTEGER NOT NULL,
    codi        INTEGER NOT NULL,
    no_cia      INTEGER NOT NULL,
    num_dcca    INTEGER NOT NULL,
    no_aprof    INTEGER NOT NULL,
    no_compta   INTEGER NOT NULL,
    cod_exp     INTEGER NOT NULL        -- 99609
);

CREATE TABLE table3
(
    no_cev      INTEGER NOT NULL,
    num_dcca    INTEGER NOT NULL,       -- 1
    codest      INTEGER NOT NULL        -- 76695
);

LOAD FROM "table1.unl" INSERT INTO table1;
LOAD FROM "table2.unl" INSERT INTO table2;
LOAD FROM "table3.unl" INSERT INTO table3;

注解表示在查询中为该列指定的值;他们帮助指导样本数据的构建。

Informix(管道分隔值)UNLOAD 格式的三个示例数据文件是:

table1.unl

2|Literal value 1|100
2|Literal value 2|123
2|Literal value 3|134
2|Literal value 4|145

table2.unl

2|2345|100|222|333|S|444|555|666|777|888|999|99609
2|2346|123|223|333|S|444|555|666|776|888|999|99609
2|2347|134|224|333|S|444|555|666|775|888|999|99609
2|2348|145|225|333|S|444|555|666|774|888|999|99609
1|2345|100|225|333|S|444|555|666|773|888|999|99609
2|2340|123|226|333|S|444|555|666|772|888|999|99609
3|2347|134|227|333|S|444|555|666|771|888|999|99609
2|2350|145|228|333|S|444|555|666|770|888|999|99609

table3.unl

2345|1|76695
2346|1|88776
2347|2|76695
2348|1|76695

使用 Informix 样式的 OUTER 连接的查询结果

假设原始查询中的杂散早期分号应该是 AND(与建议的 Oracle 查询中写入的内容匹配),删除两个空字符串结果列,并删除多余的括号级别,然后是原始查询看起来像:

SELECT DISTINCT
       table3.no_cev,   
       table1.literal,   
       table1.colid,   
       table2.repid,   
       table2.valor,   
       table2.indicador,   
       table2.origen,   
       table2.codi,  
       table2.no_cia,
       table2.num_dcca,
       table2.no_aprof,
       table2.no_compta
  FROM table1,   
       OUTER table2,   
       table3  
 WHERE (table1.colid  = table2.colid) AND  
       (table1.grupid  = table2.grupid) AND  
       (table3.no_cev  = table2.no_cev) AND  
       (table1.grupid = 2) AND  
       (table2.cod_exp = 99609) AND  
       (table2.indicador = 'S') AND  
       (table3.num_dcca = 1)    AND
       (table3.codest = 76695);

在显示的示例数据中,使用在装有 macOS 10.14.6 Mojave 的 MacBook Pro 上运行的 Informix 12.10.FC6(并不是说操作系统可能是结果中的一个因素),这会产生:

2345|Literal value 1|100|222|333|S|444|555|666|777|888|999
2345|Literal value 2|123|||||||||
2345|Literal value 3|134|||||||||
2345|Literal value 4|145|||||||||
2348|Literal value 1|100|||||||||
2348|Literal value 2|123|||||||||
2348|Literal value 3|134|||||||||
2348|Literal value 4|145|225|333|S|444|555|666|774|888|999

你问为什么?好问题! Informix 旧式 OUTER 连接是一种复杂的生物,不一定能简单地转换为现代标准 SQL(因此转换为 Oracle 等)。你可以在Complex Outer Joins找到一些关于它的工作方式的描述。

有两组表——table1table3 是主要表,table2 是这里唯一的 OUTER 表。这意味着 Informix 使用内连接处理 table1table3,然后使用 table2 外连接结果。由于table1table3 之间没有直接连接,因此结果是两个表的笛卡尔积——table1 中的 4 行中的每一行都与 table3 中的 4 行中的每一行连接,产生 16行。但是,过滤条件会删除来自table3 的行,其中no_cev 是2346 和2347。无论外连接操作的结果如何,其余8 行都将被保留。现在这些行与table2 外部连接。 (no_cev, colid) 为 (2345, 100) 和 (2348, 145) 的行在 table3 中具有匹配的行,其中数据满足 WHERE 子句中的条件。其他行没有这样的匹配行,因此来自table2 的这些行的列是“全部为空”。正如我所说,这很奇怪——扭曲了。解释是一项艰苦的工作!

使用标准 SQL 的第一个近似值

此查询是对 Informix 查询的直接翻译的适度近似:

    SELECT DISTINCT
           t3.no_cev,   
           t1.literal,   
           t1.colid,   
           t2.repid,   
           t2.valor,   
           t2.indicador,   
           t2.origen,   
           t2.codi,  
           t2.no_cia,
           t2.num_dcca,
           t2.no_aprof,
           t2.no_compta
      FROM      table1 AS t1
     INNER JOIN table3 AS t3 ON 1 = 1
      LEFT JOIN table2 AS t2 ON t3.no_cev = t2.no_cev
                            AND t1.colid = t2.colid
                            AND t1.grupid = t2.grupid
     WHERE t1.grupid = 2
       AND t2.cod_exp = 99609
       AND t2.indicador = 'S'
       AND t3.num_dcca = 1
       AND t3.codest = 76695;

输出是:

2345|Literal value 1|100|222|333|S|444|555|666|777|888|999
2348|Literal value 4|145|225|333|S|444|555|666|774|888|999

这缺少具有“空值”的行。

使用标准的 INNER 和 OUTER 连接实现相同的结果

我们可以通过查找table2 中的一列为空的行来收集这些行(因为它们要么全为空,要么不为空——因为这些列被限定为非空):

SELECT DISTINCT
       t3.no_cev,   
       t1.literal,   
       t1.colid,   
       t2.repid,   
       t2.valor,   
       t2.indicador,   
       t2.origen,   
       t2.codi,  
       t2.no_cia,
       t2.num_dcca,
       t2.no_aprof,
       t2.no_compta
  FROM      table1 AS t1
 INNER JOIN table3 AS t3 ON 1 = 1
  LEFT JOIN table2 AS t2 ON t3.no_cev = t2.no_cev
                        AND t1.colid = t2.colid
                        AND t1.grupid = t2.grupid
 WHERE t1.grupid = 2
   AND ((t2.cod_exp = 99609 AND t2.indicador = 'S') OR t2.cod_exp IS NULL)
   AND t3.num_dcca = 1
   AND t3.codest = 76695;

这会产生输出:

2345|Literal value 1|100|222|333|S|444|555|666|777|888|999
2345|Literal value 2|123|||||||||
2345|Literal value 3|134|||||||||
2345|Literal value 4|145|||||||||
2348|Literal value 1|100|||||||||
2348|Literal value 2|123|||||||||
2348|Literal value 3|134|||||||||
2348|Literal value 4|145|225|333|S|444|555|666|774|888|999

这与原始的旧式 Informix OUTER 连接查询相同。

Tejash 提出的解决方案

Tejash 的 answer (revision 1) 中的 SQL 在相同的数据上产生:

2345|Literal value 1|100|222|333|S|\ |\ |444|555|666|777|888|999
2348|Literal value 4|145|225|333|S|\ |\ |444|555|666|774|888|999

反斜杠空格值对应于空字符串——这是 Informix 编码零长度非空字符串的一种稍微特殊的方式。在这个领域,Oracle 的行为可能会略有不同,但与查询问题无关。

显然,这与 Informix 查询的结果不同。这可能更合理;它开箱即用(我只是复制'n'paste,引用数字和所有内容,并且无需编辑)。

【讨论】:

【参考方案3】:

我不知道 Informix OUTER 语法,所以我的回答可能是错误的。但是,WHERE 子句在 table1 和 table3 之间缺少任何关系,这表明这只是 table1 和 table3 的交叉连接,然后是 table2 的外部连接。

一种写法:

select t3.no_cev, t1.literal, t1.colid, t2.*
from table1 t1
cross join table3 t3
left join table2 t2 on  t2.colid = t1.colid
                    and t2.grupid = t1.grupid
                    and t2.no_cev = t3.no_cev
                    and t2.cod_exp = 2
                    and t2.indicador = 'S'
where t1.grupid = 2
and t3.num_dcca = 1
and t3.codest = 76695;

另一个是:

with t1 as (select * from table1 where grupid = 2)
   , t2 as (select * from table1 where grupid = 2 and cod_exp = 2 and indicador = 'S')
   , t3 as (select * from table3 where num_dcca = 1 and codest = 76695)
select t3.no_cev, t1.literal, t1.colid, t2.*
from t1
cross join t3
left join t2 on t2.colid = t1.colid and t2.no_cev = t3.no_cev;

以上查询是标准 SQL,我认为从 9i 版开始受到 Oracle 的支持。

【讨论】:

以上是关于将 Informix 查询转换为 Oracle?的主要内容,如果未能解决你的问题,请参考以下文章

如何将此 Informix 嵌套联接转换为 tsql 嵌套联接?

Informix `interval` 函数的 Oracle 等效项是啥?

Oracle 中的连接表(多个外连接)

informix常用函数

informix常用函数

将 Informix ESQLC 迁移到 Oracle Pro*C