Oracle 11g中条件子查询的替代方法

时间:2018-08-23 08:32:08

标签: oracle plsql subquery

我对oracle pl / sql有了越来越多的经验,但是这个问题似乎仍然存在:我有一个将外部数据合并到数据库中看起来像这样的表中的过程:

PROCEDURE updateTable (ts DATE, val NUMBER, id NUMBER)
BEGIN
  IF id NOT IN (15, 16, 23)
  THEN 
    MERGE INTO myTable dest
    USING (SELECT ts, val, id FROM Dual) src
    ON (src.id = dest.id AND src.ts = dest.ts)
    WHEN MATCHED THEN UPDATE SET dest.val = src.val
    WHEN NOT MATCHED THEN INSERT (ts, val, id) VALUES (src.ts, src.val, src.id);
  END IF;
END;

到目前为止,它的效果还不错。现在的问题是,排除的ID列表是经过硬编码的,将它们放在另一个表中会更加动态,例如,在上面的代码中替换行

IF id NOT IN (15, 16, 23)

类似

IF id NOT IN (SELECT id FROM excluTable)

返回臭名昭著的错误: PLS_00405:在此上下文中不允许子查询 如果只是一个id,我可以简单地创建一个变量,然后在其中选择id。不幸的是,这是一个很长的清单。我试图将它们批量收集到一个数组中,但是后来也找不到将其放入条件子句中的方法。我敢肯定有一个优雅的解决方案。 感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

排除表中可能有许多ID,但是您仅将一个ID传递给过程。您可以查看表中是否存在该单个值,并在本地变量中计数,然后检查计数是零还是非零;像这样:

PROCEDURE updateTable (ts DATE, val NUMBER, id NUMBER) IS
  l_excl_id PLS_INTEGER;
BEGIN
  SELECT count(*)
  INTO l_excl_id
  FROM excluTable
  WHERE excluTable.id = updateTable.id;

  IF l_excl_id = 0
  THEN 
    MERGE INTO myTable dest
    USING (SELECT ts, val, id FROM Dual) src
    ON (src.id = dest.id AND src.ts = dest.ts)
    WHEN MATCHED THEN UPDATE SET dest.val = src.val
    WHEN NOT MATCHED THEN INSERT (ts, val, id) VALUES (src.ts, src.val, src.id);
  END IF;
END;

顺便说一句,如果您的过程参数名称与表列名称或其他标识符相同,可能会造成混淆。例如,由于id是过程参数名称和表中的列名称,因此我必须在两者前加上前缀:

  WHERE excluTable.id = updateTable.id;

一个带有表名(如果添加,则为别名),另一个带有过程名。如果你刚刚

  WHERE excluTable.id = id

然后,作用域规则将意味着它将表中的每个ID与其自身(而不是参数)进行匹配,因此您将对所有行进行计数-可能并不立即清楚为什么它的行为不符合您的预期。如果参数分别命名为p_tsp_id,那么您就不必考虑这种歧义。这也是为什么我在本地标志变量前加上l_的原因。