Dealing with multiple output results in UPSERT query [SQL] - sql

I'm trying to do an update query in which a single row the table is updated and, if nothing has matched and updated, a new row is inserted. In each case, I need the query to return the ID of the inserted row.
The issue I'm having is this query is returning 2 separate results when the insert case is reached, one for each output (the first empty, the second containing the ID). I'm running this query using SQL Alchemy on python and I'm only able to see the first result, which is empty.
UPDATE [Rights]
SET accessLevel = :access_level
OUTPUT inserted.rightsID
WHERE principal = :principal and [function] = :function
IF ##ROWCOUNT = 0
INSERT INTO Rights(principal, [function], accessLevel)
OUTPUT inserted.rightsID
VALUES(:principal, :function, :access_level)
And I'm calling it like so:
inserted_right_id = session.execute(sql_rights_update, right).fetchall()
Can anyone recommend a way of changing the query so that I can still use the UPSERT method, but only receive one of the IDs? I was considering storing the OUTPUT values into a table and returning that, or wrapping the whole thing in a select but hopefully there's something more elegant out there.
Thanks a million.

Feeling quite dumb. I simply added a
IF EXISTS(SELECT * FROM Rights WHERE principal = :principal and [function] = :function)
UPDATE ...
ELSE
INSERT ...

Related

DB2 Error Code -495 while we try deleting records counts more than 400k

We have following query to get the records deleted on fk_data_table, and been found we weren’t able to, as because of 400k records.
Delete FROM BOM_LINK WHERE TEST_OBJECT_OID IN (SELECT DISTINCT
TESTOBJECT_OID FROM TESTOBJECT WHERE TESTOBJECT.TESTOBJECTTYPE_OID =
3);
DB2 Error Code -495 https://www.sqlerror.de/db2_sql_error_-495_sqlstate_57051.html
I think what we can do is,
We can write a function or procedure
We can retrieve data by writing query by SELECT and CURSOR options, instead directly deleting
While iterating CURSOR we can try deleting it. (I am not sure that in this way we can delete the row, anyway lets find this out)
We can do periodical commit, instead of doing commit after extensive amount of deletion.
Could someone help us sorting out this issue which we are facing. Pointing to some SQL code snippets will help us a lot.
Unfortunately, Db2 for Z/OS doesn't allow to delete from a subselect.
I don't have Db2 for Z/OS at hand to check, but you may try the following:
CREATE VIEW BOM_LINK_V AS
SELECT *
FROM BOM_LINK B
WHERE EXISTS
(
SELECT 1
FROM TESTOBJECT T
WHERE T.TESTOBJECT_OID = B.TEST_OBJECT_OID
AND T.TESTOBJECTTYPE_OID = 3
)
FETCH FIRST 1000 ROWS ONLY;
Run DELETE FROM BOM_LINK_V until you get SQLSTATE = '02000' (no rows affected).
Update:
The DELETE statement since v12 supports the FETCH FIRST clause.
So, you may run the following without creating a view until you get SQLSTATE = '02000':
DELETE FROM BOM_LINK B
WHERE EXISTS
(
SELECT 1
FROM TESTOBJECT T
WHERE T.TESTOBJECT_OID = B.TEST_OBJECT_OID
AND T.TESTOBJECTTYPE_OID = 3
)
FETCH FIRST 1000 ROWS ONLY;

SQL update multiple rows with different values where they match a value from a list

So perhaps the title is a little confusing. If you can suggest better wording for that please let me know and i'll update.
Here's the issue. I've got a table with many thousands of rows and i need to update a few thousand of those many to store latest email data.
For example:
OldEmail#1.com => NewEmail#1.com
OldEmail#2.com => NewEmail#2.com
I've got a list of old emails ('OldEmail#1.com','OldEmail#2.com') and a list of the new ('NewEmail#1.com','NewEmail#2.com'). The HOPE was was to sort of do it simply with something like
UPDATE Table
SET Email = ('NewEmail#1.com','NewEmail#2.com')
WHERE Email = ('OldEmail#1.com','OldEmail#2.com')
I hope that makes sense. Any questions just ask. Thanks!
You could use a case expression:
update mytable
set email = case email
when 'OldEmail#1.com' then 'NewEmail#1.com'
when 'OldEmail#2.com' then 'NewEmail#2.com'
end
where email in ('OldEmail#1.com','OldEmail#2.com')
Or better yet, if you have a large list of values, you might create a table to store them (like myref(old_email, new_email)) and join it in your update query, like so:
update t
set t.email = r.new_email
from mytable t
inner join myref r on r.old_email = t.email
The actual syntax for update/join does vary accross databases - the above SQL Server syntax.
With accuracy to the syntax in particular DBMS:
WITH cte AS (SELECT 'NewEmail#1.com' newvalue, 'OldEmail#1.com' oldvalue
UNION ALL
SELECT 'NewEmail#2.com', 'OldEmail#2.com')
UPDATE table
SET table.email = cte.newvalue
FROM cte
WHERE table.email = cte.oldvalue
or, if CTE is not available,
UPDATE table
SET table.email = cte.newvalue
FROM (SELECT 'NewEmail#1.com' newvalue, 'OldEmail#1.com' oldvalue
UNION ALL
SELECT 'NewEmail#2.com', 'OldEmail#2.com') cte
WHERE table.email = cte.oldvalue
Consider prepared statement for rows update in large batches.
Basically it works as following :
database complies a query pattern you provide the first time, keep the compiled result for current connection (depends on implementation).
then you updates all the rows, by sending shortened label of the prepared function with different parameters in SQL syntax, instead of sending entire UPDATE statement several times for several updates
the database parse the shortened label of the prepared function , which is linked to the pre-compiled result, then perform the updates.
next time when you perform row updates, the database may still use the pre-compiled result and quickly complete the operations (so the first step above can be skipped).
Here is PostgreSQL example of prepare statement, many of SQL databases (e.g. MariaDB,MySQL, Oracle) also support it.

SQLite changes() counts non-changing UPDATEs

I have question regarding SQLite's changes() function, which, according to the documentation, "returns the number of database rows that were changed or inserted or deleted by the most recently completed INSERT, DELETE, or UPDATE statement" (also see the documentation of the underlying C/C++ function).
I was hoping to use this function to check whether the execution of an UPDATE statement pertaining to a single row has really caused that row to be changed or not.
By changed I do not just mean that the row matched the statement's WHERE clause. No, instead what I mean is that, for the row in question, the value of at least 1 column is actually different after the execution compared to before. If you ask me this is the only proper definition of a change in this context.
So I was hoping to detect such changes by checking whether changes() returns 1 (row changed) or 0 (row unchanged) when called right after the execution of the UPDATE statement.
But much to my despair this does not seem to work as expected.
Allow me to illustrate:
CREATE TABLE People (Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL);
INSERT INTO People (Name) VALUES ("Astrid");
SELECT changes();
Here changes() returns 1, as expected because we just INSERTed 1 row.
UPDATE People SET Name = "Emma" WHERE Id = 1;
SELECT changes();
Here changes() returns 1, as expected because 1 row was UPDATEd (i.e. actually changed: the Name of the Person with Id = 1 was "Astrid" but is now "Emma").
UPDATE People SET Name = "John" WHERE Id = 200;
SELECT changes();
Here changes() returns 0, as expected because there is no row with Id = 200.
So far so good. But now have a look at the following UPDATE statement, which does indeed match an existing row, but does not actually change it at all (Name remains set to "Emma")...
UPDATE People SET Name = "Emma" WHERE Id = 1;
SELECT changes();
Here changes() returns 1, while I was of course hoping for 0 :-(.
Perhaps this would have made sense if the function was called something like matched_rows() or affected_rows(). But for a function called changes(), and documented as it is, this behaviour strikes me as illogical, or confusing at best.
So anyway, can somebody explain why this happens, or, even better, suggest an alternative strategy to achieve my goal in a reliable (and efficient) way?
All I can think of is to actually do something like SELECT * FROM People WHERE Id = x, compare all returned column values with the values I'm about to set in the UPDATE statement and thereby decide whether I need to execute the UPDATE at all. But that can't be very efficient, right?
Of course in this toy example it might not matter much, but in my actual application I'm dealing with tables with many more columns, some of which are (potentially big) BLOBs.
The database does not compare old and new values; any UPDATEd row always counts as "changed" even if the values happen to be the same.
The documentation says that
the UPDATE affects … those rows for which the result of evaluating the WHERE clause expression as a boolean expression is true.
If you want to check the old value, you have to do it explicitly:
UPDATE People SET Name = 'Emma' WHERE Id = 1 AND Name IS NOT 'Emma';

How to write a update SQL using a select SQL

I want to write a update SQL statement, but one conidtion of this statement is the result from a select SQL statement, and I also want to return the result of the select SQL statement.
Like this: update ... set ... where id = (select id from ...)
I want to return the value of id back.
Does anybody know how should I do this?
Thanks in advance!
I don't believe that's possible in one statement. Update then query (select) the new value, or query the value first, and then submit an update.
Alternative would be a stored procedure on the database, which executes the multiple queries and returns the result for you.
This is not possible in all Java database frameworks that I know. Probably you need to separate your query and update in Java.
I don't see any problem in using a subselect in a WHERE clause of an update statement.
For the second request, getting back the value of id, I know this is possible in DB2, and maybe others implement that syntax too:
SELECT id FROM FINAL TABLE (
update ... set ... where id = (select id from ...)
)
This works also for INSERT and DELETE statements. (See the documentation.)
Update statements won't return the updated datasets. The select in that case would be a subselect that isn't directly accessible.
You'd thus have to use at least two queries:
select the ids you want
call the update query passing the previously selected ids as a parameter

Update A multi-valued field in Access

I have created a lookup table in Access to provide the possible values for a column. Now I need to update this column with the data it had before I converted the column. I am unable to figure out a SQL Query that will work. I keep getting the error "An UPDATE or DELETE query cannot contain a multi-valued field." My research has suggested that I just need to set the value of the column but this always updates 0 records:
UPDATE [table_name] SET [column_name].Value = 55 WHERE [table_name].ID = 16;
I know this query will work if I change it to update a text column, so it is definitely a problem with just this column.
If you're adding a value to your multi-valued field, use an append query.
INSERT INTO table_name( [column_name].Value )
VALUES (55)
WHERE ID = 16;
If you want to change one particular value which exists in your multi-valued field, use an UPDATE statement. For example, to change the 55 to 56 ...
UPDATE [table_name]
SET [column_name].Value = 56
WHERE [column_name].Value = 55 And ID = 16;
See Using multivalued fields in queries for more information.
I have figured this out! It certainly was counter-intuitive! You have to use an INSERT statement to do the update.
-- Update a record with a multi-valued field that has no value
INSERT INTO [table_name] ( [[column_name].[Value] )
VALUES(55)
WHERE [table_name].ID = 16;
This confused me because I was expecting an UPDATE statement. I think it actually inserts a record into a hidden table that is used to associate multiple values with this column.
I am working with Sharepoint, I created the tables as multi-value fields, ran into the error with my INSERT INTO statement, went back to Sharepoint to change to non-multi-value fields, but that didn't fix it.
Recreated the table without using multi-value fields, and the INSERT INTO worked just fine.
do not use the .value part
UPDATE [table_name] SET [column_name] = 55 WHERE [table_name].ID = 16;
INSERT INTO Quals (cTypes.[value])
SELECT Quals_ContractTypes.ContractType
FROM Quals_ContractTypes
WHERE (Quals.ID = Quals_ContractTypes.ID_Quals);
I gotta say I didn't understand very well your problem but I saw something strange in your query. Try this:
UPDATE [table_name] SET [column_name]= 55 WHERE [table_name].ID = 16;
UPDATE:
Look at this link: it has an example
UPDATE Issues
SET Issues.AssignedTo.Value = 10
WHERE (((Issues.AssignedTo.Value)=6)
AND ((Issues.ID)=8));
NOTES
You should always include a WHERE
clause that identifies only the
records that you want to update.
Otherwise, you will update records
that you did not intend to change. An
Update query that does not contain a
WHERE clause changes every row in the
table. You can specify one value to
change.
The Multi-Valued field refers to Access databases that have tables with columns, that allow you to select multiple values, like a Combo Checkbox list.
THOSE are the only Access types that SQL cannot work with. I've tested all Access lookup possibilities, including hard-coded values, and lookup tables. They work fine, but if you have a column that has the Allow Multiple select options, you're out of luck. Even using the INSERT INTO as mentioned below, will not work as you'll get a similar but different error, about INSERTing into multi-valued fields.
As mentioned it's best to avoid using such tables outside of Access, and refer to a table specifically for your external needs. Then write a macro/vba script to update the real tables with the data from the "auxiliary" table.