How to insert two tables in a single query using oracle? - sql

Here I want to insert data in two tables,
1. COMPONENT_MASTER
2. COMPONENT_TO_ACTION
For this I wrote two queries to insert.But there is a problem with two queries.
Queries are:
INSERT INTO COMPONENT_MASTER(COMPONENT_ID,ROW_ID,COMPONENT_IDENTIFICATION,COMPONENT_NAME,COMPONENT_TYPE,COMPONENT_STATE,FECHA_DE_CREACION,REVISION)
SELECT MAX(COMPONENT_ID) + 1,?,?,?,?,?,CURRENT_TIMESTAMP,? FROM COMPONENT_MASTER
2nd
insert into COMPONENT_TO_ACTION (ORDER_NUMBER,ACTION_ID,COMPONENT_ID,FECHA_DE_CREACION,GRABADO_POR,STATUS,REVISION)
select max(ORDER_NUMBER) +1,?,?,current_timestamp,?,?,? from COMPONENT_TO_ACTION.
Both queries need to insert execute at a time. So both need to insert with same component_id.
Can I write a single Query to insert in two tables.

regarding to your question multi table insert syntax look like:
INSERT ALL
into COMPONENT_MASTER(COMPONENT_ID,ROW_ID,COMPONENT_IDENTIFICATION,COMPONENT_NAME,COMPONENT_TYPE,COMPONENT_STATE,FECHA_DE_CREACION,REVISION)
values (COMPONENT_ID,ROW_ID,COMPONENT_IDENTIFICATION,COMPONENT_NAME,COMPONENT_TYPE,COMPONENT_STATE,FECHA_DE_CREACION,REVISION)
into COMPONENT_TO_ACTION (ORDER_NUMBER,ACTION_ID,COMPONENT_ID,FECHA_DE_CREACION,GRABADO_POR,STATUS,REVISION)
values (ORDER_NUMBER,ACTION_ID,COMPONENT_ID,FECHA_DE_CREACION,GRABADO_POR,STATUS,REVISION)
SELECT (select MAX(COMPONENT_ID) + 1 from COMPONENT_MASTER) as COMPONENT_ID,
(select max(ORDER_NUMBER) + 1 from COMPONENT_TO_ACTION) as ORDER_NUMBER,
? as ACTION_ID,
? as GRABADO_POR,
? as STATUS
? as ROW_ID,
? as COMPONENT_IDENTIFICATION,
? as COMPONENT_NAME,
? as COMPONENT_TYPE,
? as COMPONENT_STATE,
CURRENT_TIMESTAMP as FECHA_DE_CREACION,
? as REVISION
FROM DUAL;
but as guys advise you not to use select max(...) + 1. Oracle has special object to create unique consequent identifiers is sequence. And you cant use sequence in subquery with multitable insert, but you may add it into values(...) clause and use it with nextval and curval. Tom Kyte said that you may rely on insert order . Please change query to use it and in result you should get something like that:
-- for example I create two sequence
CREATE SEQUENCE COMPONENT_MASTER_seq
START WITH 100000
INCREMENT BY 1
NOCACHE
NOCYCLE;
CREATE SEQUENCE COMPONENT_TO_ACTION_seq
START WITH 100000
INCREMENT BY 1
NOCACHE
NOCYCLE;
INSERT ALL
into COMPONENT_MASTER(COMPONENT_ID,ROW_ID,COMPONENT_IDENTIFICATION,COMPONENT_NAME,COMPONENT_TYPE,COMPONENT_STATE,FECHA_DE_CREACION,REVISION)
values (-- /*COMPONENT_ID*/
COMPONENT_MASTER_seq.nextval,ROW_ID,COMPONENT_IDENTIFICATION,COMPONENT_NAME,COMPONENT_TYPE,COMPONENT_STATE,FECHA_DE_CREACION,REVISION)
into COMPONENT_TO_ACTION (ORDER_NUMBER,ACTION_ID,COMPONENT_ID,FECHA_DE_CREACION,GRABADO_POR,STATUS,REVISION)
values (--/*ORDER_NUMBER*/
COMPONENT_TO_ACTION_seq.nextval ,ACTION_ID,
--/*COMPONENT_ID*/
COMPONENT_MASTER_seq.curval,FECHA_DE_CREACION,GRABADO_POR,STATUS,REVISION)
SELECT --COMPONENT_MASTER_seq.nextval as COMPONENT_ID, -- Oracle prohibit to use sequence with multi table insert
--COMPONENT_TO_ACTION_seq.nextval as ORDER_NUMBER, -- Oracle prohibit to use sequence with multi table insert
? as ACTION_ID,
? as GRABADO_POR,
? as STATUS
? as ROW_ID,
? as COMPONENT_IDENTIFICATION,
? as COMPONENT_NAME,
? as COMPONENT_TYPE,
? as COMPONENT_STATE,
CURRENT_TIMESTAMP as FECHA_DE_CREACION,
? as REVISION
FROM DUAL;

