Home » SQL & PL/SQL » SQL & PL/SQL » "Client copy" with PL/SQL
"Client copy" with PL/SQL [message #633213] Thu, 12 February 2015 06:50 Go to next message
wurmi
Messages: 4
Registered: February 2015
Location: Germany
Junior Member
Hello everybody,

maybe you can help with weak cursors. I have searched through PL/SQL tutorials but have not found a example to my issue, so I am not sure if the way I want to go is possible at all.

Im my schema, there are about 50 tables, which all have a composite primary key with first attribute MANDT (CHAR 1). I have adapted this concept from SAP.

So there are e.g. 2 tables

CREATE TABLE employees(
MANDT CHAR(1) NOT NULL,
EMPLOYEE VARCHAR2(10) NOT NULL,
NAME VARCHAR2(50) NOT NULL),
CONSTRAINT employee_pk PRIMARY KEY (MANDT, EMPLOYEE));

CREATE TABLE dept(
MANDT CHAR(1) NOT NULL,
DEPT VARCHAR2(10) NOT NULL,
NAME VARCHAR2(50) NOT NULL),
CONSTRAINT dept_pk PRIMARY KEY (MANDT, DEPT));

and there is one table which contains the table names:

CREATE TABLE MYTABLES(
TABLENAME VARCHAR2(20) NOT NULL,
CONSTRAINT mytables_pk PRIMARY KEY (TABLENAME));

I need a procedure in PL/SQL which can copy the entries from one client ("MANDT") to another client.

E.g. in table employees there are the following entries:
A;JOHN;John doe
A;JANET;Janet doe
A;SMITH;Mr. Smith
B;JIM;Jim Beam

After the copy from client A to client B, the entries should be like that:
A;JOHN;John doe
A;JANET;Janet doe
A;SMITH;Mr. Smith
B;JOHN;John doe
B;JANET;Janet doe
B;SMITH;Mr. Smith

What I was doing is to loop at all entries in the table mytables and do

--DELETE the existing entries in target client
v_del = 'DELETE FROM ' || crs_tables.TABLENAME || ' WHERE MANDT =: target'
execute immediate v_del USING 'A'.

--Loop at the source entries
v_sel = 'SELECT FROM ' || crs_tables.TABLENAME || ' WHERE MANDT =: source'
open ref cursor

Here is my problem: How can I get each line of the legacy cursor into a line variable, change the MANDT column to 'B' and then INSERT into the database? I want to avoid to write explicitely the same step for each of the 50 tables.

