how to populate database using procedures - sql

I have about 15 different Tables filled with different data and different entity relationships.
I need to create a script which will populate my database with the content of those tables.
After script is finished, i run it in cmd, using sqlplus and later START path to file
i have two different sql files, one named db_spec.sql and another db_body.sql.
In my db_body.sql i've created a procedure to store data from two tables which have 1:N relationship.
First table
CREATE TABLE LOCATION (
ID_LOCATION INTEGER NOT NULL,
LOCATION_NAME VARCHAR2 (20) NOT NULL,
POSTCODE INTEGER NOT NULL
);
ALTER TABLE LOCATION
ADD (CONSTRAINT PK_LOCATION PRIMARY KEY (ID_LOCATION));
Second table
CREATE TABLE ADDRESS (
ID_ADDRESS INTEGER NOT NULL,
STREET VARCHAR2 (20) NOT NULL,
HOUSE_NUMBER INTEGER NOT NULL,
FK_ID_LOCATION INTEGER NOT NULL
);
ALTER TABLE ADDRESS
ADD (CONSTRAINT PK_ADRESS PRIMARY KEY (ID_ADRESS));
ALTER TABLE ADRESS
ADD (CONSTRAINT FK_ADRESS_ID_LOCATION FOREIGN KEY
(FK_ID_LOCATION) REFERENCES LOCATION(ID_LOCATION));
Now i need to populate them using data. Let's say
LOCATION_NAME = "London"
POSTOCDE = "394505" ...and so on
I've created this script, but as i run it, nothing shows up, so there's obviously some error in it.
db_spec.sql script
CREATE OR REPLACE PACKAGE apartment AS
PROCEDURE fill_location(location_number NUMBER);
PROCEDURE fill_address(number_of_addresses NUMBER);
END apartment;
db_body.sql script
SET SERVEROUTPUT ON
SET LINESIZE 400
SET TIMING ON
CREATE OR REPLACE PACKAGE BODY address AS
PROCEDURE fill_location(location_number NUMBER) IS
p_location_name VARCHAR2(20);
p_postcode NUMBER (10,2);
BEGIN
FOR num IN 1..location_number LOOP
p_location_name := 'Location';
p_postcode := dbms_random.value(1000,9600);
p_postcode := p_postcode ||' '|| TO_CHAR(num);
INSERT INTO LOCATION (ID_LOCATION, LOCATION, POSTCODE)
VALUES (num, p_location_name, p_postcode);
dbms_output.put_line(num);
END LOOP;
END fill_location;
PROCEDURE fill_address(number_of_adresses NUMBER)IS
p_street_name VARCHAR(20);
p_house_number NUMBER (10,2);
p_id_address NUMBER(10);
CURSOR data IS
SELECT ID_LOCATION
FROM LOCATION;
BEGIN
FOR num_loop IN data LOOP
FOR num IN 1..number_of_adresses LOOP
p_street_name := 'Ulica';
p_house_number := dbms_random.value(1,99);
p_street_name := p_street_name ||' '|| TO_CHAR(num);
SELECT NVL(MAX(p_id_address)+1,1)
INTO p_id_address
FROM ADDRESS;
INSERT INTO ADDRESS (ID_ADDRESS, FK_ID_LOCATION, STREET, HOUSE_NUMBER)
VALUES (p_id_address, num_loop.ID_LOCATION, p_street_name, p_house_number);
dbms_output.put_line(num_loop.ID_LOCATION);
END LOOP;
END LOOP;
END fill_address;
END;
SHOW ERRORS;
Can you guys please help me fix the problem so the code will run and work fine? Any input is appreciated!
Btw oprema_stanovanja.polni_kraj = address.fill_location

Unless I'm missing it... COMMIT;
Based on the screenshot you issued, add a / to the end of your body and procedure definitions.

