【问题标题】:SQL Correlated subquerySQL 相关子查询
【发布时间】:2017-02-12 09:08:40
【问题描述】:

我正在尝试执行此查询,但收到 ORA-00904:"QM"."MDL_MDL_ID":invalid identifier。更让我困惑的是主查询有两个子查询,它们仅在 where 子句中有所不同。但是,第一个查询运行良好,但第二个查询出错。以下是查询。

select (
        select make_description
        from make_colours@dblink1
        where makc_id = (
                select makc_makc_id
                from model_colours@dblink1
                where to_char(mdc_id) = md.allocate_vehicle_colour_id
                )
        ) as colour,
    (
        select make_description
        from make_colours@dblink1
        where makc_id = (
                select makc_makc_id
                from model_colours@dblink1
                where mdl_mdl_id = qm.mdl_mdl_id
                )
        ) as vehicle_colour
from schema1.web_order wo,
    schema1.tot_order tot,
    suppliers@dblink1 sp,
    external_accounts@dblink1 ea,
    schema1.location_contact_detail lcd,
    quotation_models@dblink1 qm,
    schema1.manage_delivery md
where wo.reference_id = tot.reference_id
    and sp.ea_c_id = ea.c_id
    and sp.ea_account_type = ea.account_type
    and sp.ea_account_code = ea.account_code
    and lcd.delivery_det_id = tot.delivery_detail_id
    and sp.sup_id = tot.dealer_id
    and wo.qmd_id = qm.qmd_id
    and wo.reference_id = md.web_reference_id(+)
    and supplier_category = 'dealer'
    and wo.order_type = 'tot'
    and trunc(wo.confirmdeliverydate - 3) = trunc(sysdate)

【问题讨论】:

  • 我可以保证它确实有那一栏。
  • 如果我只写 select qm.mdl_mdl_id as Vehicle_colour 它工作正常。
  • as vehicle_colour, 去掉这个逗号
  • @Serg :抱歉,提问时打错字了。

标签: sql oracle correlated-subquery


【解决方案1】:

Oracle 通常不能识别嵌套子查询中超过一层的表别名(或其他任何东西); from the documentation:

当嵌套子查询引用表中的列时,Oracle 执行相关子查询,该表引用了子查询上一级的父语句。 [...] 从概念上讲,对于父语句处理的每一行,都会对相关子查询进行一次评估。

注意“一级”部分。因此,在嵌套子查询中,您的 qm 别名无法被识别,因为它与 qm 别名的定义相距两个级别。 (如果你没有给它起别名,原始表名也会发生同样的事情 - 它不是专门与别名有关)。

当您将查询修改为仅包含 select qm.mdl_mdl_id as Vehicle_colour - 或它的有效版本,可能是 (select qm.mdl_mdl_id from dual) as Vehicle_colour - 您删除了嵌套,qm 现在仅比主体中的定义低一级查询,所以它被识别了。

您在第一个嵌套子查询中对md 的引用可能也不会被识别,但解析器倾向于向后工作,因此它首先看到qm 问题;尽管查询重写可能会使其有效:

但是,优化器可能会选择将查询重写为连接或使用其他技术来制定语义等价的查询。

您也可以添加提示来鼓励这样做,但最好不要依赖它。

但是你不需要嵌套子查询,你可以在每个*子查询中加入:

select (
        select mc2.make_description
        from model_colours@dblink1 mc1,
            make_colours@dblink1 mc2  
        where mc2.makc_id = mc1.makc_makc_id
        and to_char(mc1.mdc_id) = md.allocate_vehicle_colour_id
    ) as colour,
    (
        select mc2.make_description
        from model_colours@dblink1 mc1,
            make_colours@dblink1 mc2
        where mc2.makc_id = mc1.makc_makc_id
        and mc1.mdl_mdl_id = qm.mdl_mdl_id
    ) as vehicle_colour
from schema1.web_order wo,
...

我一直坚持使用旧式连接语法来匹配主查询,但您确实应该考虑使用现代 ANSI 连接语法重写整个内容。 (我还删除了@Serg 提到的流氓逗号,但您在发布问题时可能只是在您的实际选择列表中遗漏了其他列。)

您可以通过在主查询中加入品牌和型号颜色表来完全避免子查询,或者两次处理单独的过滤条件,或者一次在列表达式中加入一些逻辑。不过一次一步...

【讨论】:

  • 嗨,Alex,我确实删除了其他列,因为它们不是必需的。根据您的评论 Oracle 在多个子查询级别中无法识别表别名(或其他任何内容) 我尝试通过删除 vehicle_colour 选择来运行相同的查询,它工作正常,这就是我问这个问题的确切原因。
  • @SachinKumar - 我不确定你的意思,这就是我试图解释的。我已经扩展了答案,也许现在更清楚了?
  • 好吧,我想你误会了。我想告诉的是这个。当前查询正在选择两列,如果我运行查询以仅选择一列(即第一列)它可以工作。如果我通过注释掉第一列并运行它以仅选择第二列来执行相同操作,那么它会失败并出现我在问题中提到的错误。
  • @SachinKumar - 好的,但是我引用的第二部分文档解释了这一点!?查看单列查询的执行计划并比较过滤器和访问谓词。或者跟踪查询以查看它们是如何被优化器重写的。
  • 那么按照您的说法,可以肯定地说在一个版本的 oracle 中运行的查询可能会或可能不会在另一个版本中运行吗?