So this code is basically trying to add 5 cars into the location called 'garage' where the 'car_id' is + 1 so each 'car_id' should be different with the use of a package called 'Package_Store.cars'
For example, car0001 should have a 'car_id' of 1 and car0002 should have a 'car_id' of 2 etc.
I think the issue I am getting is with the 'car_id' as it is not incrementing for car0002 till car0005
This is the error message.
00000 - "unique constraint (%s.%s) violated"
*Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
The code
Your code always inserts the same CAR_ID, hence the error.
However, even if you fix it, it'll work in a single-user environment. If two (or more) users run the same code, you're about to hit the same error again because sooner or later they will generate the same MAX + 1 value. What to do? Switch to a sequence.
If this is just for your own amusement, then you might e.g.
SQL> DECLARE
2 car_id NUMBER;
3 car_str VARCHAR2 (10);
4 BEGIN
5 FOR i IN 1 .. 5
6 LOOP
7 SELECT MAX (id) + i
8 INTO car_id
9 FROM vehicles
10 WHERE id < 10;
11
12 car_str := 'car' || LPAD (i, 4, '0');
13
14 dbms_output.put_line('car_id = ' || car_id ||', car_str = ' || car_str);
15
16 Package_Store.cars (car_id, 'garage', car_str, 1, 4);
17 END LOOP;
18 END;
19 /
car_id = 4, car_str = car0001
car_id = 5, car_str = car0002
car_id = 6, car_str = car0003
car_id = 7, car_str = car0004
car_id = 8, car_str = car0005
PL/SQL procedure successfully completed.
SQL>
You may also use plain SQL INSERTfor this simple task
insert into garage
(car_id, location, col1, col2)
select
rownum car_id, 'car'|| to_char(rownum,'FM0999'),1,4
from dual
connect by level <= 5;
select * from garage
CAR_ID LOCATION COL1 COL2
---------- ---------- ---------- ----------
1 car0001 1 4
2 car0002 1 4
3 car0003 1 4
4 car0004 1 4
5 car0005 1 4
If the table is not empty, adjust the INSERTas follows to use new ids and car names
insert into garage
(car_id, location, col1, col2)
with ci as
(select nvl(max(car_id),0) car_id from garage)
select
car_id+rownum, 'car'|| to_char(car_id+rownum,'FM0999'),1,4
from ci
connect by level <= 5;
Related
create or replace type pb_calculate_bill_ot as object ( bill id number(9),
version number(2),
rate number(5),
descp varchar2(25)
);
create or replace type pb_calculate_bill_ct is table of pb_calculate_bill_ot;
----- inside a package -----
procedure select_price ( pi_bill id in number,
pi_version in number,
po_data_ct out pb_calculate_bill_ct) IS
lt_calculate_bill_ct pb_calculate_bill_ct := pb_calculate_bill_ct();
-- procedure functionality--
end select_price;
--------- calling this proc inside same pkg ----------
pkg.select_price ( pi_bill_id => pi_bill,
pi_version => pi_version,
po_data_ct => lt_calculate_bill_ct);
how to take dbms_output of lt_calculate_bill_ct ???
Here's an example.
Sample table:
SQL> CREATE TABLE bill
2 AS
3 SELECT 1 bill_id, 20 version, 1234 rate, 'Little' descp FROM DUAL
4 UNION ALL
5 SELECT 2, 13, 434, 'Foot' FROM DUAL;
Table created.
Types you created:
SQL> CREATE OR REPLACE TYPE pb_calculate_bill_ot AS OBJECT
2 (
3 bill_id NUMBER (9),
4 version NUMBER (2),
5 rate NUMBER (5),
6 descp VARCHAR2 (25)
7 );
8 /
Type created.
SQL> CREATE OR REPLACE TYPE pb_calculate_bill_ct IS TABLE OF pb_calculate_bill_ot;
2 /
Type created.
Sample procedure:
SQL> CREATE OR REPLACE PROCEDURE select_price (
2 pi_bill_id IN NUMBER,
3 pi_version IN NUMBER,
4 po_data_ct OUT pb_calculate_bill_ct)
5 IS
6 lt_calculate_bill_ct pb_calculate_bill_ct := pb_calculate_bill_ct ();
7 BEGIN
8 SELECT pb_calculate_bill_ot (bill_id,
9 version,
10 rate,
11 descp)
12 BULK COLLECT INTO lt_calculate_bill_ct
13 FROM bill
14 WHERE bill_id = pi_bill_id
15 AND pi_version = pi_version;
16
17 po_data_ct := lt_calculate_bill_ct;
18 END select_price;
19 /
Procedure created.
Testing (this is what you asked for): declare a local variable which will hold result returned by the procedure; then, in a loop, do something with the result - I displayed it using dbms_output.put_line:
SQL> DECLARE
2 l_res pb_calculate_bill_ct;
3 BEGIN
4 select_price (1, 20, l_res);
5
6 FOR i IN 1 .. l_res.COUNT
7 LOOP
8 DBMS_OUTPUT.put_line (
9 'Bill ID = '
10 || l_res (i).bill_id
11 || ', version = '
12 || l_res (i).version
13 || ', rate = '
14 || l_res (i).rate
15 || ', description = '
16 || l_res (i).descp);
17 END LOOP;
18 END;
19 /
Bill ID = 1, version = 20, rate = 1234, description = Little
PL/SQL procedure successfully completed.
SQL>
I want to write a select query to get 4 activities against profile id= 1.
Aha; screenshot reveals the secret - activity ID is passed as a comma-separated values string having 4 values (e.g. '1,6,7,8') which make those "4 activities"; each of them should be stored into its own row.
Sample table:
SQL> create table activity_profile_mapping
2 (profile_id number,
3 activity_id number,
4 created_by varchar2(10),
5 created timestamp
6 );
Table created.
Procedure: the trick is to split p_activity_id into rows.
SQL> create or replace procedure apm_add
2 (p_profile_id in activity_profile_mapping.profile_id%type,
3 p_activity_id in varchar2,
4 p_created_by in varchar2
5 )
6 as
7 /* P_ACTIVITY_ID is passed as comma-separated values string and
8 contains 4 values, e.g. '1,6,7,8'. They represent "4 activities
9 from the title
10 */
11 begin
12 insert into activity_profile_mapping
13 (profile_id,
14 activity_id,
15 created_by,
16 created
17 )
18 select p_profile_id,
19 regexp_substr(p_activity_id, '[^,]+', 1, level),
20 p_created_by,
21 systimestamp
22 from dual
23 connect by level <= regexp_count(p_activity_id, ',') + 1;
24 end apm_add;
25 /
Procedure created.
Testing:
SQL> begin
2 apm_add (p_profile_id => 100,
3 p_activity_id => '1,6,7,8',
4 p_created_by => 'Littlefoot');
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> select * from activity_profile_mapping;
PROFILE_ID ACTIVITY_ID CREATED_BY CREATED
---------- ----------- ---------- ------------------------------
100 1 Littlefoot 07.12.21 19:51:52,480000
100 6 Littlefoot 07.12.21 19:51:52,480000
100 7 Littlefoot 07.12.21 19:51:52,480000
100 8 Littlefoot 07.12.21 19:51:52,480000
SQL>
I'm trying to create a view that access all the tables starting with the same name, they have the exactly same structure, and in time there will be more.
Table Names:
TEMP_ENTITIES_1000
TEMP_ENTITIES_1001
TEMP_ENTITIES_1002
and in the future there will be
TEMP_ENTITIES_1003
TEMP_ENTITIES_1004
and so on...
What I need is to use list of tables from the following script and then use the result the list of object name result to access in a view.
select object_name
from user_objects
where object_type = 'TABLE'
and object_name like upper('temp_entities_%');
create view entities_join as
select * from (object_name)
Is it possible to achieve?
In modern versions of Oracle you may use SQL table macros without scheduled anything. It will build dynamic query on-the-fly and may be used as a plain view.
Below is the example:
SQL> insert all
2 when mod(rownum, 5) = 0 then into TEMP_ENTITIES_1000 values (l, dt, val)
3 when mod(rownum, 5) = 1 then into TEMP_ENTITIES_1001 values (l, dt, val)
4 when mod(rownum, 5) = 2 then into TEMP_ENTITIES_1002 values (l, dt, val)
5 when mod(rownum, 5) = 3 then into TEMP_ENTITIES_1003 values (l, dt, val)
6 when mod(rownum, 5) = 4 then into TEMP_ENTITIES_1004 values (l, dt, val)
7
8 select
9 level as l,
10 sysdate + level as dt,
11 level as val
12 from dual
13 connect by level < 10;
9 rows inserted.
SQL>
SQL> create or replace function f_temp_entities_union
2 /*Create a macro*/
3 return varchar2 SQL_MACRO
4 is
5 v_union varchar2(4000);
6 begin
7 select listagg('select ''' || table_name || ''' as src, a.* from ' || table_name || ' a ', chr(10) || 'union all' || chr(10))
8 into v_union
9 from all_tables
10 where table_name like 'TEMP_ENTITIES%'
11 and owner = sys_context('USERENV', 'CURRENT_SCHEMA')
12 ;
13
14 return v_union;
15 end;
16 /
Function F_TEMP_ENTITIES_UNION compiled
SQL>
SQL> select *
2 from f_temp_entities_union();
TEMP_ENTITIES_1000 5 20.09.21 5
TEMP_ENTITIES_1001 1 16.09.21 1
TEMP_ENTITIES_1001 6 21.09.21 6
TEMP_ENTITIES_1002 2 17.09.21 2
TEMP_ENTITIES_1002 7 22.09.21 7
TEMP_ENTITIES_1003 3 18.09.21 3
TEMP_ENTITIES_1003 8 23.09.21 8
TEMP_ENTITIES_1004 4 19.09.21 4
TEMP_ENTITIES_1004 9 24.09.21 9
9 rows selected.
SQL>
SQL> /*Add new table*/
SQL> create table TEMP_ENTITIES_1005 as select * from TEMP_ENTITIES_1001 where 1 = 0;
Table TEMP_ENTITIES_1005 created.
SQL> insert into TEMP_ENTITIES_1005
2 select 1000 + rownum, sysdate + rownum, rownum - 100 from TEMP_ENTITIES_1000;
1 row inserted.
SQL>
SQL> /*Check that new data is here*/
SQL> select *
2 from v_demo;
TEMP_ENTITIES_1000 5 20.09.21 5
TEMP_ENTITIES_1001 1 16.09.21 1
TEMP_ENTITIES_1001 6 21.09.21 6
TEMP_ENTITIES_1002 2 17.09.21 2
TEMP_ENTITIES_1002 7 22.09.21 7
TEMP_ENTITIES_1003 3 18.09.21 3
TEMP_ENTITIES_1003 8 23.09.21 8
TEMP_ENTITIES_1004 4 19.09.21 4
TEMP_ENTITIES_1004 9 24.09.21 9
TEMP_ENTITIES_1005 1001 16.09.21 -99
10 rows selected.
Is it possible? Yes, using dynamic SQL.
However, from what you described, it looks that data model you use is wrong. You should have only one table with identifier that makes the difference (those 1000, 1001, ... values).
Then you wouldn't need a view at all, and do everything with that single table. I suggest you consider doing so.
Here's an example of what you might do (the way you asked):
Several sample tables:
SQL> select * from temp_entities_1000;
ID NAME
---------- ------
1000 Little
SQL> select * from temp_entities_1001;
ID NAME
---------- ----
1001 Foot
SQL> select * from temp_entities_1002;
ID NAME
---------- -----
1002 Scott
Procedure:
SQL> DECLARE
2 l_str VARCHAR2 (2000);
3 BEGIN
4 FOR cur_r IN (SELECT object_name
5 FROM user_objects
6 WHERE object_type = 'TABLE'
7 AND object_name LIKE 'TEMP_ENTITIES%')
8 LOOP
9 l_str :=
10 l_str || ' select * from ' || cur_r.object_name || ' union all ';
11 END LOOP;
12
13 l_str :=
14 'create or replace view entities_join as '
15 || RTRIM (l_str, ' union all');
16
17 EXECUTE IMMEDIATE l_str;
18 END;
19 /
PL/SQL procedure successfully completed.
Result:
SQL> select * from entities_join;
ID NAME
---------- ------
1000 Little
1001 Foot
1002 Scott
SQL>
You'd have to run that procedure every time new table is created so that it is included into the view.
I have to add average and total number of ratings of the movie! Following are my tables and tiggers! Please improve this code!
create table fanrating
(
mov_id number(10),
rate_avg number(1,1),
rate_sum number,
rate_count number
);
create table rating
(
mov_id number(10) references movie(mov_id),
userid varchar(20),
rev_star number,
primary key(mov_id,rev_star)
);
delimiter $$
create or replace trigger bi_rating_trg
before insert on fanrating
for each row
begin
set rate_sum = 0;
set rate_count = 0;
set rate_avg = null;
end;
$$
create or replace trigger ai_rating_trg
after insert on rating
for each row
begin
update fanrating
set rate_sum = rate_sum + new.rev_star,
rate_count = rate_count + 1,
rate_avg = rate_sum / rate_count
where mov_id = new.mov_id;
end;
$$ delimiter;
It is showing error in all 3 set command in triggers and also error called encountered symbol $$, I am using Oracle SQL Delevepor!
A few objections:
you don't need the first (before insert) trigger
new and old values should be preceded by colon, i.e. :new and :old
there's no set command in PL/SQL (at least, I don't know anything about it). If you want to set a variable to some value, you'd do it as my_variable := :new.rate_star;
I have never used delimiter; don't know what you meant to do with it. Standard delimiters are semi-colon and slash, I'd suggest you to use them
Here's how I'd do what you are trying to do.
Tables first:
SQL> create table fanrating
2 (mov_id number(10),
3 rate_avg number,
4 rate_sum number,
5 rate_count number
6 );
Table created.
SQL> create table rating
2 (mov_id number(10),
3 userid varchar(20),
4 rev_star number,
5 primary key(mov_id,rev_star)
6 );
Table created.
Trigger uses merge command which is also known as "upsert" as it does update (which is what I do if there's a match) or insert (if there's none):
SQL> create or replace trigger ai_rating_trg
2 after insert on rating
3 for each row
4 begin
5 merge into fanrating f
6 using (select :new.rev_star rev_star,
7 :new.mov_id mov_id
8 from dual
9 ) x
10 on (f.mov_id = x.mov_id)
11 when matched then update set
12 f.rate_sum = f.rate_sum + :new.rev_star,
13 f.rate_count = f.rate_count + 1,
14 f.rate_avg = round((f.rate_sum + :new.rev_star) /
15 (f.rate_count + 1), 2)
16 when not matched then insert values
17 (:new.mov_id, :new.rev_star, :new.rev_star, 1);
18 end;
19 /
Trigger created.
Testing:
SQL> insert into rating (mov_id, userid, rev_star) values (1, 'Little', 3);
1 row created.
SQL> select * From fanrating;
MOV_ID RATE_AVG RATE_SUM RATE_COUNT
---------- ---------- ---------- ----------
1 3 3 1
SQL> insert into rating (mov_id, userid, rev_star) values (1, 'Foot', 1);
1 row created.
SQL> select * From fanrating;
MOV_ID RATE_AVG RATE_SUM RATE_COUNT
---------- ---------- ---------- ----------
1 2 4 2
SQL> insert into rating (mov_id, userid, rev_star) values (1, 'Scott', 4);
1 row created.
SQL> select * From fanrating;
MOV_ID RATE_AVG RATE_SUM RATE_COUNT
---------- ---------- ---------- ----------
1 2,67 8 3
SQL> insert into rating (mov_id, userid, rev_star) values (2, 'Foot', 5);
1 row created.
SQL> select * From fanrating;
MOV_ID RATE_AVG RATE_SUM RATE_COUNT
---------- ---------- ---------- ----------
1 2,67 8 3
2 5 5 1
SQL>
[EDIT: procedure + trigger]
If it has to be a procedure, then - as I commented - move MERGE into it, use values you insert as parameters. Here's how:
Procedure:
SQL> create or replace procedure p_mrg
2 (par_mov_id in number,
3 par_userid in varchar2,
4 par_rev_star in number
5 )
6 as
7 begin
8 merge into fanrating f
9 using (select par_rev_star rev_star,
10 par_mov_id mov_id
11 from dual
12 ) x
13 on (f.mov_id = x.mov_id)
14 when matched then update set
15 f.rate_sum = f.rate_sum + par_rev_star,
16 f.rate_count = f.rate_count + 1,
17 f.rate_avg = round((f.rate_sum + par_rev_star) /
18 (f.rate_count + 1), 2)
19 when not matched then insert values
20 (par_mov_id, par_rev_star, par_rev_star, 1);
21 end;
22 /
Procedure created.
Trigger now calls the procedure (instead of running MERGE itself):
SQL> create or replace trigger ai_rating_trg
2 after insert on rating
3 for each row
4 begin
5 p_mrg (:new.mov_id, :new.userid, :new.rev_star);
6 end;
7 /
Trigger created.
Additional test:
SQL> insert into rating (mov_id, userid, rev_star) values (2, 'Mike', 2);
1 row created.
SQL> select * from fanrating;
MOV_ID RATE_AVG RATE_SUM RATE_COUNT
---------- ---------- ---------- ----------
1 2,67 8 3
2 3,5 7 2
SQL>
You don't need to create such a trigger for the fanrating table, but need to define default values during the creation such as :
create table fanrating
(
mov_id number(10),
rate_avg number(1,1),
rate_sum number default 0,
rate_count number default 0
);
and create insert or update trigger for the rating table :
create or replace trigger ai_rating_trg
after insert or update on rating
for each row
begin
update fanrating
set rate_sum = nvl(rate_sum,0) + nvl(:new.rev_star,0),
rate_count = nvl(rate_count,0) + 1,
rate_avg = nvl(rate_sum,0) / nvl(rate_count,1)
where mov_id = :new.mov_id;
end;
/
where nvl(..,0) is used for converting null values to zero.
I'm trying to create a trigger which raise error if the total number of row exceeds 10, The plsql code was successfully compiled but it's not generating any error.
This is the plsql code:
SQL> CREATE OR REPLACE TRIGGER customer_count_check
2 BEFORE INSERT OR UPDATE ON customer2
3 FOR EACH ROW
4 DECLARE
5 count_customer NUMBER;
6 max_customer NUMBER := 10;
7 BEGIN
8 SELECT COUNT(*) INTO count_customer FROM customer2 WHERE cusid = :new.cusid;
9 IF count_customer >= max_customer THEN
10 RAISE_APPLICATION_ERROR (-20000,'Customer Table capacity exceeded');
11 END IF;
12 END;
13 /
i would say your trigger does't work as you expect, because your select statement inside return always one row.
you filter on a primary key!
that should work
SQL> CREATE OR REPLACE TRIGGER customer_count_check
2 BEFORE INSERT OR UPDATE ON customer2
3 FOR EACH ROW
4 DECLARE
5 count_customer NUMBER;
6 max_customer NUMBER := 10;
7 BEGIN
8 SELECT COUNT(*) INTO count_customer FROM customer2; --WHERE cusid = :new.cusid;
9 IF count_customer >= max_customer THEN
10 RAISE_APPLICATION_ERROR (-20000,'Customer Table capacity exceeded');
11 END IF;
12 END;
13 /