Firstly, the package posted doesn't compile without errors.
You would need to fix the following first:
Terminate FOR LOOP statements with END LOOP
Terminate BEGIN of procedures with END <procedure_name_here>
PL/SQL variable names need to be different than the actual column name to avoid ambiguity in the INSERT statement. [ You can't have LOCATION the column name and PL/SQL variable with the same name. Oracle cannot resolve which is to be used where in the INSERT]
To get you started, I have fixed the fill_location procedure below. Proceed similarly for the other procedure.
create or replace package body apartment as
procedure fill_location(location_number number) is
p_location_name varchar2(20);
p_postcode number(10,2);
begin
for num in 1.. location_number loop
p_location_name := 'location';
p_postcode := dbms_random.value(1000,9600);
p_location_name := p_location_name ||' '|| to_char(num);
insert into location (id_location, location_name, postcode)
values (num, p_location_name, p_postcode);
dbms_output.put_line(num);
end loop;
end fill_location;
Fix the above errors/suggestions until you get 'PL/SQL successfully compiled`. If you get 'compiled with errors' use "show errors" to find and fix any other errors.

Related

How to loop through keys values | Oracle |

I am stuck with this approach not getting. How do I need to go with it.
I have table which as column -> KEYS. This as more then 500+ keys
Like below the data would be :
KEYS
12RTY
UIIJ9
GHSJ8
HJSKI
I should not be making use of cursor need to loop one by one values from column : KEYS to my PLSQL logic
This is how I want to convert that data to comma separated format and used in my loop to read one by one value
(key1,key2,key3....)
My code :
DECLARE
v_name varchar2(10 char);
BEGIN
FOR v_counter in 1..(key1,key2,key3..) LOOP
select NAME into v_name from EMP where KEY=v_counter;
DBMS_OUTPUT.PUT_LINE(v_name)
END;
END;
I would have made use of list_agg but only 10 values can be created as comma separated.
More then that it throws error like exceeded 4000
You can use a cursor:
DECLARE
v_name varchar2(10 char);
BEGIN
FOR v_row IN (SELECT key FROM your_key_table)
LOOP
select NAME into v_name from EMP where KEY=v_row.key;
DBMS_OUTPUT.PUT_LINE(v_name);
END LOOP;
END;
/
But you could combine the two queries into one:
DECLARE
v_name varchar2(10 char);
BEGIN
FOR v_row IN (SELECT e.name
FROM your_key_table ykt
INNER JOIN emp e
ON (ykt.key = e.key)
ORDER BY ykt.key)
LOOP
DBMS_OUTPUT.PUT_LINE(v_row.name);
END LOOP;
END;
/
db<>fiddle here
Instead of using a cursor, you can use BULK COLLECT to store all of the keys into a collection, then loop through that collection selecting and printing the data you are looking for.
DECLARE
TYPE keys_t IS TABLE OF your_schema.key_table.keys%TYPE;
l_keys keys_t;
l_name your_schema.emp.name%TYPE;
BEGIN
SELECT keys
BULK COLLECT INTO l_keys
FROM your_schema.key_table;
FOR i IN 1 .. l_keys.COUNT
LOOP
SELECT NAME
INTO l_name
FROM EMP
WHERE KEY = l_keys (i);
DBMS_OUTPUT.PUT_LINE (l_name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL; --No employee exists with that key
END LOOP;
END;
If you are just trying to tie the keys to EMPs, it would probably make more sense to just do a join.

PLSQL execute immediate ORA-00955 name is already used by an existing object

I have made an PL/SQL block which creates an table if it does not exist. If it exist then it truncates the table. When i execute the script i get the following error:
ORA-00955 name is already used by an existing object
Can you please help me to solve this error?
SCRIPT
DECLARE
nCount NUMBER;
vSqlStatement LONG;
BEGIN
SELECT count(1) into nCount FROM all_tables where table_name = 'CHANGES';
IF(nCount <= 0)
THEN
dbms_output.put_line (' Greather or equal to 0: ' || nCount);
vSqlStatement:='
CREATE TABLE ADMRAPPORT.CHANGES
(
"ID" NUMBER(10),
"VersionName" VARCHAR2(255),
"VersionNumber" NUMBER(10),
"Version_ID" NUMBER(10),
"VersionFlag" VARCHAR2(255)
)';
execute immediate vSqlStatement;
END IF;
IF(nCount > 0)
THEN
dbms_output.put_line (' Smaller then 0');
vSqlStatement:='TRUNCATE TABLE ADMRAPPORT.CHANGES';
execute immediate vSqlStatement;
END IF;
END;
This would all be a lot simpler if could run the script as the ADMRAPPORT user and remove references to the schema name.
Currently you check ALL_TABLES for any table named CHANGES belonging to any schema. ALL_TABLES only shows you tables you have access to, which means tables that have been granted to you or to a role you have. Two ways this could go wrong:
Some other schema (not ADMRAPPORT) has a table named CHANGES and grants you access to it. Your count result will be 1 so your code will stop without creating ADMRAPPORT.CHANGES.
ADMRAPPORT.CHANGES does in fact exist but it's not granted to you, so your count result is 0 because the table isn't shown in ALL_TABLES. Then the code goes ahead and tries to create it, which fails. Maybe this is what's happening here.
If you can call the script as the ADMRAPPORT user, try this simplified version:
declare
object_already_exists exception;
pragma exception_init(object_already_exists, -955);
vsqlstatement long :=
'create table changes
( id number(10)
, versionname varchar2(255)
, versionnumber number(10)
, version_id number(10)
, versionflag varchar2(255) )';
begin
execute immediate vsqlstatement;
dbms_output.put_line('Table created');
exception
when object_already_exists then
execute immediate 'truncate table changes';
dbms_output.put_line('Table truncated.');
end;
I would also add some constraints (is ID the primary key? should any columns be NOT NULL? etc), and consider grants and synonyms as well.
Note I have removed the double-quotes from your column names, because those just create confusion and bugs later on.
Try using DBA_OBJECTS instead of ALL_TABLES. If you don't have access to this table, then you have to handle exception as suggested by William
Did you copy/paste the script correctly? For example, you have:
IF(nCount <= 0)
THEN
dbms_output.put_line (' Greather or equal to 0: ' || nCount);
and later
IF(nCount > 0)
THEN
dbms_output.put_line (' Smaller then 0');

How to select all rows from the oracle PL/SQL collection into SYS_REFCURSOR

Note: I have seen many solution and all says I can not use SQL with a PL/SQL type. I must have to use CREATE or REPLACE, but my restriction is I can not use system object for this task.
What I have tried the below example returns only last row.
create or replace PROCEDURE SP_TEST (TEST_cursor OUT SYS_REFCURSOR)IS
TYPE TEMP_RECORD IS RECORD(
entries NUMBER,
name VARCHAR2(50),
update VARCHAR2(200)
);
TYPE TEMP_TABLE IS TABLE OF TEMP_RECORD INDEX BY PLS_INTEGER;
VAR_TEMP TEMP_TABLE;
IDX PLS_INTEGER := 0;
BEGIN
VAR_TEMP(IDX).cur_entries := 1;
VAR_TEMP(IDX).cur_entries := 2;
OPEN TEST_cursor FOR
SELECT VAR_TEMP(idx).cur_entries from dual;
END SP_TEST;
Another way tried.
OPEN TEST_cursor FOR
SELECT * FROM TABLE(VAR_TEMP)
--- It gives compilation error ora-
Given that you can't create an object in the database, the only solution I can think of is to use dynamic SQL:
CREATE TYPE temp_record AS OBJECT
(
entries NUMBER,
entry_name VARCHAR2 (50),
update_value VARCHAR2 (200)
);
CREATE TYPE temp_table IS TABLE OF temp_record;
CREATE OR REPLACE PROCEDURE sp_test (test_cursor OUT SYS_REFCURSOR) IS
var_temp temp_table := temp_table ();
strSql VARCHAR2(32767);
BEGIN
-- Populate the temp table, or pass it in from elsewhere
var_temp.EXTEND();
var_temp (var_temp.LAST).entries := 1;
var_temp (var_temp.LAST).entry_name := 'test';
FOR i IN 1..var_temp.COUNT LOOP
strSql := strSql ||
CASE
WHEN LENGTH(strSql) > 0 THEN ' UNION ALL '
ELSE NULL
END ||
'SELECT ' || var_temp.ENTRIES || ' ENTRIES,' ||
'''' || var_temp.ENTRY_NAME || ''' ENTRY_NAME FROM DUAL';
END LOOP;
OPEN test_cursor FOR strSql;
END sp_test;
Now, I may have messed up the string concatenation logic there a bit, but the objective is to end up with an SQL string which looks something like
SELECT 1 ENTRIES,'test' ENTRY_NAME FROM DUAL
UNION ALL
SELECT 2 ENTRIES,'test 2' ENTRY_NAME FROM DUAL
UNION ALL
SELECT 3 ENTRIES,'test_3' ENTRY_NAME FROM DUAL
but, of course, without the nice white space and etc.
The 32K limit on dynamic SQL may bite you eventually, but if push comes to shove you can the DBMS_SQL package to handle arbitrarily large SQL text, although that presents its own challenges.
Best of luck.
In order to reference types in SQL (as opposed to PL/SQL), they must be created as objects in the database. This is effectively a scope issue: when you run SQL you are shifting to a different context. Any structures that you have created locally are not available there.
CREATE TYPE temp_record AS OBJECT
(
entries NUMBER,
entry_name VARCHAR2 (50),
update_value VARCHAR2 (200)
);
CREATE TYPE temp_table IS TABLE OF temp_record;
CREATE OR REPLACE PROCEDURE sp_test (test_cursor OUT SYS_REFCURSOR) IS
var_temp temp_table := temp_table ();
BEGIN
var_temp.EXTEND ();
var_temp (var_temp.LAST).entries := 1;
var_temp (var_temp.LAST).entry_name := 'test';
OPEN test_cursor FOR SELECT * FROM TABLE (var_temp);
END sp_test;

Pl SQL Oracle PLS-00103: Encountered the symbol "CREATE"

this code takes input for zipcode, city, and state and then inserts that into the table created Address. Prior to inserting data it will check if the zipcode is already in the table, if so calling procedure(error) to display an error code.
Im getting an error code pls-00103: encountered the symbol "CREATE" when trying to execute the code. Here is my code so far. Thanks for any help in advance.
drop table address;
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
create or replace procedure error as
begin
dbms_output.put_line('Error Zip Code already found in table');
end error;
declare
zzip number;
ccity varchar2(30);
sstate varchar2(30);
create or replace procedure location(p_zipcode NUMBER,
p_city varchar2,
p_state varchar2) is
zip address.zipcode%type;
cit address.city%type;
st address.state%type;
begin
select count(*) from address into zip where zipcode = zip;
if any_rows_found then
error;
else
Insert into address values(zip, cit, st);
end if;
end location;
begin
select &zipcode into zzip from dual;
select &city into ccity from dual;
select &state into sstate from dual;
procedure location(zzip, ccity, sstate);
end;
/
I'm not sure what you're trying to do, but the following may be closer to what you had in mind:
drop table address;
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
declare
zzip number;
ccity varchar2(30);
sstate varchar2(30);
procedure error is
begin
dbms_output.put_line('Error Zip Code already found in table');
end error;
procedure location(p_zipcode NUMBER, p_city varchar2, p_state varchar2) is
zip_count NUMBER;
begin
select count(*)
into zip_count
from address
where zipcode = p_zipcode;
if zip_count > 0 then
error;
else
Insert into address
(zipcode, city, state)
values
(p_zipcode, p_city, p_state);
end if;
end location;
begin
select &zipcode into zzip from dual;
select &city into ccity from dual;
select &state into sstate from dual;
location(zzip, ccity, sstate);
end;
/
Best of luck.
I dont know if I understand your problem correctly, but there are certain correction I'd like to address to answer your problem
First, if you are going to create a procedure/function, do it in a separate worksheet then compile it. Dont compile it together with other anonymous blocks because most of the time, if you dont end your other blocks with '/', errors will surely generate.
Second, your DECLARE statement is misplaced, if you are going to make an anonymous block, make sure DECLARE, BEGIN and END are in line, dont create a procedure/function inside an anonymous block.
Third, you are declaring variables in your procedures and using them but doesnt have an initial value, so it will just pass a null value to the DML statement in your procedure. just use the parameter directly.
Fourth, avoid creating a procedure that only contains dbms_output.put_line. Its silly.
Lastly, your anonymous block that should be calling your procedure, uses '&', please avoid using '&' inside pl/sql as '&' is a feature in SQL*Plus and doesnt have any meaning in PL/SQL, instead, you can use ':' as for binding variables. But you use '&' not in binding variables so you should remove that;
Try this:
drop table address;
/
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
/
create or replace procedure location(p_zipcode NUMBER,
p_city varchar2,
p_state varchar2) is
zip address.zipcode%type;
begin
select count(*)
from address
into zip
where zipcode = p_zipcode
and city =p_city
and state = p_state;
if zip > 0 then
dbms_output.put_line('Error Zip Code already found in table');
else
Insert into address values(p_zipcode, p_city, p_state);
end if;
end location;
/
begin
location(:zzip, :ccity, :sstate);
end;

Creating and reading from a table in the same SQL script

I am developing a SQL script in SQL Developer which will obfuscate personal data in a schema using Oracle SQL. The script looks into a table called "OBFUS_TABLE" which contains a list of which tables and columns need to be obfuscated and how. It then loops through the table, altering the data as it goes.
I have tested the actual loop and obfuscate process and it works fine, I have also successfully tested the beginning of the script up to just before the loop, which creates OBFUS_TABLE and inserts the values into it. The problem comes when it tries to do the two together, failing on a "table or view does not exist" error when it attempts to execute the loop. Snippet of code below:
alter session set current_schema = SYSTEM;
DECLARE
t_count NUMBER;
t_count2 NUMBER;
p_tname VARCHAR2(100);
p_cname VARCHAR2(100);
l_datatype VARCHAR2(100);
BEGIN
SELECT COUNT(*) INTO t_count FROM all_tables WHERE table_name = 'OBFUS_TABLE';
SELECT COUNT(*) INTO t_count2 FROM all_tables WHERE table_name = 'OBFUS_LOG';
IF (t_count = 0)
THEN
EXECUTE immediate 'create table OBFUS_TABLE( TABLENAME VARCHAR2(200 BYTE), COLUMNNAME VARCHAR2(200 BYTE), DATA_TYPE VARCHAR2(20 BYTE), ACTIVE VARCHAR(1 BYTE) )';
END IF;
IF (t_count2 = 0)
THEN
EXECUTE immediate 'CREATE TABLE OBFUS_LOG (SRC_TABLENAME VARCHAR2(50 BYTE), SRC_TABLE_ROW_COUNT NUMBER, COPY_TABLENAME VARCHAR2(50 BYTE), COPY_TABLE_ROW_COUNT NUMBER, UPDATE_DATE TIMESTAMP(6) )';
END IF;
EXECUTE immediate 'INSERT INTO OBFUS_TABLE VALUES (''OB_MYTABLE1'',''SRNM'',''NAME'',''Y'')';
COMMIT;
FOR x IN (SELECT TABLENAME, COLUMNNAME, DATA_TYPE FROM OBFUS_TABLE WHERE ACTIVE='Y')
LOOP
p_tname := upper(x.TABLENAME); -- Table name
p_cname := upper(x.COLUMNNAME); -- Column name
l_datatype := upper(x.DATA_TYPE);
dbms_output.put_line('Started: '||TO_CHAR(sysdate,'YYYY/MM/DD HH24:MI:SS'));
END LOOP;
END;
NB: There are actually around 30 insert statements in exactly the same format as the one above. I removed them since they would pad out this post too much, but I have manually checked every insert statement and they're all correct.
I assume the problem is that SQL Developer does a "sanity check" on the code before running, and looks ahead to the loop and realises OBFUS_TABLE doesn't exist, but fails to understand that by the time that piece of code is executed, OBFUS_TABLE will definitely exist.
Is there a way to get around this? I thought maybe a GOTO statement might help but no luck. I would rather keep the solution as one single script rather than two seperate ones, but if the only way around this is to do so then I could do that I suppose. Any help would be much appreciated.
You will need to use dynamic SQL for the select like this:
declare
...
l_tname varchar2(100);
l_cname varchar2(100);
l_datatype varchar2(100);
rc sys_refcursor;
begin
...
open rc for 'SELECT TABLENAME, COLUMNNAME, DATA_TYPE
FROM OBFUS_TABLE WHERE ACTIVE=''Y''';
loop
fetch rc into l_tname, l_cname, l_datatype;
exit when rc%notfound;
dbms_output.put_line('Started: '||TO_CHAR(sysdate,'YYYY/MM/DD HH24:MI:SS'));
end loop;
close rc;
end;