Postgres UPSERT reuse column values from INSERT on UPDATE - sql

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;

Related

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.

SQL insert multi rows if do not exists else update a specific column with COALESCE

I have a table with two columns: id, age where id is the primary key
I know how to insert ONE new row for new primary key else update the age with the new value if the age value is not null using the following sql:
insert into obj (id, age)
values (2, 42)
on conflict (id) do
update set age = coalesce(42, obj.age)
but how do I do it with multiple rows? For example:
insert into obj (id, age)
values (2, 42), (3, 43), (5, 60)
on conflict (id) do
update set age = coalesce(???, obj.age)
the question is what should I put in the '???' in COALESCE?
I thought some one suggested using COALESCE(values(age), obj.age, I tried but it didn't work (syntax error).
insert into obj (id, age)
values (2, 42), (3, 43), (5, 60)
on conflict (id) do
update set age = coalesce(excluded.age, obj.age)
Note the use of the special record excluded. Per the documentation:
The SET and WHERE clauses in ON CONFLICT DO UPDATE have access to the existing row using the table's name (or an alias), and to rows proposed for insertion using the special excluded table.

Return id from postgresql insertion

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');

SQL Server Insert Example

I switch between Oracle and SQL Server occasionally, and often forget how to do some of the most trivial tasks in SQL Server. I want to manually insert a row of data into a SQL Server database table using SQL. What is the easiest way to do that?
For example, if I have a USERS table, with the columns of ID (number), FIRST_NAME, and LAST_NAME, what query do I use to insert a row into that table?
Also, what syntax do I use if I want to insert multiple rows at a time?
To insert a single row of data:
INSERT INTO USERS
VALUES (1, 'Mike', 'Jones');
To do an insert on specific columns (as opposed to all of them) you must specify the columns you want to update.
INSERT INTO USERS (FIRST_NAME, LAST_NAME)
VALUES ('Stephen', 'Jiang');
To insert multiple rows of data in SQL Server 2008 or later:
INSERT INTO USERS VALUES
(2, 'Michael', 'Blythe'),
(3, 'Linda', 'Mitchell'),
(4, 'Jillian', 'Carson'),
(5, 'Garrett', 'Vargas');
To insert multiple rows of data in earlier versions of SQL Server, use "UNION ALL" like so:
INSERT INTO USERS (FIRST_NAME, LAST_NAME)
SELECT 'James', 'Bond' UNION ALL
SELECT 'Miss', 'Moneypenny' UNION ALL
SELECT 'Raoul', 'Silva'
Note, the "INTO" keyword is optional in INSERT queries. Source and more advanced querying can be found here.
Here are 4 ways to insert data into a table.
Simple insertion when the table column sequence is known.
INSERT INTO Table1 VALUES (1,2,...)
Simple insertion into specified columns of the table.
INSERT INTO Table1(col2,col4) VALUES (1,2)
Bulk insertion when...
You wish to insert every column of Table2 into Table1
You know the column sequence of Table2
You are certain that the column sequence of Table2 won't change while this statement is being used (perhaps you the statement will only be used once).
INSERT INTO Table1 {Column sequence} SELECT * FROM Table2
Bulk insertion of selected data into specified columns of Table2.
.
INSERT INTO Table1 (Column1,Column2 ....)
SELECT Column1,Column2...
FROM Table2
I hope this will help you
Create table :
create table users (id int,first_name varchar(10),last_name varchar(10));
Insert values into the table :
insert into users (id,first_name,last_name) values(1,'Abhishek','Anand');
For example, "person" table has "id" IDENTITY column as shown below:
CREATE TABLE person (
id INT IDENTITY, -- Here
name NVARCHAR(50),
age INT,
PRIMARY KEY(id)
)
Then, we don't need to manually put a value to "id" IDENTITY column when inserting a row:
INSERT INTO person VALUES ('John', 27) -- The value for "id" is not needed
And, we can also insert multiple rows without the values for "id" IDENTITY column:
INSERT INTO person VALUES ('John', 27), ('Tom', 18)

Error inserting new rows (foreign key issue) in SQL

I am trying to debug some classic ASP code and the app keeps breaking on an insert statement. The backend is in SQL
The statement looks something like this:
insert into tableX (id, fo, ao) values (12, '', 'ab')
in tableX both fo and ao are set to allow null values.
fo is a foreign key pointing to the foTable (look up table)
I guess it's not liking the single quotes? How else do I go about this in classic asp?
The error I'm getting is:
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_tableX_FO". The conflict occurred in database "tableX", table
"dbo.FIELD_OFFICE", column 'FO'.
I also want to add that this is a process of migrating from Oracle. Does anyone know if Oracle treats '' as nulls, which is why I'm now getting an error in SQL?
An empty string is not a null value. You need to call it like this
insert into tableX (id, fo, ao) values (12, null, 'ab')
Alternatively
insert into tableX (id, ao) values (12, 'ab')
Either of these statements will work.
insert into tableX (id, ao) values (12, 'ab')
or
insert into tableX (id, fo, ao) values (12, NULL, 'ab')
If fo is set to allow nulls and you don't want to violate the FK constraint, then you should provide NULL instead of an empty string for the fo column.