Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I Have Two Tables
first :
CREATE TABLE z_names (ID number,
NAME VARCHAR2(200))
Second:
CREATE TABLE Z_FNAME
("FAMILY" VARCHAR2(200 BYTE),
"ID" NUMBER,
"NAME" VARCHAR2(200 BYTE),
"NAME_ID" NUMBER
)
How Write Procedure for Insert Into Second Table By String ,With This Conditions :
1- Example Of input String : nam1;fam1,nam2;fam2 => nam1 Insert Into NAME Column And fam1 Into FAMILY
2-ID Generated By Trigger I Have Writed Before
3-NAME_ID Comes From FIRST table
With a table you previously created (I remember that question so I reused it, as well as the sequence), you'd split input string into rows and fetch names, somehow; I chose regular expressions, presuming that names consist of only one word.
Table with names (inserted previously):
SQL> select * From z_names;
ID NAME
---------- --------------------
1 john
2 jim
3 jack
Procedure expects that strings you're passing as parameters contain name which is already inserted into the z_names table.
SQL> create or replace procedure p_test (par_string in varchar2) is
2 begin
3 insert into z_fname (family, id, name, name_id)
4 with temp as
5 (select regexp_substr(par_string, '[^,]+', 1, level) nf
6 from dual
7 connect by level <= regexp_count(par_string, ',') + 1
8 )
9 select regexp_substr(t.nf, '\w+', 1, 2) family,
10 z_names_seq.nextval id,
11 regexp_substr(t.nf, '\w+', 1, 1) name,
12 n.id
13 from temp t join z_names n on n.name = regexp_substr(t.nf, '\w+', 1, 1);
14 end;
15 /
Procedure created.
Testing:
SQL> exec p_test('john;Little,jack;Foot,jim;Bigfoot');
PL/SQL procedure successfully completed.
SQL> select * from z_fname;
FAMILY ID NAME NAME_ID
---------- ---------- -------------------- ----------
Little 10 john 1
Bigfoot 11 jim 2
Foot 12 jack 3
SQL>
However, from my point of view, that's somewhat awkward approach. I don't know what tables you use in this exercise represent, but I'd expect them to be related to each other (via referential integrity constraint). It also means that z_fname most probably isn't normalized - you would store only the foreign key constraint value which points to the master table (z_names), not store both name AND id.
It's just a template and you should scheck it and modify as you want (that's all what i can do with all data than you give me):
CREATE OR REPLACE PROCEDURE some_name
(name_in IN varchar2, fam_in IN varchar2)
IS
max_id_names number; --variable for max id from z_names and new name
max_id_fam number; --variable for max id from Z_FNAME
BEGIN
--1. Find max id from "ID Generated By Trigger I Have Writed Before" and SET IT TO max_id_names
INSERT INTO z_names
(
max_id_names,
NAME
)
VALUES
(
:max_id_names, --you will find it
name_in -- IN param
);
--2. Find max id from "ID Generated By Trigger I Have Writed Before" and SET IT TO max_id_fam
INSERT INTO Z_FNAME
(
FAMILY,
ID,
NAME,
NAME_ID
)
VALUES
(
fam_in, -- IN param
:max_id_fam, -- you will find it
name_in, --IN param
:max_id_names --you already have it because you inserted the names
);
END;
/
Related
we have an api operation that enters a row into our table with a report_type=5, is there some sort of operation i can apply to the table to make it so whenever a record gets entered or pulled
with a report_id=12 it returns the report_type as 4?
As commented, trigger would do. Here's an example.
Sample table:
SQL> create table test
2 (report_id number,
3 report_type number);
Table created.
Trigger:
SQL> create or replace trigger trg_bi_test
2 before insert on test
3 for each row
4 when (new.report_id = 12)
5 begin
6 :new.report_type := 4;
7 end;
8 /
Trigger created.
Testing:
SQL> insert into test (report_id, report_type) values (1, 13);
1 row created.
SQL> insert into test (report_id, report_type) values (12, 99);
1 row created.
SQL> select * from test;
REPORT_ID REPORT_TYPE
---------- -----------
1 13
12 4 --> I inserted report_type = 99, but trigger modified it to 4
--> because report_id = 12
SQL>
It's not clear which value you want to be STORED in the database: 12 (as entered), or 4 (as translated).
A trigger as proposed by another commenter would certainly be able to translate the value on insert or update.
If you want the original value to be stored, you'd need to set up a different column, that is derived based on the original one. An example swiped from an Oracle publication:
create table PERSON (
(employee_id integer,
employee_id_disp computed by
SUBSTRING (CAST(employee_id + 100000 as VARCHAR (6)) from 2)
);
In your case, you might do something like
create table MYTABLE (
somekey varchar(20) not null,
entered_office int,
display_office computed by decode(entered_office,12,4,entered_office)
);
Then, anything that needs to display the office number would need to use the display_office field, not the entered one. Any tool that does an insert into the table would also need to insert the entered_office field, as display_office is not updateable.
I have procedure like this...
declare
v_psg varchar2(10);
id_no number;
begin
select value into v_psg from settings_am where key = 'PSG';
select id into id_no from product where to_char(psg) in (v_psg);
end;`
The value returned from select value into v_psg from settings_am where key = 'PSG'; would be
'1','2','3'
when i run this procedure i am returned with ora error - ORA-01403.
please advise how i should pass the v_psg value to psg column of product table?
EDIT - Tried with test case suggested
If you got ORA-01403, you were kind of lucky. It is the NO_DATA_FOUND error, which means that one (probably the first) query didn't return anything.
Those two statements could be combined into
select id
from product
where to_char(psg) in (select value
from settings_am
where key = 'PSG'
);
Why would you select value first, and then use it in another query? Besides, it just wouldn't work. v_psg is declared as VARCHAR2 variable. The way you described it, it contains the following string: '1','2','3', as if this is what you have:
SQL> create table settings_am (key varchar2(10),
2 value varchar2(20)); --> note size here
Table created.
SQL> insert into settings_am (key, value)
2 values ('PSG', q'['1','2','3']');
1 row created.
SQL> select * From settings_am;
KEY VALUE
---------- --------------------
PSG '1','2','3'
SQL>
As you can see, I enlarged the value column size, although variable you declared says 10. Why? Because of
SQL> select length(value) from settings_am where key = 'PSG';
LENGTH(VALUE)
-------------
11
i.e. you can't put something that is long 11 into something that accepts length 10.
Or, if your data actually contains 3 rows for the PSG key, are those values already enclosed into single quotes? If so, that's strange; people usually don't do that. Anyway, suppose that you managed to get string '1,2,3' (which is what I presume you actually have) into a VARCHAR2 variable, then you have to split it into rows in order to be able to use it in the IN clause:
SQL> create table product (id number, psg varchar2(10));
Table created.
SQL> insert into product (id, psg) values (100, '1');
1 row created.
SQL> insert into product (id, psg) values (200, '2');
1 row created.
SQL>
Query is then (where lines #3 - 5 represent splitting a string into rows):
SQL> select p.id
2 from product p
3 where p.psg in (select regexp_substr('&&v_psg', '[^,]+', 1, level)
4 from dual
5 connect by level <= regexp_count('&&v_psg', ',') + 1
6 );
Enter value for v_psg: 1,2,3
ID
----------
100
200
So, wouldn't it be simpler to use
SQL> select id
2 from product
3 where to_char(psg) in (select value
4 from settings_am
5 where key = 'PSG'
6 );
ID
----------
100
200
SQL>
Note that both options also show why your query is wrong: you can't put two values (rows) into a variable declared as id_no number; as you'd get TOO_MANY_ROWS error.
Finally, what is it that you'd want to do? What problem are you trying to solve? Apparently, except for special cases (only one row for each value) your query can't work. If you could provide test case (create table & insert into sample data), as well as expected output, it would be easier to help you.
I created a table in sqlplus using
create table employee(id number(1) primary key, name varchar(5));
then I wanted to enter data into table using loop so I implemented
SQL> begin
2 for i in 1..4
3 loop
4 insert into employee
5 values(i, '&name');
6 end loop;
7 end;
8 /
It showed me a prompt to enter name once
Enter value for name: a
Instead of running for 4 times and asking to input name everytime it just ran once.
After checking the output, I found my table as
SQL> select * from employee;
ID NAME
---------- ----
1 a
2 a
3 a
4 a
I want to enter different names for every employee id, not the same name for all the customer.
I want to create a sequence for this varchar. It would have been easier had it been a number instead of varchar. In that case, I could do
seq_no := seq_no + 1;
But what can I do when I want to store next value in column as A0000002, when the previous value was A0000001 (to increment the number in the next varchar rowby 1)?
This can be done by
to_char(seq_no,'FM0000000')
your example can be done by creating sequence in oracle
create sequence seq_no start with 1 increment by 1;
then
select 'A'||to_char(seq_no.nextval,'FM0000000') from dual;
Right now i have used in dual ..but place this
'A'||to_char(seq_no.nextval,'FM0000000')
in your required query ..this will create sequence as you mentioned
sqlfiddle
Sequences are purely numeric. However, you need a trigger anyway, so simply adapt such trigger to insert the desired prefix:
CREATE OR REPLACE TRIGGER FOO_TRG1
BEFORE INSERT
ON FOO
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
IF :NEW.FOO_ID IS NULL THEN
SELECT 'A' || TO_CHAR(FOO_SEQ1.NEXTVAL, 'FM0000000') INTO :NEW.FOO_ID FROM DUAL;
END IF;
END FOO_TRG1;
/
ALTER TRIGGER FOO_TRG1 ENABLE;
If you're able I'd actually use a virtual column as defined in the CREATE TABLE syntax. It makes it more easily extensible should the need arise.
Here's a working example.
SQL> create table tmp_test (
2 id number(7,0) primary key
3 , col1 number
4 , seq varchar2(8 char) generated always as (
5 'A' || to_char(id, 'FM0999999'))
6 );
Table created.
SQL>
SQL> create sequence tmp_test_seq;
Sequence created.
SQL>
SQL> create or replace trigger tmp_test_trigger
2 before insert on tmp_test
3 for each row
4 begin
5
6 :new.id := tmp_test_seq.nextval;
7 end;
8 /
Trigger created.
SQL> show errors
No errors.
SQL>
SQL> insert into tmp_test (col1)
2 values(1);
1 row created.
SQL>
SQL> select * from tmp_test;
ID COL1 SEQ
---------- ---------- --------------------------------
1 1 A0000001
Having said that; you would be better off if you did not do this unless you have an unbelievably pressing business need. There is little point to making life more difficult for yourself by prepending a constant value onto a number. As A will always be A it doesn't matter whether it's there or not.
If the format is always a letter followed by 7 digits you can do:
sequence = lpad(substr(sequence,2,7)+1,7,'0')
I have a question about stored procedures in Oracle.
Below is the stored procedure and tables as it stands:
create table STORES
(
ID number,
NAME varchar2(100),
CITY varchar2(100),
EXPIRES DATE
)
insert into stores values(1, 'Store 1', 'City 1', sysdate);
insert into stores values(2, 'Store 2', 'City 1', sysdate);
insert into stores values(3, 'Store 3', 'City 2', sysdate);
create table CLOSED
(
ID number,
NAME varchar2(100),
CITY varchar2(100)
)
create or replace PROCEDURE
pr_TestProc(subQuery IN VARCHAR2)
IS
begin
insert into CLOSED (ID, NAME, CITY)
select ID, NAME, CITY
from STORES
where ID in (1, 2, 3);
end;
What I'd like to do is replace the "in" values with the subQuery passed in as a parameter.
So if I run the procedure like:
execute pr_TestProc('select ID from STORES where EXPIRES <= sysdate');
The query passed in should be executed as a subquery being run inside the procedure.
Something like:
insert into CLOSED (ID, NAME, CITY) select ID, NAME, CITY
from STORES
where ID in (execute(subQuery));
Obviously this doesn't work, but what would be the best way to achieve this, or is it even possible?
Thanks,
Brian
You can use dynamic SQL
create or replace PROCEDURE pr_TestProc(subQuery IN VARCHAR2)
IS
l_sql_stmt varchar2(1000);
begin
l_sql_stmt := 'insert into CLOSED (ID, NAME, CITY) ' ||
' select ID, NAME, CITY ' ||
' from STORES ' ||
' where id in (' || subquery || ')';
dbms_output.put_line( l_sql_stmt );
EXECUTE IMMEDIATE l_sql_stmt;
end;
SQL> execute pr_TestProc('select ID from STORES where EXPIRES <= sysdate');
PL/SQL procedure successfully completed.
SQL> column name format a20
SQL> column city format a20
SQL> select * from closed;
ID NAME CITY
---------- -------------------- --------------------
1 Store 1 City 1
2 Store 2 City 1
3 Store 3 City 2
If you are calling this procedure rarely in a system that is relatively idle, that will probably work acceptably well. If you are calling it frequently with different subqueries, however, you are going to generate a ton of non-sharable SQL statements. That will force Oracle to do a lot of hard parsing. It will also flood your shared pool with non-sharable SQL statements, likely forcing out plans that you want to be cached, forcing more hard parsing when those SQL statements are then executed again. And if you do it fast enough, you're likely to end up getting errors (or causing other processes to get errors) that Oracle couldn't allocate enough memory in the shared pool for a particular query. Plus, dynamic SQL is harder to write, harder to debug, vulnerable to SQL injection attacks, etc. so it generally makes the system harder to deal with.
A more elegant solution would be to pass in a collection rather than a subquery would be to pass in a collection
SQL> create type id_coll
2 as table of number;
3 /
Type created.
SQL> ed
Wrote file afiedt.buf
1 create or replace PROCEDURE pr_TestProc( p_ids IN id_coll)
2 is
3 begin
4 insert into CLOSED (ID, NAME, CITY)
5 select ID, NAME, CITY
6 from STORES
7 where ID in (select column_value
8 from table( p_ids ) );
9* end;
SQL> /
Procedure created.
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_ids id_coll;
3 begin
4 select id
5 bulk collect into l_ids
6 from stores
7 where expires <= sysdate;
8 pr_TestProc( l_ids );
9* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> select * from closed;
ID NAME CITY
---------- -------------------- --------------------
1 Store 1 City 1
2 Store 2 City 1
3 Store 3 City 2
Maybe you dont need to pass the query into the stored procedure. Just call the stored procedure with the query as a parameter.
create or replace PROCEDURE
pr_TestProc(listOfIds_IN IN integer)
IS
begin
ids integer := listOfIds_IN;
insert into CLOSED (ID, NAME, CITY)
select ID, NAME, CITY from STORES where ID in (ids );
end;
Call the stored procedure like this:
pr_TestProc(SELECT id FROM Table WHERE condition)