Related

Generate insert column based on select columns

I have a scenario, where 100's of select statements sql's are in one metadata table or some text file.
Need to insert all sql results into one specific table. (master table has col1, col2,col3 .... 200columns )
problem im facing(ORA-00947) is every select statement has different number of columns.
.. i need to generate INSERT PART.
CASE 1 : INSERT INTO (COL1,COL2,COL3) <<this select part comes from file/variable>>
CASE 2 : INSERT INTO (COL1) <<this select part comes from file/variable>>
CASE 3 : INSERT INTO (COL1) <<this select part comes from file/variable>>
have to figure out how many columns are in select part then generate INSERT part.
.
Thought of create as select but problem is some select statement has max(col) without alias so it will fail.
This is too long for a comment.
If you are storing SQL in a table, then you are constructing your query dynamically. So, update the table and list the columns that you want.
You could then construct the inserts as :
insert into master_table (<column list here>)
<select here>;
Both the select and column list would come from the table.
By far the easiest is to create a view for each SELECT statement. Then you can query the USER_TAB_COLUMNS view on the view name and get the column names.
Best regards,
Stew Ashton

Oracle SQL: How to generate per-order sequence numbers

I would like to generate unique sequence numbers for each order comment as they are entered.
For example, the segment:
INSERT INTO order_comments(order_number,
seq_num,
comment_text,
remark_timestamp)
VALUES (
'Smith',
(SELECT NVL (MAX (seq_num), 0) + 1
FROM order_comments
WHERE order_number='141592'),
'Shipped updated version 1.1’,
current_timestamp);
can be run multiple times, getting a new sequence number for that user each time.
The bad news is when this is written in Pro*C and we have multiple copies running simultaneously, we seem to get locks if we try to insert two comments for the same user at the same time. We could have an order_comment_sequence and just use nextval in the insert but that seems untidy since the “141592” comments will not have sequential numbers.
For the sake of the current discussion, we will say we have a few million orders, each of which will have five or ten comments. The order_number and seq_num are the primary key.
Is there something wrong with doing it this way? Is there an easier way of doing it?
You can use Oracle Sequence.
First create the sequence, like this:
CREATE SEQUENCE order_comments_seq
MINVALUE 1
MAXVALUE 999999999999999999999999999
START WITH 1
INCREMENT BY 1
CACHE 20;
For more info: http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_6015.htm#SQLRF01314
then you can use sequence.NEXTVAL like this:
INSERT INTO order_comments(order_number,
seq_num,
comment_text,
remark_timestamp)
VALUES ( 'Smith',
order_comments_seq.NEXTVAL,
'Shipped updated version 1.1’,
current_timestamp);
I don't think you can avoid the "untidy" approach if you want to avoid locks. You can generate a sequence number by ORDER_NUMBER when retrieving data from the database as follows:
SELECT order_number, ROW_NUMBER() OVER ( PARTITION BY order_number ORDER BY remark_timestamp ) AS seq_num
FROM order_comments
The "sequence" approach given by #benji is one approach. Another is to generate a random number using SYS_GUID():
TO_NUMBER( SYS_GUID(), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' )
You could even use this on the SEQ_NUM column as a default:
ALTER TABLE order_comments MODIFY seq_num DEFAULT TO_NUMBER( SYS_GUID(), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' );
Hope this helps.

SELECT * FROM NEW TABLE equivalent in Postgres

In DB2 I can do a command that looks like this to retrieve information from the inserted row:
SELECT *
FROM NEW TABLE (
INSERT INTO phone_book
VALUES ( 'Peter Doe','555-2323' )
) AS t
How do I do that in Postgres?
There are way to retrieve a sequence, but I need to retrieve arbitrary columns.
My desire to merge a select with the insert is for performance reasons. This way I only need to execute one statement to insert values and select values from the insert. The values that are inserted come from a subselect rather than a values clause. I only need to insert 1 row.
That sample code was lifted from Wikipedia Insert Article
A plain INSERT ... RETURNING ... does the job and delivers best performance.
A CTE is not necessary.
INSERT INTO phone_book (name, number)
VALUES ( 'Peter Doe','555-2323' )
RETURNING * -- or just phonebook_id, if that's all you need
Aside: In most cases it's advisable to add a target list.
The Wikipedia page you quoted already has the same advice:
Using an INSERT statement with RETURNING clause for PostgreSQL (since
8.2). The returned list is identical to the result of a SELECT.
PostgreSQL supports this kind of behavior through a returning clause in a common table expression. You generally shouldn't assume that something like this will improve performance simply because you're executing one statement instead of two. Use EXPLAIN to measure performance.
create table test (
test_id serial primary key,
col1 integer
);
with inserted_rows as (
insert into test (c1) values (3)
returning *
)
select * from inserted_rows;
test_id col1
--
1 3
Docs

Inserting a single sequence value on multiple rows

I'm trying to insert multiple records into a table, but using the same sequence value for every record.
This is similiar to: How can I insert multiple rows into oracle with a sequence value? however the answer given inserts multiple, different sequence numbers, and I want the same sequence number for multiple recs.
create table test1 (
col_a number,
col_b number
);
commit;
create sequence test_seq increment by 1 start with 2 minvalue 1 nocycle nocache noorder;
commit;
insert into test1 (col_a, col_b)
select a.object_id, test_seq.nextval from (
select object_id from all_objects where rownum < 5
) a;
commit;
The problem with the above is that it retrieves and inserts multiple (different) "test_seq.nextval" values, and I want the same value inserted for every row.
Is this even possible in straight sql without resorting to a trigger (or multiple sql statements)? One of the answers to the related question hinted it may not be, but it wasn't clear to me.
Thanks.
I'm using Oracle 11g if that helps.
use currval instead of nextval.
select test_seq.nextval from dual;
insert into test1 (col_a, col_b)
select a.object_id, test_seq.currval from (
select object_id from all_objects where rownum < 5
) a;
I know of no method to do that without two statements, the first to increment the sequence (and thus make it selectable through currval) and the second to use currval.

Does DB2 have an "insert or update" statement?

From my code (Java) I want to ensure that a row exists in the database (DB2) after my code is executed.
My code now does a select and if no result is returned it does an insert. I really don't like this code since it exposes me to concurrency issues when running in a multi-threaded environment.
What I would like to do is to put this logic in DB2 instead of in my Java code.
Does DB2 have an insert-or-update statement? Or anything like it that I can use?
For example:
insertupdate into mytable values ('myid')
Another way of doing it would probably be to always do the insert and catch "SQL-code -803 primary key already exists", but I would like to avoid that if possible.
Yes, DB2 has the MERGE statement, which will do an UPSERT (update or insert).
MERGE INTO target_table USING source_table ON match-condition
{WHEN [NOT] MATCHED
THEN [UPDATE SET ...|DELETE|INSERT VALUES ....|SIGNAL ...]}
[ELSE IGNORE]
See:
http://publib.boulder.ibm.com/infocenter/db2luw/v9/index.jsp?topic=/com.ibm.db2.udb.admin.doc/doc/r0010873.htm
https://www.ibm.com/support/knowledgecenter/en/SS6NHC/com.ibm.swg.im.dashdb.sql.ref.doc/doc/r0010873.html
https://www.ibm.com/developerworks/community/blogs/SQLTips4DB2LUW/entry/merge?lang=en
I found this thread because I really needed a one-liner for DB2 INSERT OR UPDATE.
The following syntax seems to work, without requiring a separate temp table.
It works by using VALUES() to create a table structure . The SELECT * seems surplus IMHO but without it I get syntax errors.
MERGE INTO mytable AS mt USING (
SELECT * FROM TABLE (
VALUES
(123, 'text')
)
) AS vt(id, val) ON (mt.id = vt.id)
WHEN MATCHED THEN
UPDATE SET val = vt.val
WHEN NOT MATCHED THEN
INSERT (id, val) VALUES (vt.id, vt.val)
;
if you have to insert more than one row, the VALUES part can be repeated without having to duplicate the rest.
VALUES
(123, 'text'),
(456, 'more')
The result is a single statement that can INSERT OR UPDATE one or many rows presumably as an atomic operation.
This response is to hopefully fully answer the query MrSimpleMind had in use-update-and-insert-in-same-query and to provide a working simple example of the DB2 MERGE statement with a scenario of inserting AND updating in one go (record with ID 2 is updated and record ID 3 inserted).
CREATE TABLE STAGE.TEST_TAB ( ID INTEGER, DATE DATE, STATUS VARCHAR(10) );
COMMIT;
INSERT INTO TEST_TAB VALUES (1, '2013-04-14', NULL), (2, '2013-04-15', NULL); COMMIT;
MERGE INTO TEST_TAB T USING (
SELECT
3 NEW_ID,
CURRENT_DATE NEW_DATE,
'NEW' NEW_STATUS
FROM
SYSIBM.DUAL
UNION ALL
SELECT
2 NEW_ID,
NULL NEW_DATE,
'OLD' NEW_STATUS
FROM
SYSIBM.DUAL
) AS S
ON
S.NEW_ID = T.ID
WHEN MATCHED THEN
UPDATE SET
(T.STATUS) = (S.NEW_STATUS)
WHEN NOT MATCHED THEN
INSERT
(T.ID, T.DATE, T.STATUS) VALUES (S.NEW_ID, S.NEW_DATE, S.NEW_STATUS);
COMMIT;
Another way is to execute this 2 queries. It's simpler than create a MERGE statement:
update TABLE_NAME set FIELD_NAME=xxxxx where MyID=XXX;
INSERT INTO TABLE_NAME (MyField1,MyField2) values (xxx,xxxxx)
WHERE NOT EXISTS(select 1 from TABLE_NAME where MyId=xxxx);
The first query just updateS the field you need, if the MyId exists.
The second insertS the row into db if MyId does not exist.
The result is that only one of the queries is executed in your db.
I started with hibernate project where hibernate allows you to saveOrUpdate().
I converted that project into JDBC project the problem was with save and update.
I wanted to save and update at the same time using JDBC.
So, I did some research and I came accross ON DUPLICATE KEY UPDATE :
String sql="Insert into tblstudent (firstName,lastName,gender) values (?,?,?)
ON DUPLICATE KEY UPDATE
firstName= VALUES(firstName),
lastName= VALUES(lastName),
gender= VALUES(gender)";
The issue with the above code was that it updated primary key twice which is true as
per mysql documentation:
The affected rows is just a return code. 1 row means you inserted, 2 means you updated, 0 means nothing happend.
I introduced id and increment it to 1. Now I was incrementing the value of id and not mysql.
String sql="Insert into tblstudent (id,firstName,lastName,gender) values (?,?,?)
ON DUPLICATE KEY UPDATE
id=id+1,
firstName= VALUES(firstName),
lastName= VALUES(lastName),
gender= VALUES(gender)";
The above code worked for me for both insert and update.
Hope it works for you as well.