Oracle - insert select max where not exist - sql

I am trying to write something like below.
The objective is not to have more than one record with the same description. We have the unique constraint on the description column.
I must write this insert query that should work (without throwing errors) even if it gets executed more than once accidentally. Column id is the primary key of the table
insert into test (id, description)
select max(id)+1, 'test record' from test
where not exists ( select 1 from test where description = 'test record' );
if there is already a record in test table with description = 'test record', then the result of the following query has null for the id, and the insert fails with primary key violation
select max(id)+1, 'test record' from test
where not exists ( select 1 from test where description = 'test record' );
if i have to alternately write sql block with variable and begin/end to accomplish this, i am good to do that
however, any advice is appreciated

Nest the select statement inside another query, like this:
insert into test (id, description)
select t.id, t.description
from (
select max(id)+1 as id, 'test record' as description
from test
where not exists (select 1 from test where description = 'test record' )
) t
where t.id is not null
See the demo.

The use of an aggregate function without a group by clause forces the query to generate a record even when the where clause eliminates all rows
A quick workaround would be add a (dummy) group by clause:
insert into test (id, description)
select max(id)+1, 'test record' from test
where not exists ( select 1 from test where description = 'test record' )
group by 2;
Or alternatively, you can move the aggregate function to a subquery. I find that this solution makes the intent clearer:
insert into test (id, description)
select t.id, 'test record'
from (select max(id) + 1 id from test) t
where not exists ( select 1 from test where description = 'test record');
Demo on DB Fiddle

Related

how to use insert statement with where clause from diff table in oracle without using plsql

I want to insert like below
If
select *from class where student_name='A' ;
then
Insert into student (Studnetname varchar2,rollno integer) values('A',1);
without using PL/SQL
Wouldn't that be as simple as
insert into student (studentname, rollno)
select student_name, rollno
from class
where student_name = 'A'
and rollno = 1;
If such a student (whose values satisfy the WHERE clause) exists, a row will be inserted. Otherwise, nothing will happen.
I want direct insert statement without select
The closest you will get to meeting this requirement is something like this:
Insert into student (Studnetname, rollno)
select 'A',1
from dual
where exists (select null from class where student_name = 'A' )
;
This will insert one row into STUDENT if there is at least one record in CLASS matching the given criterion.

Return value cross join

I have two tables, one is a table #1 contains user information, email, password, etc..
the other table #2 contains item information
when I do a insert into table #2, and then use the returning statement, to gather what was inserted (returning auto values as well as other information), I also need to return information from table #1.
(excuse the syntax)
example:
insert into table #1(item,user) values('this item','the user')
returning *, select * from table 2 where table #1.user = table #2.user)
in other words, after the insert I need to return the values inserted, as well as the information about the user who inserted the data.
is this possible to do?
the only thing I came up with is using a whole bunch of subquery statements in the returning clause. there has to be a better way.
I suggest a data-modifying CTE (Postgres 9.1 or later):
WITH ins AS (
INSERT INTO tbl1(item, usr)
VALUES('this item', 'the user')
RETURNING usr
)
SELECT t2.*
FROM ins
JOIN tbl2 t2 USING (usr)
Working with the column name usr instead of user, which is a reserved word.
Use a subquery.
Simple demo: http://sqlfiddle.com/#!15/bcc0d/3
insert into table2( userid, some_column )
values( 2, 'some data' )
returning
userid,
some_column,
( SELECT username FROM table1
WHERE table1.userid = table2.userid
);

Insert values into table from the same table

Using SQL server (2012)
I have a table - TABLE_A with columns
(id, name, category, type, reference)
id - is a primary key, and is controlled by a separte table (table_ID) that holds the the primary next available id. Usually insertions are made from the application side (java) that takes care of updating this id to the next one after every insert. (through EJBs or manually, etc..)
However,
I would like to to write stored procedure (called from java application) that
- finds records in this table where (for example) reference = 'AAA' (passed as
parameter)
- Once multiple records found (all with same reference 'AAA', I want it to INSERT new
records with new ID's and reference = 'BBB', and other columns (name, category, type)
being same as in the found list.
I am thinking of a query similar to this
INSERT INTO table_A
(ID
,NAME
,CATEGORY
,TYPE,
,Reference)
VALUES
(
**//current_nextID,**
(select NAME
from TABLE_A
where REFENCE in (/*query returning value 'AAA' */),
(select CATEGORY
from TABLE_A
where REFENCE in (/*query returning value 'AAA' */),
(select TYPE
from TABLE_A
where REFENCE in (/*query returning value 'AAA' */),
'BBB - NEW REFERENCE VALUE BE USED'
)
Since, I don't know how many records I will be inserting , that is how many items in the result set of a criteria query
select /*field */
from TABLE_A
where REFENCE in (/*query returning value 'AAA' */),
I don't know how to come up with the value of ID, on every record. Can anyone suggest anything, please ?
It's not clear from your question how sequencing is handled but you can do something like this
CREATE PROCEDURE copybyref(#ref VARCHAR(32)) AS
BEGIN
-- BEGIN TRANSACTION
INSERT INTO tablea (id, name, category, type, reference)
SELECT value + rnum, name, category, type, 'BBB'
FROM
(
SELECT t.*, ROW_NUMBER() OVER (ORDER BY id) rnum
FROM tablea t
WHERE reference = 'AAA'
) a CROSS JOIN
(
SELECT value
FROM sequence
WHERE table_id = 'tablea'
) s
UPDATE sequence
SET value = value + ##ROWCOUNT + 1
WHERE table_id = 'tablea'
-- COMMIT TRANSACTION
END
Sample usage:
EXEC copybyref 'AAA';
Here is SQLFiddle demo

Update Null value

This question reflects my issue. How to do this in SQLite?
I've tried UPDATE with self-joins, isolating the self join in sub-query, triggers, and something similar to this. Here is an example:
UPDATE stage
SET title =
(
SELECT
prior.title
FROM
stage prior,
stage now
WHERE
prior.rownum+1 = now.rownum
)
WHERE
title is null
Every table in SQLite has got a pseudo-column called rowid (which can be accessible under several different names: rowid, oid, _rowid_, unless those names are assigned to other, real, columns). The rowid column is essentially a unique row identifier and you can use it as a sort criterion and/or in conditions.
The following query demonstrates how your problem can be solved with the help of rowid:
UPDATE stage
SET title = (
SELECT title
FROM stage AS prev
WHERE title IS NOT NULL AND prev.rowid < stage.rowid
ORDER BY prev.rowid DESC
LIMIT 1
)
WHERE title IS NULL
Here's a demo on SQL Fiddle.
You can read more about rowid in this SQLite manual.
I presented a solution for the problem you referred (I successfully tested on SQL2008, SQLite3 and Oracle11g). I copied that solution below:
CREATE TABLE test(mysequence INT, mynumber INT);
INSERT INTO test VALUES(1, 3);
INSERT INTO test VALUES(2, NULL);
INSERT INTO test VALUES(3, 5);
INSERT INTO test VALUES(4, NULL);
INSERT INTO test VALUES(5, NULL);
INSERT INTO test VALUES(6, 2);
SELECT t1.mysequence, t1.mynumber AS ORIGINAL
, (
SELECT t2.mynumber
FROM test t2
WHERE t2.mysequence = (
SELECT MAX(t3.mysequence)
FROM test t3
WHERE t3.mysequence <= t1.mysequence
AND mynumber IS NOT NULL
)
) AS CALCULATED
FROM test t1;
-- below here it was only tested in SQLite3, but I believe it should
-- work on other DBMS since it uses standard/non-proprietary SQL
UPDATE test
SET mynumber = (
SELECT t2.mynumber
FROM test t2
WHERE t2.mysequence = (
SELECT MAX(t3.mysequence)
FROM test t3
WHERE t3.mysequence <= test.mysequence
AND mynumber IS NOT NULL
)
);

Return id if a row exists, INSERT otherwise

I'm writing a function in node.js to query a PostgreSQL table.
If the row exists, I want to return the id column from the row.
If it doesn't exist, I want to insert it and return the id (insert into ... returning id).
I've been trying variations of case and if else statements and can't seem to get it to work.
A solution in a single SQL statement. Requires PostgreSQL 8.4 or later though.
Consider the following demo:
Test setup:
CREATE TEMP TABLE tbl (
id serial PRIMARY KEY
,txt text UNIQUE -- obviously there is unique column (or set of columns)
);
INSERT INTO tbl(txt) VALUES ('one'), ('two');
INSERT / SELECT command:
WITH v AS (SELECT 'three'::text AS txt)
,s AS (SELECT id FROM tbl JOIN v USING (txt))
,i AS (
INSERT INTO tbl (txt)
SELECT txt
FROM v
WHERE NOT EXISTS (SELECT * FROM s)
RETURNING id
)
SELECT id, 'i'::text AS src FROM i
UNION ALL
SELECT id, 's' FROM s;
The first CTE v is not strictly necessary, but achieves that you have to enter your values only once.
The second CTE s selects the id from tbl if the "row" exists.
The third CTE i inserts the "row" into tbl if (and only if) it does not exist, returning id.
The final SELECT returns the id. I added a column src indicating the "source" - whether the "row" pre-existed and id comes from a SELECT, or the "row" was new and so is the id.
This version should be as fast as possible as it does not need an additional SELECT from tbl and uses the CTEs instead.
To make this safe against possible race conditions in a multi-user environment:
Also for updated techniques using the new UPSERT in Postgres 9.5 or later:
Is SELECT or INSERT in a function prone to race conditions?
I would suggest doing the checking on the database side and just returning the id to nodejs.
Example:
CREATE OR REPLACE FUNCTION foo(p_param1 tableFoo.attr1%TYPE, p_param2 tableFoo.attr1%TYPE) RETURNS tableFoo.id%TYPE AS $$
DECLARE
v_id tableFoo.pk%TYPE;
BEGIN
SELECT id
INTO v_id
FROM tableFoo
WHERE attr1 = p_param1
AND attr2 = p_param2;
IF v_id IS NULL THEN
INSERT INTO tableFoo(id, attr1, attr2) VALUES (DEFAULT, p_param1, p_param2)
RETURNING id INTO v_id;
END IF;
RETURN v_id:
END;
$$ LANGUAGE plpgsql;
And than on the Node.js-side (i'm using node-postgres in this example):
var pg = require('pg');
pg.connect('someConnectionString', function(connErr, client){
//do some errorchecking here
client.query('SELECT id FROM foo($1, $2);', ['foo', 'bar'], function(queryErr, result){
//errorchecking
var id = result.rows[0].id;
};
});
Something like this, if you are on PostgreSQL 9.1
with test_insert as (
insert into foo (id, col1, col2)
select 42, 'Foo', 'Bar'
where not exists (select * from foo where id = 42)
returning foo.id, foo.col1, foo.col2
)
select id, col1, col2
from test_insert
union
select id, col1, col2
from foo
where id = 42;
It's a bit longish and you need to repeat the id to test for several times, but I can't think of a different solution that involves a single SQL statement.
If a row with id=42 exists, the writeable CTE will not insert anything and thus the existing row will be returned by the second union part.
When testing this I actually thought the new row would be returned twice (therefor a union not a union all) but it turns out that the result of the second select statement is actually evaluated before the whole statement is run and it does not see the newly inserted row. So in case a new row is inserted, it will be taken from the "returning" part.
create table t (
id serial primary key,
a integer
)
;
insert into t (a)
select 2
from (
select count(*) as s
from t
where a = 2
) s
where s.s = 0
;
select id
from t
where a = 2
;