Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Peter.

Asked: September 08, 2016 - 3:34 pm UTC

Last updated: September 08, 2016 - 4:01 pm UTC

Version: 11g

Viewed 1000+ times

You Asked

I have a script that calls a table from our database but that table is replaced by newer tables quarterly. The format for the tables is like TABLENAME_YYYYMMDD. I would like to call the most current table in the script so that I don't have to update the script every time I get a new table.

-- THE DATABASE CONTAINS SEVERAL TABLES WITH SIMILAR NAMES.

CREATE TABLE ESL_20160623 (ID INT, ADDRESS_NUMBER INT, STREET_NAME NVARCHAR2(80));

-- WITH THIS DATA IN IT:

INSERT INTO ESL_20160623 VALUES (1, 1, 'NEW MAIN STREET');
INSERT INTO ESL_20160623 VALUES (1, 2, 'NEW MAIN STREET');

CREATE TABLE ESL_20160106 (ID INT, ADDRESS_NUMBER INT, STREET_NAME NVARCHAR2(80));

-- WITH THIS DATA IN IT:

INSERT INTO ESL_20160106 VALUES (1, 11, 'OLD MAIN STREET');
INSERT INTO ESL_20160106 VALUES (1, 12, 'OLD MAIN STREET');

-- I'D LIKE TO BE ABLE TO CALL THE NEW TABLE INTO MY QUERY BASED ON THE DATE PORTION OF THE NAME.
-- BELOW IS WHAT I TRIED:

SELECT *
FROM
(
SELECT TABLE_NAME
FROM DBA_TABLES
WHERE REGEXP_LIKE(TABLE_NAME, 'ESL_\d+{8}$')
AND SUBSTR(TABLE_NAME, 5, 8) =
(
SELECT MAX(SUBSTR(TABLE_NAME, 5, 8))
FROM DBA_TABLES
WHERE REGEXP_LIKE(TABLE_NAME, 'ESL_\d+{8}$')
)
)
;


Thank you

and Chris said...

You can't pass queries as table names. To do this you'll need dynamic SQL.

Using your tables it would look something like this:

declare
  sql_stmt   varchar2(1000);
  table_name user_tables.table_name%type;
  rw         esl_20160623%rowtype; --assumes all tables have same structure
begin
  select max(table_name)
  into   table_name
  from   user_tables
  where  regexp_like ( table_name, 'ESL_\d+{8}$' );
  
  sql_stmt := 'select id, address_number , street_name from ' || table_name || 
    ' where rownum = 1'; -- replace with your where clause
  
  execute immediate sql_stmt into rw;
  
  dbms_output.put_line(
    rw.id || ',' || rw.address_number || ',' || rw.street_name
  );
end;
/

1,1,NEW MAIN STREET


If you want to know more about dynamic SQL, check out:

http://stevenfeuersteinonplsql.blogspot.co.uk/2016/07/a-quick-guide-to-writing-dynamic-sql-in.html
http://www.oracle.com/technetwork/issue-archive/2015/15-may/o35plsql-2541606.html

But there is a better way. Stop creating date specific tables! Instead have a single ESL table with a date column separating the values. Then you can use regular static SQL instead of messing around with the dynamic stuff.

For example:

create table esl (
  load_quarter date, id int, address_number int, street_name nvarchar2 ( 80 )
) ;
insert into esl values ( date'2016-06-23', 1, 1, 'NEW MAIN STREET') ;
insert into esl values ( date'2016-06-23', 1, 2, 'NEW MAIN STREET') ;
insert into esl values ( date'2016-01-06', 1, 11, 'OLD MAIN STREET') ;
insert into esl values ( date'2016-01-06', 1, 12, 'OLD MAIN STREET') ;

select * from esl
where  load_quarter = date'2016-01-06';

LOAD_QUARTER          ID  ADDRESS_NUMBER  STREET_NAME      
06-JAN-2016 00:00:00  1   11              OLD MAIN STREET  
06-JAN-2016 00:00:00  1   12              OLD MAIN STREET


You may want check out partitioning to help you manage this. This effectively splits your single ESL table into several smaller ones. But with the upside that you access them like a one table.

Is this answer out of date? If it is, please let us know via a Comment

More to Explore

VLDB

If you are new to partitioning, check out Connor McDonald's introduction series here.

VLDB

Documentation set on VLDB and Partitioning.