Return id from postgresql insertion - sql

In an external application I want to do the following:
Insert an entry to table A
Insert a bunch of entries to table B, with the id of my newly inserted item in A as a foreign key
So the tables would look like this:
A(_id, data)
B(_id, other_data)
_id --> A._id
Is this possible to accomplish in only postgresql? Or is it possible to return the id to my application after the item in table A was created, so my application can add the rest of the values?
I've looked at the following post, but it has an incorrect syntax and it causes an infinite loop resulting in a stack overflow (how ironic).

There are several ways to do it:
Assuming a._id is a serial column:
insert into a (data) values ('foo');
insert into b (_id, other_data) values (lastval(), 'foobar');
Edit (after discussion in comments):
Note that lastval() is concurrency safe (as all sequence related functions). Even if other sessions insert something into a between the two statements, lastval() would still return the value relevant for the current session (i.e. the one that was generated by the previous insert)
Other ways of doing that are described in detail in the question you already linked to: https://stackoverflow.com/a/6560530/330315
Or using a data modifying CTE:
with insert_a as (
insert into a (data) values ('foo')
returning _id
)
insert into b (_id, other_data)
values
((select _id from insert_a), 'one'),
((select _id from insert_a), 'two'),
((select _id from insert_a), 'three');

Related

INSERT OR REPLACE multiple rows, but there is no unique or primary keys

Hi I'm running into the following problem on SQlite3
I have a simple table
CREATE TABLE TestTable (id INT, cnt INT);
There are some rows already in the table.
I have some data I want to be inserted into the table: {(id0, cnt0), (id1, cnt1)...}
I want to insert data into the table, on id conflict, update TestTable.cnt = TestTable.cnt + value.cnt
(values.cnt is cnt0, cnt1 ... basically my data to be inserted)
*** But the problem is, there is no primary or unique constraint on id, and I am not allowed to change it!
What I currently have :
In my program I loop through all the values
UPDATE TestTABLE SET count = count + value.cnt WHERE id = value.id;
if (sqlite3_changes() == 0)
INSERT INTO MyTable (id, cnt) values (value.id, value.cnt);
But the problem is, with a very large dataset, doing 2 queries for each data entry takes too long. I'm trying to bundle multiple entries together into one call.
Please let me know if you have questions about my description, thank you for helping!
If you are able to create temporary tables, then do the following. Although I don't show it here, I suggest wrapping all this in a transaction. This technique will likely increase efficiency even if you are also able to add a temporary unique index. (In that case you could use an UPSERT with source data in the temporary table.)
CREATE TEMP TABLE data(id INT, cnt INT);
Now insert the new data into the temporary table, whether by using the host-language data libraries or crafting an insert statement similar to
INSERT INTO data (id, cnt)
VALUES (1, 100),
(2, 200),
(5, 400),
(7, 500);
Now update all existing rows using the single UPDATE statement. SQLite does not have a convenient syntax for joining tables and/or providing a source query for an UPDATE statement. However, one can use nested statement to provide similar convenience:
UPDATE TestTable AS tt
SET cnt = cnt + ifnull((SELECT cnt FROM data WHERE data.id == tt.id), 0)
WHERE tt.id IN (SELECT id FROM data);
Note that the two nested queries are independent of each other. In fact, one could eliminate the WHERE clause altogether and get the same results for this simple case. The WHERE clause is simply to make it more efficient, only attempting to update matching id's. The other subquery in the SET clause also specifies a match on id, but alone it would still allow updates of rows that don't have a match, defaulting to a null value and being converted to 0 (by isnull() function) for a no-op. By the way, without the isnull() function, the sum would result in null and would overwrite non-null values.
Finally, insert only rows with non-existing id values:
INSERT INTO TestTable (id, cnt)
SELECT data.id, data.cnt
FROM data LEFT JOIN TestTable
ON data.id == TestTable.id
WHERE TestTable.id IS NULL;

Creating New GUID automatically while inserting a new row to an existing table Not Working

I have an existing table in MS SQL called myTab.
It has the following fields
empno(PK) nchar(10),
age int
Now, i want to add a myGUID column and fill it up with a GUID whenever i insert a new row as well as Updating existing rows.
So i added the statement
ALTER TABLE myTab ADD myGUID uniqueidentifier DEFAULT NewId() NOT NULL;
Updating existing rows works correctly.
But, when i try to insert values,
INSERT INTO myTab VALUES ( 1000, 22 );
It fails, and gives the following message
**Column name or number of supplied values does not match table definition.**
When i do
insert into sourav_test2 values (20055711,23,NEWID());
The above statement works.
I want a GUID to be filled without changing the insert statement. Is it possible via a Trigger or a Function?
Always list the columns you are inserting!
INSERT INTO myTab (empno, age)
VALUES ('1000', 22);
Also use correct types for the values. Unmentioned columns will be assigned their default values, or NULL if there is no explicit default.
Your table has three columns, so if you leave out the column list, then the insert expects three values. You can still set a default, if you want by using the DEFAULT keyword in the VALUES clause:
INSERT INTO myTab (empno, age, myGUID)
VALUES ('1000', 22, DEFAULT);
Sourav's question about triggers got me thinking, so I tried a little test. Why?
Imagine a scenario where an application has already been written with thousands of INSERT statements that leave off the column list. In this case, if you could write an INSTEAD OF INSERT trigger that provides the column list, you could hopefully save yourself from correcting thousands of INSERT statements due to a newly added column.
Off the top of my head, I admittedly did not know if this could work.
So I wrote this little test:
CREATE TABLE tt (ColA varchar(1));
INSERT INTO tt VALUES ('a');
ALTER TABLE tt
ADD ColB uniqueidentifier DEFAULT NEWID();
GO
CREATE TRIGGER tr_tt
ON tt
INSTEAD OF INSERT
AS
INSERT INTO tt (ColA)
SELECT ColA FROM inserted;
GO
INSERT INTO tt VALUES ('a');
SELECT * FROM tt;
DROP TABLE tt;
I also tried a variation of the TRIGGER with the following INSERT just to be thorough:
INSERT INTO tt (ColA, ColB)
SELECT ColA, NEWID() FROM inserted;
The result was the same in both cases: The same error as reported in the question. So to answer the question:
Can't we use a trigger here which can do it?
The answer is NO. Even if you put an INSTEAD OF INSERT TRIGGER on the table, the parser will still not let you write an INSERT..VALUES() statement unless the number and order of VALUES exactly matches the definition of the table. A TRIGGER cannot be used to get around it.
Sooner or later, lazy coding exacts its price.

