【问题标题】:Multi-join 'gotohell' update多连接“gotohell”更新
【发布时间】:2026-01-12 22:50:01
【问题描述】:

我有这个返回简单表的选择查询 - 见下文。

我花了一整天的时间试图写一个更新语句,当 EQLOC 为 NULL 或与 HOOKNO 不同时,它应该用 HOOKNO 更新......但我失败了。

请帮忙。

SELECT ho.HOOK_OFFICE_LETTER || LPAD(khc.HOOK_NO, 5, '0') as hookno, eq.location_description as eqloc
FROM KEY_HOOK_CURRENT khc 
    INNER JOIN KEY_HOLDING kho                ON khc.KEY_HOLDINGOID = kho.KEY_HOLDINGOID
    INNER JOIN CONTRACT con                   ON kho.CUSTOMEROID = con.CUSTOMEROID
    INNER JOIN VW_CURRENT_CONTRACT_PERIOD ccp ON con.CONTRACT_ID = ccp.CONTRACT_ID
    INNER JOIN CONTRACT_PERIOD cp             ON (ccp.CONTRACT_ID = cp.CONTRACT_ID AND ccp.CONTRACT_PERIOD = cp.CONTRACT_PERIOD)
    INNER JOIN CONTRACT_EQUIP_PERIOD cep      ON cp.CONTRACT_PERIODOID = cep.CONTRACT_PERIODOID
    INNER JOIN EQUIPMENT eq                   ON cep.EQUIPMENTOID = eq.EQUIPMENTOID
    INNER JOIN HOOK_OFFICE ho                 ON khc.HOOK_OFFICEOID = ho.HOOK_OFFICEOID
WHERE
    eq.PRODUCT_ID = 'XXX' AND
    (eq.LOCATION_DESCRIPTION IS NULL OR
    ho.HOOK_OFFICE_LETTER || LPAD(khc.HOOK_NO, 5, '0') <> eq.LOCATION_DESCRIPTION)

查询返回如下:

HOOKNO  EQLOC
G00754  (null)
L02860  (null)
L04052  L12345
L01126  (null)
L01348  (null)
L01950  L56789
L00857  (null)
L04651  (null)
L03762  (null)

试了一下

UPDATE (SELECT ho.HOOK_OFFICE_LETTER || LPAD(khc.HOOK_NO, 5, '0') AS hookey, eq.LOCATION_DESCRIPTION AS eqloc
        FROM KEY_HOOK_CURRENT khc 
            INNER JOIN KEY_HOLDING kho                ON khc.KEY_HOLDINGOID = kho.KEY_HOLDINGOID
            INNER JOIN CONTRACT con                   ON kho.CUSTOMEROID = con.CUSTOMEROID
            INNER JOIN VW_CURRENT_CONTRACT_PERIOD ccp ON con.CONTRACT_ID = ccp.CONTRACT_ID
            INNER JOIN CONTRACT_PERIOD cp             ON (ccp.CONTRACT_ID = cp.CONTRACT_ID AND ccp.CONTRACT_PERIOD = cp.CONTRACT_PERIOD)
            INNER JOIN CONTRACT_EQUIP_PERIOD cep      ON cp.CONTRACT_PERIODOID = cep.CONTRACT_PERIODOID
            INNER JOIN EQUIPMENT eq                   ON cep.EQUIPMENTOID = eq.EQUIPMENTOID
            INNER JOIN HOOK_OFFICE ho                 ON khc.HOOK_OFFICEOID = ho.HOOK_OFFICEOID
        WHERE
        eq.PRODUCT_ID = 'XXX' AND
        (eq.LOCATION_DESCRIPTION IS NULL OR (ho.HOOK_OFFICE_LETTER || LPAD(khc.HOOK_NO, 5, '0')) <> eq.LOCATION_DESCRIPTION))
SET hookno = eqloc

     but getting 01733 - "virtual column not allowed"

...以及许多其他查询,包括合并,但没有太多经验,所以运气不好! :(

请注意:此查询将每 10 分钟 24/7 运行一次,因此它应该非常快(如果可能)并且仅在必要时更新。

您的帮助将不胜感激。

谢谢!

【问题讨论】:

  • 这里可以使用 MERGE 语句,但是您需要某种方法来确定设备表中的哪些行需要更新。设备表的主键是什么?

标签: sql oracle sql-update inner-join


【解决方案1】:

假设设备表的主键是deviceoid,我们可以创建MERGE语句如下:

merge into equipment tgt
using (select eq.equipmentoid,
              ho.hook_office_letter || lpad(khc.hook_no, 5, '0') as hookno,
              eq.location_description as eqloc
       from key_hook_current khc 
           inner join key_holding kho                on khc.key_holdingoid = kho.key_holdingoid
           inner join contract con                   on kho.customeroid = con.customeroid
           inner join vw_current_contract_period ccp on con.contract_id = ccp.contract_id
           inner join contract_period cp             on (ccp.contract_id = cp.contract_id and ccp.contract_period = cp.contract_period)
           inner join contract_equip_period cep      on cp.contract_periodoid = cep.contract_periodoid
           inner join equipment eq                   on cep.equipmentoid = eq.equipmentoid
           inner join hook_office ho                 on khc.hook_officeoid = ho.hook_officeoid
       where
           eq.product_id = 'XXX' and
           (eq.location_description is null or
            ho.hook_office_letter || lpad(khc.hook_no, 5, '0') <> eq.location_description)) src
  on (tgt.equipmentoid = src.equipmentoid)
when matched then
update set tgt.location_description = src.hookno;

我所做的只是获取您的 select 语句,添加到设备表的主键列的引用中,这允许我们将源子查询连接回表并更新相关行。

【讨论】:

  • 非常感谢 Boneist!急于立即检查...看起来很容易;-)
  • 这就是合并语句的美妙之处——如果你有生成你想要的结果的 sql 语句,通常只是将它作为合并的源部分插入。 (当然,有时您可以修改源查询以删除目标表,并强制将联接作为合并语句的一部分,这可以使其运行得更快,但我不确定在这种情况下会有所不同)
  • 成功了!! :) 纯真棒。 Boneist 我从您的友好回答中学到的东西比我从今天翻阅的那几十个 oracle 手册页中学到的更多。非常感谢!
  • 很高兴能帮上忙 *{:-)
【解决方案2】:

看起来您想将 location_description 划分为一个字母和 5 位数字代码,并将其分配给 HOOK_OFFICE_LETTER 和 HOOK_NO。

UPDATE ho 
    INNER JOIN KEY_HOLDING kho                ON khc.KEY_HOLDINGOID = kho.KEY_HOLDINGOID
    INNER JOIN CONTRACT con                   ON kho.CUSTOMEROID = con.CUSTOMEROID
    INNER JOIN VW_CURRENT_CONTRACT_PERIOD ccp ON con.CONTRACT_ID = ccp.CONTRACT_ID
    INNER JOIN CONTRACT_PERIOD cp             ON (ccp.CONTRACT_ID = cp.CONTRACT_ID AND ccp.CONTRACT_PERIOD = cp.CONTRACT_PERIOD)
    INNER JOIN CONTRACT_EQUIP_PERIOD cep      ON cp.CONTRACT_PERIODOID = cep.CONTRACT_PERIODOID
    INNER JOIN EQUIPMENT eq                   ON cep.EQUIPMENTOID = eq.EQUIPMENTOID
    INNER JOIN HOOK_OFFICE ho                 ON khc.HOOK_OFFICEOID = ho.HOOK_OFFICEOID

SET HOOK_OFFICE_LETTER = SUBSTRING(eq.location_description,1,1),
--cut letter and assignto HOOK_OFFICE_LETTER 
khc.HOOK_NO =   SUBSTRING(eq.location_description,2,5) 
--cut 5-digit code and assign to khc.HOOK_NO
WHERE
    eq.PRODUCT_ID = 'XXX' AND
        (eq.LOCATION_DESCRIPTION IS NULL) OR
        ((ho.HOOK_OFFICE_LETTER || LPAD(khc.HOOK_NO, 5, '0') <> eq.LOCATION_DESCRIPTION))

但您可能希望将 eq.location_description 更新为 (ho.HOOK_OFFICE_LETTER || LPAD(khc.HOOK_NO, 5, '0') 因此,使用下一个查询:

 UPDATE ho 
        INNER JOIN KEY_HOLDING kho                ON khc.KEY_HOLDINGOID = kho.KEY_HOLDINGOID
        INNER JOIN CONTRACT con                   ON kho.CUSTOMEROID = con.CUSTOMEROID
        INNER JOIN VW_CURRENT_CONTRACT_PERIOD ccp ON con.CONTRACT_ID = ccp.CONTRACT_ID
        INNER JOIN CONTRACT_PERIOD cp             ON (ccp.CONTRACT_ID = cp.CONTRACT_ID AND ccp.CONTRACT_PERIOD = cp.CONTRACT_PERIOD)
        INNER JOIN CONTRACT_EQUIP_PERIOD cep      ON cp.CONTRACT_PERIODOID = cep.CONTRACT_PERIODOID
        INNER JOIN EQUIPMENT eq                   ON cep.EQUIPMENTOID = eq.EQUIPMENTOID
        INNER JOIN HOOK_OFFICE ho                 ON khc.HOOK_OFFICEOID = ho.HOOK_OFFICEOID

    SET eq.location_description = (ho.HOOK_OFFICE_LETTER || LPAD(khc.HOOK_NO, 5, '0')
    WHERE
        eq.PRODUCT_ID = 'XXX' AND
        (eq.LOCATION_DESCRIPTION IS NULL) OR
        ((ho.HOOK_OFFICE_LETTER || LPAD(khc.HOOK_NO, 5, '0') <> eq.LOCATION_DESCRIPTION))

【讨论】: