I'm making a table which contains nested table:
create DOCUMENT as OBJECT (
DOC_ID NUMBER,
DESCRIPTION VARCHAR(1000));
create type documents_t is table of DOCUMENT;
create table projects (
ID NUMBER GENERATED ALWAYS AS IDENTITY ,
DOCUMENTS documents_t)
NESTED TABLE DOCUMENTS STORE AS documents_nested(
(PRIMARY KEY(nested_table_id, DOC_ID)) ORGANIZATION INDEX);
This works ok, but I can't seem to find how to make the nested table's primary key as identity column. any suggestions?
Thanks
Please find the code snippet,
CREATE SEQUENCE seq_documents
START WITH 1
INCREMENT BY 1
NOCACHE
NOCYCLE;
CREATE OR REPLACE TYPE documents_q AS OBJECT
(
doc_id NUMBER,
description VARCHAR2(1000),
CONSTRUCTOR FUNCTION documents_q(p_description VARCHAR2) RETURN SELF AS RESULT
);
CREATE OR REPLACE TYPE BODY documents_q AS
CONSTRUCTOR FUNCTION documents_q(p_description VARCHAR2) RETURN SELF AS RESULT IS
BEGIN
self.doc_id := seq_documents.nextval;
self.description := p_description;
RETURN;
END;
END;
/
CREATE TYPE documents_t AS TABLE OF documents_q;
create table projects (
id NUMBER GENERATED ALWAYS AS IDENTITY ,
documents documents_t)
NESTED TABLE documents STORE AS documents_nested(
(PRIMARY KEY(nested_table_id, doc_id)) ORGANIZATION INDEX);
INSERT INTO PROJECTS(documents) VALUES (documents_t(documents_q('Description One'),documents_q('Description Two')));
SELECT * FROM projects;
Please let me know if it gives you the solution. Thank you
If you want another solution without constructor (because using constructor adds more maintenance)
I took #Sujitmohanty30 idea and used the sequence in the insert statement. It makes code more easy to maintain, but force you to use sequence on insert and insert one item at each query (because "sequence.nextval" don't get incremented in same query):
CREATE SEQUENCE documents_seq NOCACHE;
/
CREATE TYPE document_type AS OBJECT (
doc_id NUMBER,
description VARCHAR2(1000)
);
/
CREATE TYPE documents AS TABLE OF document_type;
/
create table projects_docs (
id NUMBER GENERATED ALWAYS AS IDENTITY ,
docs documents)
NESTED TABLE docs STORE AS docs_nested(
(PRIMARY KEY(nested_table_id, doc_id)) ORGANIZATION INDEX);
/
INSERT INTO projects_docs (docs) VALUES(
documents(document_type(documents_seq.nextval, 'doc')));
/
select p.id, d.* from projects_docs p, table(p.docs) d;
Related
I would like to access the newly created records ID(PM) in the insert command.
I have a table like this:
CREATE TABLE item_list."groups" (
id serial4 NOT NULL,
"name" varchar NOT NULL,
hierarchical_id varchar NOT NULL,
parent_id int4 NOT NULL
)
When I insert to the table, a new value for the id is generated automatically, because of the serial type.
However, I would like to fill the hierarchical_id so its the concatenation of the groups parents hierarchical_id + the newly created groups id.
EG.:
The parents hierarchical id is: 0-12
I insert a new group, which gets 20 for it's id.
The new groups hierarchical_id should be 0-12-20.
Can I do this in the same query? If so, how should the insert command look like, to access the newly generated id?
The parents hieararchical_id is known, it doesnt need to be queried.
The best solution is probably a BEFORE INSERT trigger like this:
CREATE FUNCTION ident_trig() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
NEW.hierarchical_id := concat(NEW.hierarchical_id, '-', NEW.id);
RETURN NEW;
END;$$;
CREATE TRIGGER ident_trig BEFORE INSERT ON item_list."groups"
FOR EACH ROW EXECUTE FUNCTION ident_trig();
Then you insert the parent's hierarchical_id, and the trigger will modify it correctly.
I have the following code snippet :
CREATE TYPE ModeleRoue (nom VARCHAR2(10) );
CREATE TYPE ModeleMoteur (nom VARCHAR2(10) );
CREATE TYPE ModeleVoiture (nom VARCHAR2(50) );
CREATE TABLE EnsModeleVoiture OF ModeleVoiture;
CREATE TABLE EnsModeleMoteur OF ModeleMoteur;
CREATE TABLE EnsModeleRoue OF ModeleRoue;
CREATE TYPE ModeleRoue (nom VARCHAR2(10) );
CREATE TYPE ModeleMoteur (nom VARCHAR2(10) );
CREATE TYPE ModeleVoiture (nom VARCHAR2(50) );
CREATE TABLE EnsModeleVoiture OF ModeleVoiture;
CREATE TABLE EnsModeleMoteur OF ModeleMoteur;
CREATE TABLE EnsModeleRoue OF ModeleRoue;
I want to get the Voitures having number of Roue = 554 :
SELECT v.numero FROM ensvoiture v
WHERE EXISTS (SELECT * FROM v.roues r WHERE r.numero=554);
What is the difference of using the table in the EXISTS subquery directly in the WHERE clause?
SELECT v.numero FROM ensvoiture v
WHERE v.roues.numero =554;
The right way to write your first query would be:
SELECT v.numero FROM ensvoiture v
WHERE EXISTS (SELECT * FROM v.roues r WHERE r.numero=v.numero and r.numero=554);
This query, written this way, is doubtfully usefull, because it seems that you want to filter your ensvoiture table for those numbers equal to 554. Therefore, the exists would be unnecesary and you could use just your second query.
SELECT v.* FROM ensvoiture v
WHERE v.numero =554;
This doesn't mean that both queries will return the same data, because if there is no any r.numero=554, your first query will return nothing. The second one will keep returning data even whether the roues table doesn't have any numero=554.
Since Oracle 12c we can use IDENTITY fields.
Is there a way to retrieve the last inserted identity (i.e. select ##identity or select LAST_INSERTED_ID() and so on)?
Well. Oracle uses sequences and default values for IDENTITY functionality in 12c. Therefore you need to know about sequences for your question.
First create a test identity table.
CREATE TABLE IDENTITY_TEST_TABLE
(
ID NUMBER GENERATED ALWAYS AS IDENTITY
, NAME VARCHAR2(30 BYTE)
);
First, lets find your sequence name that is created with this identity column. This sequence name is a default value in your table.
Select TABLE_NAME, COLUMN_NAME, DATA_DEFAULT from USER_TAB_COLUMNS
where TABLE_NAME = 'IDENTITY_TEST_TABLE';
for me this value is "ISEQ$$_193606"
insert some values.
INSERT INTO IDENTITY_TEST_TABLE (name) VALUES ('atilla');
INSERT INTO IDENTITY_TEST_TABLE (name) VALUES ('aydın');
then insert value and find identity.
INSERT INTO IDENTITY_TEST_TABLE (name) VALUES ('atilla');
SELECT "ISEQ$$_193606".currval from dual;
you should see your identity value. If you want to do in one block use
declare
s2 number;
begin
INSERT INTO IDENTITY_TEST_TABLE (name) VALUES ('atilla') returning ID into s2;
dbms_output.put_line(s2);
end;
Last ID is my identity column name.
Please check
INSERT INTO yourtable (....)
VALUES (...)
RETURNING pk_id INTO yourtable;
It will help you to retrieve last inserted row
It seems that Oracle implemented IDENTITY just to say that they support identities. Everything is still implemented using SEQUENCES and sometimes you need to access the SEQUENCE to make some of the work (i.e. retrieve the latest inserted IDENTITY).
There is not a way to retrieve the IDENTITY similar to MySQL, SQL Server, DB2, and so on, you have to retrieve it using the SEQUENCE.
IDENTITY column uses a SEQUENCE “under the hood” - creating and dropping sequence automatically with the table it uses.
Also, you can specify start with and increment parameters using
start with 1000 and increment by 2. It's really very convenient to use IDENTITY when you don't want to operate it's values directly.
But if you need to somehow operate sequence directly you should use
another option available in Oracle 12c - column default values. Sutch default
values could be generated from sequence nextval or currval. To allow you to have a comprehensible sequence name and use it as "identity" without a trigger.
create table my_new_table
(id number default my_new_table_seq.nextval not null)
You will be always able to call: my_new_table_seq.currval.
It is possible to get ID generated from SEQUENCE on insert statement using RETURNING clause.
For example, create a temporary table:
create global temporary table local_identity_storage ("id" number) on commit delete rows
Make some insert saving this value in the temporary table:
CREATE TABLE identity_test_table (
id_ident NUMBER GENERATED BY DEFAULT AS IDENTITY,
same_value VARCHAR2(100)
);
declare
v_id number(10, 0);
begin
INSERT INTO identity_test_table
(same_value)
VALUES
('Test value')
RETURNING id_ident INTO v_id;
insert into local_identity_storage ("id") values (v_id);
commit;
end;
Now you have "local" inserted id.
select "id" from local_identity_storage
As I've written in this blog post, you could fetch all the current identity values of your schema with a single query:
with
function current_value(p_table_name varchar2) return number is
v_current number;
begin
for rec in (
select sequence_name
from user_tab_identity_cols
where table_name = p_table_name
)
loop
execute immediate 'select ' || rec.sequence_name || '.currval from dual'
into v_current;
return v_current;
end loop;
return null;
end;
select *
from (
select table_name, current_value(table_name) current_value
from user_tables
)
where current_value is not null
order by table_name;
/
What is your scope, global or last user inserted?
If global just use
SELECT mytable_seq.nextval MyTableID FROM DUAL
https://www.sitepoint.com/community/t/oracle-last-insert-id-question/1402
If specific encapsulate your inserts & query within a transaction.
the last insert will be the highest value of the column.
so I think that the easiest way to do it is with the max() method.
something like this
select max(id) from table_name
I have a problem with SQL Object (ORACLE). :(
I have an abstract type Forum, generalized in ForumCategory, and a type Category :
CREATE OR REPLACE TYPE Forum_t AS OBJECT
(
moderators Users_table_ref,
topics Topics_table,
MEMBER FUNCTION getName RETURN VARCHAR2
)
NOT FINAL NOT INSTANTIABLE;
/
CREATE TABLE Forum OF Forum_t
NESTED TABLE moderators STORE AS Liste_moderators,
NESTED TABLE topics STORE AS Liste_topics
(NESTED TABLE posts STORE AS Liste_posts);
CREATE OR REPLACE TYPE ForumCategory_t UNDER Forum_t
(
category REF Category_t,
OVERRIDING MEMBER FUNCTION getName RETURN VARCHAR2
);
/
CREATE OR REPLACE TYPE BODY ForumCategory_t AS
OVERRIDING MEMBER FUNCTION getName RETURN VARCHAR2 IS
c Category_t;
BEGIN
SELECT DEREF(SELF.category) INTO c FROM DUAL;
RETURN c.name;
END getName;
END;
/
CREATE OR REPLACE TYPE Category_t AS OBJECT
(
name VARCHAR2(60)
);
/
CREATE TABLE Category OF Category_t
(
CONSTRAINT PK_CAT_NAME PRIMARY KEY(name)
);
I want to make a trigger after insert on table Category which insert a ForumCategory_t on table Forum.
I have this code :
CREATE OR REPLACE TRIGGER Category_insert
AFTER INSERT ON Category
FOR EACH ROW
DECLARE
ref_cat REF Category_t;
BEGIN
SELECT REF(c) INTO ref_cat
FROM Category c
WHERE c.name = :NEW.name;
INSERT INTO Forum VALUES(ForumCategory_t(Users_table_ref(),Topics_table(),ref_cat));
END;
/
The beginning don't work, I cannot select REF of the inserted Category (it's work outside the trigger).
Please, I need help ^^
PS : Sorry for my english
Do the following scheme for my database:
create sequence data_sequence;
create table data_table
{
id integer primary key;
field varchar(100);
};
create view data_view as
select id, field from data_table;
create function data_insert(_new data_view) returns data_view as
$$declare
_id integer;
_result data_view%rowtype;
begin
_id := nextval('data_sequence');
insert into data_table(id, field) values(_id, _new.field);
select * into _result from data_view where id = _id;
return _result;
end;
$$
language plpgsql;
create rule insert as on insert to data_view do instead
select data_insert(new);
Then type in psql:
insert into data_view(field) values('abc');
Would like to see something like:
id | field
----+---------
1 | abc
Instead see:
data_insert
-------------
(1, "abc")
Is it possible to fix this somehow?
Thanks for any ideas.
Ultimate idea is to use this in other functions, so that I could obtain id of just inserted record without selecting for it from scratch. Something like:
insert into data_view(field) values('abc') returning id into my_variable
would be nice but doesn't work with error:
ERROR: cannot perform INSERT RETURNING on relation "data_view"
HINT: You need an unconditional ON INSERT DO INSTEAD rule with a RETURNING clause.
I don't really understand that HINT. I use PostgreSQL 8.4.
What you want to do is already built into postgres. It allows you to include a RETURNING clause on INSERT statements.
CREATE TABLE data_table (
id SERIAL,
field VARCHAR(100),
CONSTRAINT data_table_pkey PRIMARY KEY (id)
);
INSERT INTO data_table (field) VALUES ('testing') RETURNING id, field;
If you feel you must use a view, check this thread on the postgres mailing list before going any further.