简体   繁体   中英

How to use Oracle PL/SQL to create ten similar tables from a given Oracle source table

I have data in one table in a oracle database and I want to “split” this table in SQLplus into ten tables based on the content of one column.

Source table is all_data with columns:

kind , time_period , amount

kind is the row to split on: the value of kind In each row is exactly one of ten different names "peter", "maria", "eric", etc...

Now I want to create ten tables, one for each name. The first table for name "peter" would be created manually in SQL as:

CREATE TABLE peter_data  AS (
  SELECT p.kind, p.time_period, p.amount as amount_peter
    FROM all_data
    WHERE kind = 'peter'
)
;

How can I use PL/SQL to create all ten tables peter_data , maria_data , eric_data , etc.?


I tried:

DECLARE
    TYPE array_t IS VARRAY(3) OF VARCHAR2(10);
    ARRAY array_t := array_t('peter', 'maria', 'eric');   
BEGIN
    FOR i IN 1..ARRAY.COUNT LOOP
        CREATE TABLE ARRAY(i) AS (
          SELECT p.kind, p.time_period, p.amount as amount_peter
            FROM all_data
            WHERE kind = ARRAY(i)
        )
        ;
   END LOOP;
END;

but this gives understandably the error “ PLS-00103: Encountered the symbol "CREATE"...

You need dynamic SQL for it and with Oracle we can use execute immediate to do so.

declare
  type array_t is table of varchar2(10);
  array array_t := array_t();
  lo_stmt varchar2(2000);
begin
  select distinct kind
    bulk collect into array
    from all_data;
  
  for i in 1..array.count loop
    --dbms_output.put_line(array(i));
    lo_stmt :=
    'create table '||array(i)||'_data '||
    'as '||
    'select kind,time_period,amount '||
    '  from all_data '||
    ' where kind = '''||array(i)||'''';
    --dbms_output.put_line(lo_stmt);
    execute immediate lo_stmt;
  end loop;
end;
/

Demo can be seen here

OR with a single statement

declare
  type array_t is table of varchar2(4000);
  array array_t := array_t();
  lo_stmt varchar2(2000);
begin
  select distinct 'create table '||kind||'_data '||
    'as '||
    'select kind,time_period,amount '||
    'from all_data '||
    'where kind = '''||kind||''''
    bulk collect into array
    from all_data;
  
  for i in 1..array.count loop
    --dbms_output.put_line(array(i));
    execute immediate array(i);
  end loop;
end;
/

Also works here

You just need to wrap your DDL statement in a string and call EXECUTE IMMEDIATE :

DECLARE
  TYPE array_t IS VARRAY(3) OF VARCHAR2(10);
  ARRAY array_t := array_t('peter', 'maria', 'eric');   
BEGIN
  FOR i IN 1..ARRAY.COUNT LOOP
    EXECUTE IMMEDIATE
      'CREATE TABLE ' || ARRAY(i) || ' ( kind, time_period, amount_' || ARRAY(i) || ' ) AS'
      || ' SELECT kind, time_period, amount'
      || ' FROM all_data WHERE kind = ''' || ARRAY(i) || '''';
   END LOOP;
END;
/

(and remove p. as you didn't define the alias p anywhere.)

Then, if you have the table:

CREATE TABLE all_data ( kind, time_period, amount ) AS
SELECT 'peter', DATE '2020-01-01', 23 FROM DUAL UNION ALL
SELECT 'maria', DATE '2020-02-01', 42 FROM DUAL UNION ALL
SELECT 'eric',  DATE '2020-03-01', 11 FROM DUAL;

Then you get the tables:

SELECT * FROM peter;
\nKIND | TIME_PERIOD |  AMOUNT_PETER \n:---- |  :------------------ |  -----------: \npeter | 2020-01-01 00:00:00 |  23 \n
SELECT * FROM maria;
\nKIND | TIME_PERIOD |  AMOUNT_MARIA \n:---- |  :------------------ |  -----------: \nmaria | 2020-02-01 00:00:00 |  42 \n
SELECT * FROM eric;
\nKIND | TIME_PERIOD |  AMOUNT_ERIC \n:--- |  :------------------ |  ----------: \neric | 2020-03-01 00:00:00 |  11 \n

db<>fiddle here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM