Can INSERT with ON CONFLICT in PostgreSQL be conditional in SQL - sql

I have the following SQL query:
WITH inserted_id AS (
INSERT INTO users (email,name,user_id,user_name,source)
VALUES (
'a',
'b',
'c',
'd',
'e')
ON CONFLICT (email) DO UPDATE SET
user_id='c',
user_name='d',
source='e'
RETURNING id AS users_id
)
INSERT INTO users_groups (users_id, groups_id)
SELECT users_id, 5 FROM inserted_id;
I would like the second INSERT to users_groups to be conditional on the first INSERT being done without any conflicts — that is, the ON CONFLICT part is not run. Can this be done with the query or what do I need to change?

You are doing something in the on conflict, so the row will be returned.
I think you can do:
INSERT INTO users_groups (users_id, groups_id)
SELECT users_id, 5
FROM inserted_id
WHERE users_id <> 'aa';
If you can, you can simply use on conflict do nothing. Then the id will not be returned.

Related

Update statement gives wrong result with subquery [duplicate]

This question already has answers here:
sql server 2008 management studio not checking the syntax of my query
(2 answers)
Closed 4 years ago.
I have the following query which gives no error when I used a non-existent column reference in the subquery. The column which I referred in the subquery is actually a column in the table being updated.
create table tbl1 (f1 bigint, f2 char(10), f3 integer);
insert into tbl1 values (1, 'aa', 0);
insert into tbl1 values (2, 'bb', 0);
insert into tbl1 values (3, 'cc', 0);
insert into tbl1 values (4, 'dd', 0);
create table temp_tbl (ref_num bigint);
insert into temp_tbl values (1);
insert into temp_tbl values (3);
update tbl1 set f2='ok' where f1 in (select f1 from temp_tbl);
-- 4 records updated
can anyone tell me why it is not giving any error? and records are updated irrespective of the condition.
I tried this in both Oracle and SQLserver. results are same
The sub-query's column reference goes to the outer table!
update tbl1 set f2='ok' where f1 in (select f1 from temp_tbl);
Is read as
update tbl1 set f2='ok' where f1 in (select tbl1.f1 from temp_tbl);
Qualify your columns:
update tbl1 set f2='ok' where f1 in (select temp_tbl.ref_num from temp_tbl);
This is happening because the values in a SELECT don't just have to be columns from the table you're selecting from, the sub-query is returning the value for f1 from the outer query, instead of a value from temp_tbl.
Consider if you re-wrote the UPDATE query to:
SELECT *
FROM tbl1
WHERE f1 IN (select f1 from temp_tbl);
The results returned would actually be:
When you're trying to reason about something like this (and as a generally good way of working to get queries right!), it's useful to write your UPDATE query in the form:
UPDATE T
SET F2 = 'ok'
FROM TBL1 T
WHERE T.f1 IN
(
SELECT F1
FROM temp_tbl
)
By writing it this way you can readily comment out the UPDATE and SET components of the query, replace them with a SELECT and see what the set of data the query will operate on is.

"ORA-00984: column not allowed here" when inserting with select statement

I would like to insert some data into a table. One field I would like to get from another table, so I'm using select statement inside. This is the code:
INSERT INTO t.table1 (
id,
id_t2,
date_of_change
)
VALUES (
t.table1_seq.nextval,
SELECT s.id_id_t2 from t.table2 s where s.something='something',
TO_DATE('02/05/2017 13:43:34','DD/MM/YYYY HH24:MI:SS')
)
Although select statement is always returning only 1 field (1 row), I presume this is why I'm getting the error.
How can I write INSERT statement with SELECT statement for just 1 field? Can it be done? If not, is there any other solution for this problem? Thank you.
You can translate your whole insert statement into the form of
insert into table1 (fields)
select fields from table2
This will allow you to specify in your select some values from the source table and some constant values. Your resulting query would be
INSERT INTO t.table1 (
id,
id_t2,
date_of_change
)
SELECT t.table1_seq.nextval,
s.id_id_t2,
TO_DATE('02/05/2017 13:43:34','DD/MM/YYYY HH24:MI:SS')
FROM t.table2 s
WHERE s.something='something'

Insert values in table only one column changes value

I've got a table with 2 columns,
GROUP PROJECTS
10001 1
10001 2
First column (GROUP) stays the same value 10001.
Second column (PROJECTS) changes values 3,5,9,100, etc. (I have 400 project ID's)
What would be to correct (loop?) statement to insert all 400 PROJECTS.
I used insert, values for smaller lists.
INSERT INTO table (GROUP_ID, PROJECTS) VALUES (10001, 1, 10001, 2, 10001, etc, 10001, etc);
I have the list in Excel (if needed I can create a Temp table with all 400 project ID's)
Thanks.
I typically write such inserts as:
INSERT INTO table(GROUP_ID, PROJECTS)
select 10001, 1 from dual union all
select 10001, 2 from dual union all
. . . ;
You should be able to generate the select statement pretty easily in Excel.
If the project IDs exist in their own table (or you can create one from your Excel data), then yu can get the list of values from there and cross-join those with all the group IDs:
insert into group_projects (group_id, project_id)
select g.group_id, p.project_id
from groups g
cross join projects p
where not exists (
select 1 from group_projects gp
where gp.group_id = g.group_id and gp.project_id = p.project_id
);
The where not exists() excludes all the existing pairs so you don't insert duplicates.
SQL Fiddle
If the groups don't have their own table then you can use the existing values from a subquery:
insert into group_projects (group_id, project_id)
select g.group_id, p.project_id
from (select distinct group_id from group_projects) g
cross join projects p
where not exists (
select 1 from group_projects gp
where gp.group_id = g.group_id and gp.project_id = p.project_id
);
You could use Gordon's approach to generate the project ID list as a subquery as well, if you didn't want to create a table for those.
I'd go with what I view as a simpler and much more readable solution... create the temp table with the data from Excel, then run this-
DECLARE
CURSOR c1
IS
SELECT project_id
FROM temp_table
WHERE project_id IS NOT NULL;
BEGIN
FOR rec in c1
LOOP
INSERT INTO table
VALUES (10001, rec.project_id);
COMMIT;
END LOOP;
END;
Seems cleaner than one giant insert statement or something complex with joins and sub-queries. If you wanted to make sure the value doesn't already exist in "table", add that criteria to the cursor select statement, or if you have constraints on the table add an exception handler in the loop.

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

MySQL INSERT with multiple nested SELECTs

Is a query like this possible? MySQL gives me an Syntax error. Multiple insert-values with nested selects...
INSERT INTO pv_indices_fields (index_id, veld_id)
VALUES
('1', SELECT id FROM pv_fields WHERE col1='76' AND col2='val1'),
('1', SELECT id FROM pv_fields WHERE col1='76' AND col2='val2')
I've just tested the following (which works):
insert into test (id1, id2) values (1, (select max(id) from test2)), (2, (select max(id) from test2));
I imagine the problem is that you haven't got ()s around your selects as this query would not work without it.
When you have a subquery like that, it has to return one column and one row only. If your subqueries do return one row only, then you need parenthesis around them, as #Thor84no noticed.
If they return (or could return) more than row, try this instead:
INSERT INTO pv_indices_fields (index_id, veld_id)
SELECT '1', id
FROM pv_fields
WHERE col1='76'
AND col2 IN ('val1', 'val2')
or if your conditions are very different:
INSERT INTO pv_indices_fields (index_id, veld_id)
( SELECT '1', id FROM pv_fields WHERE col1='76' AND col2='val1' )
UNION ALL
( SELECT '1', id FROM pv_fields WHERE col1='76' AND col2='val2' )