Question

I would like to check all columns of all tables for entries which are not contained in the table of valid dates.

In other words the elements of all columns with type date have to one of the entries of the table column CALENDAR.BD.

My problem is that executing the function by select checkAllDateColumns() from DUAL results only in

invalid table name, line 16

and I don't see why?

CREATE OR REPLACE FUNCTION checkAllDateColumns RETURN NUMBER IS 
    l_count NUMBER; 
BEGIN 
  FOR t IN (SELECT table_name 
              FROM all_tables 
             WHERE owner = 'ASK_QUESTION') 
    LOOP 
        dbms_output.put_line ('Current table : ' || t.table_name); 

        FOR c IN (SELECT column_name 
                    FROM all_tab_columns 
                   WHERE TABLE_NAME = t.table_name AND data_type = 'DATE') 
        LOOP 
            execute immediate 'select count(*) from :1 where :2 not in (select BD from CALENDAR where is_business_day = 1)' 
                into l_count 
                using t.table_name, c.column_name;


            IF (l_count > 0) THEN         
                RETURN 1; 
            END IF; 

        END LOOP; 
    END LOOP; 
    RETURN 0; 
END checkAllDateColumns; 

/ 

Btw: I am fine with the statement stopping on the first mismatch, currently I would like to figure out the dynamic sql...

Was it helpful?

Solution

You can't use a bind variable for the table or column names, only for column values. It's trying to interpret :1 as an identifier, it isn't using the value from the using clause. Your returning into clause should also just be into. This works:

execute immediate 'select count(*) from "' || t.table_name
    || '" where "' || c.column_name
    || '" not in (select BD from CALENDAR where is_business_day = 1)' 
  into l_count; 

You might get a performance difference using a left-join approach, but it depends on your data:

execute immediate 'select count(*) from "' || t.table_name
    || '" t left join calendar c on c.bd = trunc(t."'
    || c.column_name || '") and c.is_business_day = 1 ' 
    || ' where c.bd is null'
  into l_count;

I added a trunc() in case any of the fields might have times in them; you can do the same in the other version too of course.

And in both I've included double-quotes around the table and column names, just in case there are any quoted identifiers - without those there's a risk of getting an ORA-00904 'invalid identifier' error. But hopefully you don't have any to worry about anyway.

You also don't really need nested loops; you can either get the table name from all_tab_columns, or if you prefer, join all_tables and all_tab_columns in a single cursor. You should also be checking that the owner is the same in both tables, in case there are two versions of a table in different schemas.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top