I want to add column but it should be 10 options to my database(PL/SQL).
My sql query is look like this:
ALTER TABLE mytable
ADD NEWCOL
Do you think that is it work?
I don't have to think, I know it won't work.
SQL> INSERT_INTO MYTABLE
2 (MYNEW_COL)
3 VALUES
4 (1,2,3,4,5,6,7,8,9,10);
INSERT_INTO MYTABLE
*
ERROR at line 1:
ORA-00900: invalid SQL statement
If you want to insert exactly those values, use a row generator:
SQL> insert into mytable (mynew_col)
2 select level from dual
3 connect by level <= 10;
10 rows created.
SQL> select * from mytable;
MYNEW_COL
----------
1
2
3
4
5
6
7
8
9
10
10 rows selected.
SQL>
Otherwise, discover other valid ways to do that, such as
SQL> insert into mytable (mynew_col)
2 select 1 from dual union all
3 select 2 from dual union all
4 select 3 from dual;
3 rows created.
or
SQL> insert all
2 into mytable (mynew_col) values (1)
3 into mytable (mynew_col) values (2)
4 into mytable (mynew_col) values (3)
5 select * from dual;
3 rows created.
SQL>
[EDIT] Ah, you turned the question upside down. If you want to add a new column and limit number of valid values, then:
SQL> alter table mytable add newcol number;
Table altered.
SQL> alter table mytable add constraint
2 ch_col check (newcol between 1 and 10);
Table altered.
Testing:
SQL> update mytable set newcol = 0;
update mytable set newcol = 0
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.CH_COL) violated
SQL> update mytable set newcol = 11;
update mytable set newcol = 11
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.CH_COL) violated
SQL> update mytable set newcol = 2;
16 rows updated.
SQL>
Related
I have a table "TEST_TABLE" with two columns TABLE_NAME and RECORD_COUNT.
enter image description here
We want to update the column RECORD_COUNT by taking the total records in table specified in TABLE_NAME.
You could do this with dynamic SQL but why be complicated?
Create a view
Create view my_tables as
Select
'table_1' as "table name",
count(*) as "rows"
From table_1
Add the following for each table
Union all
Select 'table_2',count(*) From table_2
You can then use the view like a table:
Select * from my_tables;
OK, but - why wouldn't you use information Oracle already provides for you? You should regularly gather statistics anyway, so:
SQL> execute dbms_stats.gather_schema_stats(ownname => 'SCOTT', estimate_percent => null);
PL/SQL procedure successfully completed.
and then fetch num_rows from user_tables:
SQL> select table_name, num_rows from user_tables where rownum <= 10;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
DUMMY 1
TBL_ERROR 1
AUDIT_TAB 2
SOURCE_DET 3
EXAMPLE 1
FLIGHT 0
10 rows selected.
SQL>
It can't be much worse than your attempt (due to possible frequent changes to tables' contents; inserts and deletes) because you'd collect your own data periodically (how often?) anyway.
If it has to be your way, then you'd use dynamic SQL, looping through all tables in your schema and actually count number of rows:
SQL> create table test_table
2 (table_name varchar2(30),
3 num_rows number);
Table created.
SQL> create or replace procedure p_test as
2 l_cnt number;
3 begin
4 execute immediate 'truncate table test_table';
5 for cur_R in (select table_name from user_tables) loop
6 execute immediate 'select count(*) from ' ||cur_R.table_name into l_Cnt;
7 insert into test_table (table_name, num_rows) values (cur_r.table_name, l_cnt);
8 end loop;
9 end;
10 /
Procedure created.
Running the procedure and viewing the result:
SQL> exec p_test;
PL/SQL procedure successfully completed.
SQL> select * From test_Table where rownum <= 10;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
DUMMY 1
TBL_ERROR 1
AUDIT_TAB 2
SOURCE_DET 3
EXAMPLE 1
FLIGHT 0
10 rows selected.
SQL>
Note that performance will suffer as number of tables and number of rows stored in them grows.
If I were you, I'd go for the first option.
CREATE OR REPLACE TRIGGER "DUPLICATE_FOO" AFTER INSERT ON "FOO" FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
insert into remote_foo values(:new); -- can I do this?
EXCEPTION
-- TODO log somewhere
END;
Is there an elegant way to create a trigger that basically duplicates a record from one table to another?
I would like to avoid having to specify the fields of the table since it will mean that the trigger would have to be updated in case there are changes in the schema (the remote scheme would be updated of course). I have like a dozen of tables more.
All the examples I have found always specify the fields in the insert (:new.fieldX).
The keyword here is NOT to specify column names, right? In my opinion, you should because that's the only way you can control it.
the remote scheme would be updated of course
is kind of dangerous. WHAT IF it doesn't happen? "Of course" works until it does not work.
Sample tables (both are empty):
SQL> create table foo (id number, name varchar2(20));
Table created.
SQL> create table remote_foo as select * From foo where 1 = 2;
Table created.
If you use a trigger which is an autonomous transaction, then it won't see :new pseudorecord (as this is an autonomous transaction; right?); to this trigger, select * from foo where id = :new.id; won't return anything and remote_foo remains empty:
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 for each row
4 declare
5 pragma autonomous_transaction;
6 begin
7 insert into remote_foo select * from foo where id = :new.id;
8 commit;
9 end;
10 /
Trigger created.
SQL> insert into foo (id, name) values (1, 'Littlefoot');
1 row created.
SQL> select * from foo;
ID NAME
---------- --------------------
1 Littlefoot
SQL> select * from remote_foo; --> it remained empty
no rows selected
SQL>
Note that - if you specified columns - it would work (but that's not what you wanted):
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 for each row
4 declare
5 pragma autonomous_transaction;
6 begin
7 insert into remote_foo (id, name) values (:new.id, :new.name);
8 commit;
9 end;
10 /
Trigger created.
SQL> insert into foo (id, name) values (2, 'Bigfoot');
1 row created.
SQL> select * from foo;
ID NAME
---------- --------------------
2 Bigfoot
SQL> select * from remote_foo;
ID NAME
---------- --------------------
2 Bigfoot
SQL>
So, what to do? Switch to a statement-level trigger (instead of a row-level): it doesn't have to be autonomous, but has to have something that will prevent duplicates to be inserted - for example, a NOT EXISTS clause:
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 begin
4 insert into remote_foo
5 select * from foo a
6 where not exists (select null from remote_foo b
7 where b.id = a.id);
8 end;
9 /
Trigger created.
SQL> insert into foo (id, name) values (1, 'Littlefoot');
1 row created.
SQL> insert into foo (id, name)
2 select 2, 'Bigfoot' from dual union all
3 select 3, 'anat0lius' from dual;
2 rows created.
Result:
SQL> select * from foo;
ID NAME
---------- --------------------
2 Bigfoot
3 anat0lius
1 Littlefoot
SQL> select * from remote_foo;
ID NAME
---------- --------------------
1 Littlefoot
3 anat0lius
2 Bigfoot
SQL>
Is it possible to check the count of a table before any changes happen and the count after the insert and match them inside the same trigger?
for ex: old.count and new.count (before and after insert) ?
old.count and new.count (before and after insert)
Nothing stops you from using SELECT COUNT(*) in a before insert trigger. Of course, you won't do it in a after insert trigger, since a select count(*) on the same table on which an after trigger is defined would throw mutating table error. One way is autonomous transaction. But, in your case, it isn't that complex.
You could define a BEFORE INSERT TRIGGER and take the table count. The after insert count could be taken manually after the actual insert is done.
For example, I have a table t1 with one row. I have defined a before insert trigger on it, which would give me the table count before the insert happens.
SQL> DROP TABLE t1 PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t1 (A NUMBER);
Table created.
SQL>
SQL> INSERT INTO t1 VALUES (1);
1 row created.
SQL>
SQL> SELECT * FROM t1;
A
----------
1
SQL>
SQL> CREATE OR REPLACE TRIGGER trg
2 BEFORE INSERT
3 ON t1
4 FOR EACH ROW
5
6 DECLARE
7 val number;
8 BEGIN
9 SELECT COUNT(*)
10 INTO val
11 FROM t1;
12
13 DBMS_OUTPUT.PUT_LINE('TABLE COUNT BEFORE INSERT = '||val);
14
15 END;
16 /
Trigger created.
SQL>
SQL> set serveroutput on
SQL> INSERT INTO t1 VALUES (1);
TABLE COUNT BEFORE INSERT = 1
1 row created.
SQL>
SQL> SELECT COUNT(*) FROM t1;
COUNT(*)
----------
2
SQL>
So, you see TABLE COUNT BEFORE INSERT = 1 and then after insert the count is 2.
I'm trying to simulate ON UPDATE option in my foreign keys by making trigger that will update child records after update.
Trigger looks like this:
CREATE OR REPLACE TRIGGER SOMETABLE_AU_ID_TRG
AFTER UPDATE OF ID ON SOMETABLE
FOR EACH ROW
BEGIN
UPDATE SOMECHILDTABLE SET SOMETABLE_ID = :NEW.ID WHERE ID = :OLD.ID;
END;
And I'm making the child table constraint deferrable like this:
set constraint
SOMECHILDTABLE_FK_SOMETABLE deferred;
But it still gives me ORA-02292 (child record found) on commit. How can I avoid this?
Kudos to #BrianCamire for spotting the bug:
Let's start by creating your trigger...
SQL> CREATE OR REPLACE TRIGGER SOMETABLE_AU_ID_TRG
AFTER UPDATE OF ID ON SOMETABLE
FOR EACH ROW
BEGIN
UPDATE SOMECHILDTABLE SET SOMETABLE_ID = :NEW.ID WHERE ID = :OLD.ID;
END; 2 3 4 5 6
7 /
Trigger created.
SQL> alter table somechildtable add constraint some_fk foreign key (sometable_id)
2 references sometable (id) deferrable initially deferred;
Table altered.
SQL>
Here is the data ...
SQL> select * from somechildtable;
ID SOMETABLE_ID
---------- ------------
11 1
12 2
13 6
14 4
15 5
SQL>
So, let's mess with the parent data....
SQL> update sometable set id = 3 where id = 6
2 /
1 row updated.
SQL> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02292: integrity constraint (APC.SOME_FK) violated - child record found
SQL>
Aha! So, as Brian says we need to change the trigger code:
SQL> CREATE OR REPLACE TRIGGER SOMETABLE_AU_ID_TRG
AFTER UPDATE OF ID ON SOMETABLE
FOR EACH ROW
BEGIN
UPDATE SOMECHILDTABLE SET SOMETABLE_ID = :NEW.ID WHERE SOMETABLE_ID = :OLD.ID;
END;
/
2 3 4 5 6 7
Trigger created.
SQL> SQL> update sometable set id = 3 where id = 6;
1 row updated.
SQL> commit;
Commit complete.
SQL> select * from somechildtable
2 /
ID SOMETABLE_ID
---------- ------------
11 1
12 2
13 3
14 4
15 5
SQL>
I am using plsql in a oracle database 9i
I have a stored procedure,
With an in parameter of "table of number" that is called numbers.
I now want to select all rows from a table where a column named: ID is equal to a number inside "numbers"
Just like I can do select * from table name where Id in (!,!,!,...)
Thanks for the help.
Update :
Just to clear up,
I have a user defined type named numbers,
Number is defined: table of number.
So in the procedure decleration I have
"P_array in numbers"
I need to select * from a table where Id is found in p_array
like this?
SQL> create type numbers as table of number;
2 /
Type created.
SQL> create table foo (id number) ;
Table created.
SQL> insert into foo select rownum from dual connect by level <= 10;
10 rows created.
SQL> select * from foo;
ID
----------
1
2
3
4
5
6
7
8
9
10
10 rows selected.
SQL> create procedure testnum(p_num in numbers)
2 is
3 begin
4 for r_row in (select id
5 from foo f
6 where f.id in (select /*+ cardinality(t, 10) */ column_value
7 from table(p_num) t))
8 loop
9 dbms_output.put_line(r_row.id);
10 end loop;
11 end;
12 /
Procedure created.
SQL> set serverout on
SQL> exec testnum(numbers(2, 6, 9));
2
6
9
the cardinality hint is used to tell oracle roughly how many elements are in your table. without it, Oracle will assume ~8k rows which may be too high and cause unwanted full scans in the plans.
you can do a direct join too if you prefer.
for r_row in (select /*+ cardinality(t, 10) */ f.id
from foo f
inner join table(p_num) t
on t.column_value = f.id)
try the following :-
select * from tableName where id in (select c.column_value from table(cast(p_array as numbers)) c);
where numbers is table of number
SELECT * FROM A_TABLE WHERE ID IN (SELECT SAVED_NUMBERs FROM TABLE_NUMBER);
Do you mean this?