Postgres UPSERT reuse column values from INSERT on UPDATE

I have a basic upsert query that works ok:
insert into table (id, data) values (1, '<data_string>')
on conflict (id) do update set data='<data_string>';
The only problem is I need to fly lots of these queries over the network so I wonder if there is a way to cut the traffic in half by not having <data_string> listed twice in the query?
Yes, there is the special table EXCLUDED for the purpose:
INSERT INTO tbl (id, data)
VALUES (1, '<data_string>')
ON CONFLICT (id) DO UPDATE SET data = EXCLUDED.data;
Like the manual explains:
Note that the special excluded table is used to reference values originally proposed for insertion:
Works for multi-row INSERTs as well:
INSERT INTO tbl (id, data)
VALUES (1, '<data_string1>')
, (2, '<data_string2>')
, (3, '<data_string3>')
ON CONFLICT (id) DO UPDATE
SET data = EXCLUDED.data;

need to combine 3 sql-statement into 1

I have 3 SQL-Statements that I would like to combine into just one so I dont have to make multiple requests to my database from my programm (java).
My DB is PostgreSQL 9.4
First one creates a new user in umgmt_users
INSERT INTO umgmt_users ("user") VALUES ('test1')
Second one gets the id of that user (db is postgres and id data type is serial, so it get assigned automatically with me/the programm not knowing what id the user will get
SELECT umgmt_users.id
FROM umgmt_users
WHERE umgmt_users.user = 'test1'
Thrird is to add the just created user with his id (which I need the second statement for) and some other values into a different table
INSERT INTO
umgmt_user_oe_fac_role ("user_id", "oe_id", "fac_id", "role_id")
VALUES ('ID OF USER test1 created in first statement', '1', '2', '1');
Is there a way to get all three Statements into one?
create user
look up the ID he got assigned
insert his ID + other values into a different table
I'm not that good at SQL, I tried to put brackets around the select and put it into the insert & also looked at UNION and WITH but can not get it to work...
EDIT: Ended up using this solution from a_horse_with_no_name
with new_user as (
INSERT INTO umgmt_users ("user") VALUES ('test1')
returning id
)
INSERT INTO umgmt_user_oe_fac_role (user_id, oe_id, fac_id, role_id)
SELECT id, 1, 2, 1
FROM new_user;
All you need is two inserts:
INSERT INTO umgmt_users ("user") VALUES ('test1');
INSERT INTO umgmt_user_oe_fac_role (user_id, oe_id, fac_id, role_id)
VALUES (lastval(), 1, 2, 1);
In order for lastval() to work correctly there must be no other statement between the two inserts and the have to be run in a single transaction (so autocommit needs to be turned off)
Alternatively you can use a data modifying CTE which is then executed as a single statement:
with new_user as (
INSERT INTO umgmt_users ("user") VALUES ('test1')
returning id
)
INSERT INTO umgmt_user_oe_fac_role (user_id, oe_id, fac_id, role_id)
SELECT id, 1, 2, 1
FROM new_user;
Please don't put numbers in single quotes.
The answer to this is : It's impossible to combine these into a single plain vanilla ANSI SQL statement.
The first and third ones talk about two different tables altogether.
The second one is a Select Statement which is a different type of statement from the other two.

Store and reuse value returned by INSERT ... RETURNING

In PostgreSQL, it is possible to put RETURNING at the end of an INSERT statement to return, say, the row's primary key value when that value is automatically set by a SERIAL type.
Question:
How do I store this value in a variable that can be used to insert values into other tables?
Note that I want to insert the generated id into multiple tables. A WITH clause is, as far as I understand, only useful for a single insert. I take it that this will probably have to be done in PHP.
This is really the result of bad design; without a natural key, it is difficult to grab a unique row unless you have a handle on the primary key;
... that can be used to insert values into other tables?
You can even do that in a single SQL statement using a data-modifying CTE:
WITH ins1 AS (
INSERT INTO tbl1(txt)
VALUES ('foo')
RETURNING tbl1_id
)
INSERT INTO tbl2(tbl1_id)
SELECT * FROM ins1
Requires PostgreSQL 9.1 or later.
db<>fiddle here (Postgres 13)
Old sqlfiddle (Postgres 9.6)
Reply to question update
You can also insert into multiple tables in a single query:
WITH ins1 AS (
INSERT INTO tbl1(txt)
VALUES ('bar')
RETURNING tbl1_id
)
, ins2 AS (
INSERT INTO tbl2(tbl1_id)
SELECT tbl1_id FROM ins1
)
INSERT INTO tbl3(tbl1_id)
SELECT tbl1_id FROM ins1;