Is there an efficient way to do this?
Re: "Client copy" with PL/SQL [message #633214 is a reply to message #633213] Thu, 12 February 2015 06:59 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
Welcome to the forum
Please read and follow How to use [code] tags and make your code easier to read?

Why use a ref cursor?
Just write a insert/select statement (dynamic via execute immediate if you must).
INSERT INTO <table> SELECT <target>, <other columns> FROM <table> WHERE mandt = <source>
Re: "Client copy" with PL/SQL [message #633215 is a reply to message #633214] Thu, 12 February 2015 07:00 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
And as general point - ref cursors primarily exist to get data to non-oracle client programs.
I can't think of anything you would need to do within PL/SQL where a ref cursor is the best approach.

EDIT: typo

[Updated on: Thu, 12 February 2015 07:01]

Report message to a moderator

Re: "Client copy" with PL/SQL [message #633216 is a reply to message #633214] Thu, 12 February 2015 07:14 Go to previous messageGo to next message
wurmi
Messages: 4
Registered: February 2015
Location: Germany
Junior Member
cookiemonster wrote on Thu, 12 February 2015 06:59


INSERT INTO <table> SELECT <target>, <other columns> FROM <table> WHERE mandt = <source>


Hi cookiemonster, thank you for your quick reply.

If ref Cursors are not the right thing, then it is perfect with me.

But in this code above, how do I determine the <other columns> for the different tables while Looping to a list of table_names?
Re: "Client copy" with PL/SQL [message #633217 is a reply to message #633215] Thu, 12 February 2015 07:17 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
@OP, what is the purpose of having table names in a separate table? And if you really want to loop through all tables dynamically, then Oracle has the metadata, *_TABLES view. But I don't know the purpose of all this.

Another thing, if you want to do DELETE and INSERT, I would suggest MERGE statement.
Re: "Client copy" with PL/SQL [message #633218 is a reply to message #633217] Thu, 12 February 2015 07:19 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
>how do I determine the <other columns> for the different tables while Looping to a list of table_names?

USER_TAB_COLS view
Re: "Client copy" with PL/SQL [message #633219 is a reply to message #633217] Thu, 12 February 2015 07:28 Go to previous messageGo to next message
wurmi
Messages: 4
Registered: February 2015
Location: Germany
Junior Member
Lalit Kumar B wrote on Thu, 12 February 2015 07:17
@OP, what is the purpose of having table names in a separate table?


Not all tables in user_tables are client dependent, i.e. some master data do not have the MANDT Attribute in their key. So I decided to put all the relevant tables in a control table. Also the deleting and inserting needs to be done in a defined order because of foreign key constraints.

How do I use user_tab_cols view to fill the insert Statement by cookiemonster? I am new to PL/SQL and always tend to concatenate SQL-Strings before executing; however I know need to get over this...

Right now, I do not have Access to a database and can not try things out.

[Updated on: Thu, 12 February 2015 07:28]

Report message to a moderator

Re: "Client copy" with PL/SQL [message #633220 is a reply to message #633219] Thu, 12 February 2015 07:33 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
I am not sure of what you are trying to imement, so I will only answer about getting column names for the table names that you have stored in a separate table.

You can join MYTABLES with the view I mentioned, join on table_name and select the column names from the view to get the column names for each table.
Re: "Client copy" with PL/SQL [message #633221 is a reply to message #633219] Thu, 12 February 2015 07:36 Go to previous messageGo to next message
BlackSwan
Messages: 26766
Registered: January 2009
Location: SoCal
Senior Member
you are unqualified to write any export/import replacement & a fool for attempting to reinvent the wheel.
Re: "Client copy" with PL/SQL [message #633223 is a reply to message #633219] Thu, 12 February 2015 08:18 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
wurmi wrote on Thu, 12 February 2015 13:28

Not all tables in user_tables are client dependent, i.e. some master data do not have the MANDT Attribute in their key. So I decided to put all the relevant tables in a control table.

If all the relevant tables have the mandt column and the non-relevant tables don't then you could just query user_tab_columns for the list of tables with a column called mandt.

wurmi wrote on Thu, 12 February 2015 13:28


How do I use user_tab_cols view to fill the insert Statement by cookiemonster? I am new to PL/SQL and always tend to concatenate SQL-Strings before executing; however I know need to get over this...

Loop over list of columns, append each one to a string, adding a comma seperater between each one.
Concatenate string with rest of statement.
Re: "Client copy" with PL/SQL [message #633224 is a reply to message #633216] Thu, 12 February 2015 08:22 Go to previous messageGo to next message
John Watson
Messages: 8931
Registered: January 2010
Location: Global Village
Senior Member
The CREATE TABLE statments you provided do not run. Check your use of brackets. In future, please test code before you post it.
You are making this very complicated. To copy rows with a different value for MANDT, all you need is something like
insert into employees select 'B',employee,name from employees where mandt='A';


Re: "Client copy" with PL/SQL [message #633225 is a reply to message #633213] Thu, 12 February 2015 08:24 Go to previous messageGo to next message
BlackSwan
Messages: 26766
Registered: January 2009
Location: SoCal
Senior Member
>Is there an efficient way to do this?

use Oracle provided utilities; expdp & impdp otherwise new tables are missing column constraints that old tables have.
Re: "Client copy" with PL/SQL [message #633226 is a reply to message #633223] Thu, 12 February 2015 08:24 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
@CM,

I didn't understand, why comma separated column names? OP has a list of table names, he needs column names for each table, so I suggested the join of that table with the user_tab_cols view. The same column name could be used in OP's dynamic query. Am I missing something in your suggestion?
Re: "Client copy" with PL/SQL [message #633227 is a reply to message #633226] Thu, 12 February 2015 08:28 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
@John,

OP wants the value 'B', i.e. the column name to be dynamic. Else, no need of the complexity. That's why CM already suggested the plain INSERT in his previous post.

[Updated on: Thu, 12 February 2015 08:30]

Report message to a moderator

Re: "Client copy" with PL/SQL [message #633228 is a reply to message #633227] Thu, 12 February 2015 08:32 Go to previous messageGo to next message
Roachcoach
Messages: 1576
Registered: May 2010
Location: UK
Senior Member
I'm pretty convinced there's a better way around this problem than duplicating data.

What are you actually trying to do? What's the customer wanting?
Re: "Client copy" with PL/SQL [message #633229 is a reply to message #633225] Thu, 12 February 2015 08:43 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
BlackSwan wrote on Thu, 12 February 2015 14:24
>Is there an efficient way to do this?

use Oracle provided utilities; expdp & impdp otherwise new tables are missing column constraints that old tables have.


You haven't read the question properly
Re: "Client copy" with PL/SQL [message #633230 is a reply to message #633226] Thu, 12 February 2015 08:46 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
Lalit Kumar B wrote on Thu, 12 February 2015 14:24
@CM,

I didn't understand, why comma separated column names?



Because a select statement where the column names aren't comma separated is invalid. I'm not suggesting anything clever here, just pointing out the basics of generating a select statement using user_tab_columns.
Re: "Client copy" with PL/SQL [message #633231 is a reply to message #633224] Thu, 12 February 2015 08:49 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
John Watson wrote on Thu, 12 February 2015 14:22
The CREATE TABLE statments you provided do not run. Check your use of brackets. In future, please test code before you post it.
You are making this very complicated. To copy rows with a different value for MANDT, all you need is something like
insert into employees select 'B',employee,name from employees where mandt='A';




That is just the static version of what I already suggested.
To be honest, assuming new tables can't be created on the fly, it's the better approach and I should have pointed it out before.
Just create one procedure with a set of hard-coded delete and insert statements. If a new table is added then modify the procedure to include it.

If tables can be created on the lfy then that's a seperate design issue that really should be fixed before it causes significant problems.
Re: "Client copy" with PL/SQL [message #633232 is a reply to message #633231] Thu, 12 February 2015 08:51 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
I also have to suspect, as Roachcoach pointed out, that if you're copying existing data in this manner then there is probably a better solution that doesn't invlove copying tons of data.
Re: "Client copy" with PL/SQL [message #633313 is a reply to message #633213] Sun, 15 February 2015 09:29 Go to previous message
wurmi
Messages: 4
Registered: February 2015
Location: Germany
Junior Member
Hallo everybody,

thank your for your tips and hints, I have included them into my code.

The database where I will be using my procedure is just a personal test database where it should be possible to create quick snapshots and switch between them. No tons of data and security issues. Since I worked many years with SAP, the leading MANDT column and the "Client copy" is something very common to me, although it may seem odd to somebody who sees that for the first time.

After fiddling around, here is something that works. The commit statements however are now on statement level, will be corrected later. Feel free to comment.

create or replace procedure prc_test as 

cursor cur_DELETE is
  select UPPER(TABELLENNAME) as TABELLE
  from TBLMANDTSchema 
  where MITMANDT='X'
  order by RHFLG desc;
  
cursor cur_TABLES is
  select UPPER(TABELLENNAME) AS TABELLE
  from TBLMANDTSCHEMA 
  where MITMANDT='X'
  order by RHFLG asc;  
  
  wa_col   user_tab_cols%ROWTYPE;  
  wa_table cur_DELETE%ROWTYPE;
  
  v_delete varchar2(255);
  v_select varchar2(255);
  v_cols   varchar2(255);
  
  v_qmandt char(1);
  v_zmandt char(1);
  
BEGIN

  v_qmandt := '0';  --source client
  v_zmandt := '3';  --target client
  
  dbms_output.put_line('Start deleting:');
  
  /* DELETE ENTRIES IN TARGET CLIENT */
  for wa_table in cur_DELETE
  loop
    v_delete := 'DELETE FROM ' || wa_table.TABELLE || ' WHERE MANDT= :m';
    dbms_output.put_line( v_delete );
    execute immediate v_delete using v_zmandt;
    commit;      
  end loop; 
  
  dbms_output.put_line('Start copying:');
  for wa_table in cur_TABLES
    loop
  
        /* CONCATENATE COLUMNS OTHER THAN 'MANDT' */
        declare
        cursor cur_TABELLEN_SP is
           SELECT * FROM USER_TAB_COLS WHERE TABLE_NAME = wa_table.TABELLE
           AND COLUMN_NAME <> 'MANDT' 
           ORDER BY COLUMN_ID;       
        begin
            v_cols := '';
            for wa_col in cur_TABELLEN_sp
            loop
              --dbms_output.put_line( wa_col.column_name );
              v_cols := v_cols || wa_col.column_name || ', ';
            end loop;
        end;
        v_cols := substr(v_cols, 0, LENGTH(v_cols)-2);
        dbms_output.put_line( 'Table ' || wa_table.TABELLE || ' has columns: ' || v_cols);
          
        v_SELECT := 'INSERT INTO ' || wa_table.TABELLE || ' SELECT :t , ' || v_cols || ' FROM ' ||  wa_table.TABELLE || ' WHERE MANDT = :s';
        execute immediate v_select using v_zmandt, v_qmandt;
        commit;
     
    end loop;

end prc_test;
Previous Topic: fetch the same value with & without using the trunc ??
Next Topic: Identity column inserts duplicates with Insert All statement, unique constraint violation
Goto Forum:
  


Current Time: Fri Apr 26 00:17:35 CDT 2024