I_am_Batman I_am_Batman - 7 months ago 44
SQL Question

Can select using dblink, cannot insert using dblink

I have inherited a code which helps me to fetch data from external systems. It is a bit screwed up, but I have to use it as is.

SELECT NVL (
(
SELECT TRIM (alias.SERVICING_CODE)
FROM schema.CX_SER@db_link alias
WHERE row_id =
(
SELECT mix.par_row_id
FROM schema.CX_SER_MI_XM@db_link mix
WHERE mix.bill = SA.BILL_AC
AND ROWNUM < 2
)
AND ROWNUM < 2
),
(
SELECT TRIM (ba.SERVICING_CODE)
FROM schema.s_some_table@db_link ba
WHERE ba.row_id = sa.BILL_AC AND ROWNUM < 2
)
) REQUIRED_CODE, --NVL ends here
COUNT (*) order_count,
TRUNC (ia.CREATED_DATE) CREATED_DATE_date,
TRUNC (ia.CREATED_DATE + 1) inserted_date
FROM schema.s_some_table@db_link sa, schema.action_table@db_link ia, schema.s_order@db_link ord
WHERE ia.CREATED_DATE >= TRUNC (SYSDATE - 1)
AND ia.CREATED_DATE < TRUNC (SYSDATE)
AND ord.status = 'Done'
GROUP BY SA.BILL_AC, TRUNC (ia.CREATED_DATE), TRUNC (IA.CREATED_DATE + 1);


The said code returns results when I run the select as is.
But when I try to insert these records in my schema(using simple
insert into(columns) <this select statement>
, I get the following error messages :


  1. Error: ORA-00904:"SA"."BILLING_ACC_ID" :invalid identifier.
    ORA - 02063 : preceding line from db_link.



A select statement like :

with c as(
SELECT NVL (
(
SELECT TRIM (alias.SERVICING_CODE)
FROM schema.CX_SER@db_link alias
WHERE row_id =
(
SELECT mix.par_row_id
FROM schema.CX_SER_MI_XM@db_link mix
WHERE mix.bill = SA.BILL_AC
AND ROWNUM < 2
)
AND ROWNUM < 2
),
(
SELECT TRIM (ba.SERVICING_CODE)
FROM schema.s_some_table@db_link ba
WHERE ba.row_id = sa.BILL_AC AND ROWNUM < 2
)
) REQUIRED_CODE, --NVL ends here
COUNT (*) order_count,
TRUNC (ia.CREATED_DATE) CREATED_DATE_date,
TRUNC (ia.CREATED_DATE + 1) inserted_date
FROM schema.s_some_table@db_link sa, schema.action_table@db_link ia, schema.s_order@db_link ord
WHERE ia.CREATED_DATE >= TRUNC (SYSDATE - 1)
AND ia.CREATED_DATE < TRUNC (SYSDATE)
AND ord.status = 'Done'
GROUP BY SA.BILL_AC, TRUNC (ia.CREATED_DATE), TRUNC (IA.CREATED_DATE + 1))
select * from c where REQUIRED_CODE IS NOT NULL;


also fail with same error. However, I am able to obtain results when I query using some other column in above
with
clause statement gives results, for e.g
where order_count>2
gives result. So the problem is in the REQUIRED_CODE section, and maybe, in the group by.

Please guide on the course of action. I need to insert the records flowing into my schema.

NOTE : All columns are either varchar2 or date

Answer

If my attempt to replicate the issue has ended up close to yours, you can avoid the error with the CTE by adding a driving_site hint:

with c as (
   SELECT /*+ DRIVING_SITE (sa) */ NVL (
...

That prevents the query being written and distributed in a way that confuses the optimiser; I think it's tripping over the nested reference to SA in the subquery and it's ending up too many levels down to be recognised.

That hint doesn't have any effect on the insert though.

As mentioned in comments I've had a quick go at rewriting the query to avoid the subqueries. It's a bit rough and I'm not sure I understand everything you're currently doing, partly because of the table name changes etc. But you wanted to see it, and it might give you something to work from...

INSERT INTO t42
  SELECT NVL (TRIM(MIN(t.SERVICING_CODE) KEEP (DENSE_RANK FIRST ORDER BY NULL)),
           TRIM(MIN(ba.SERVICING_CODE) KEEP (DENSE_RANK FIRST ORDER BY NULL))
         ) REQUIRED_CODE,  --NVL ends here
         COUNT (*) order_count,
         TRUNC (ia.CREATED_DATE) CREATED_DATE_date,
         TRUNC (ia.CREATED_DATE + 1) inserted_date
    FROM schema.s_some_table@db_link sa
    LEFT JOIN (
      SELECT mix.bill, alias.servicing_code
        FROM schema.CX_SER_MI_XM@db_link mix
        JOIN schema.CX_SER_MY_IDEA@db_link alias
          ON alias.row_id = mix.par_row_id
      ) t
      ON t.bill = SA.BILLING_ACC_ID
    LEFT JOIN schema.s_some_table@db_link ba
      ON ba.row_id = sa.BILLING_ACC_ID
    CROSS JOIN schema.action_table@db_link ia
    CROSS JOIN schema.s_order@db_link ord
   WHERE     ia.CREATED_DATE >= TRUNC (SYSDATE - 1)
         AND ia.CREATED_DATE < TRUNC (SYSDATE)
         AND ord.status = 'Done'
GROUP BY SA.BILLING_ACC_ID, TRUNC (ia.CREATED_DATE), TRUNC (IA.CREATED_DATE + 1); 

If you can't get it working then you could use your original query in a PL/SQL block, either as a cursor and doing row-by-row inserts, or preferably (particularly if it will return a lot of data) using a collection with bulk collect and a forall insert.

If you search My Oracle Support for ORA-02063 and ORA-00904 you'll see quite a few bugs, some of which seem to apply to 11g but ought to have been fixed by my version 11.2.0.4; I can't see anything that quite matches from a quick browse but it's possible you are hitting one of those, or indeed one that hasn't been reported. It may be worth raising an SR to investigate your specific scenario.