Update Multiple Rows Using An Inequality - sql

I'm trying to update one column in a subset of a table but I can't figure out how to do it in a clean and efficient manner.
Consider the following:
// MyTable
id name flag
0 Steve 0
1 Bob 0
...
10500 Rick 0
I want to change flag to 1 but only for some of the cases. I tried to use
UPDATE MyTable
SET flag = 1
WHERE id <= 500
But obviously that does not work because the subquery returns more than one value. Technically, I could do it like this:
UPDATE MyTable SET flag = 1 WHERE id = 0
UPDATE MyTable SET flag = 1 WHERE id = 1
...
UPDATE MyTable SET flag = 1 WHERE id = 500
But who wants to do it like that? :) Is there a better way for me to format this query and only update those which match an inequality?
EDIT
To clarify exactly what's going on: when I say 'some of these cases' I only mean those which match the inequality, in this case id <= 500
When I run UPDATE MyTable SET flag = 1 WHERE id <= 500 I get the following error:
Subquery returned more than 1 value.
This is not permitted when the subquery follows =, !=, <, <= , >, >=
or when the subquery is used as an expression.

SInce your query does not have a subquery, I would suspect that you have a poorly wrtten trigger on the table that expects only one record at a time to be updated. This needs to be fixed as no trigger should ever be written on this assumption. Triggers in SQL Server need to perform only set-based operations as they work against the whole set not one row at a time.

This is one of the primary use cases for conditional queries (use of a where clause). You look for a way to uniquely identify the rows rows you wish to change and construct a query based on the identifying information. Your initial attempt applies this concept, however, your table as constructed is so general that composing an adequate query to select out only the relevant information is difficult if not impossible. Perhaps try changing the table to make the data more specific preferably by partitioning the data. For example is there some way of grouping the data so that some people can be identified as "students" or "professors" etc? What actually is flag supposed to indicate?

The query you have should work, if it is only that query, it should return the number of rows effected.
The long hand you wrote does exactly what your first query does. What environment are you running it in?

Related

TSQL BIT =0 and <>1

Are there any differences if I write
select * from table where bit_field = 0
or
select * from table where bit_field <> 1
?
UPDATE:
I've got a report that it is better to use "bit_field = 0" version because if you have some indexes on a field an use the second option there is some issue with "Seek Predicates"... It has two seek predicates one with < 1 and other with > 1... or something.
I don't have any example to show. :(
After the change from <> to =, there is a decrease of exclusive locks (X) and intent exclusive locks (IX)
Any thoughts on that?
There should be no differences between the two queries. If there are no NULL values present, then obviously any value which is zero is also the same thing as not being one. If there are NULLs present, it doesn't change anything, because a NULL record would not be returned from either query.
See the demo below.
Demo
There should be no difference, because a bit only takes on the values of 0 and 1.
Both will exclude NULL values.

Is there a way to include a query that is non updateable in an UPDATE query? [duplicate]

This question already has an answer here:
Access SQL Update One Table In Join Based on Value in Same Table
(1 answer)
Closed 10 years ago.
For the following query:
UPDATE tempSpring_ASN AS t
SET t.RECORD_TYPE = (
SELECT TOP 1 RECORD_TYPE
FROM (
SELECT "A" AS RECORD_TYPE
FROM TABLE5
UNION ALL
SELECT "B" AS RECORD_TYPE
FROM TABLE5
)
);
I'm getting, "Operation must use an updateable query." I don't understand. I'm not trying to update a union query. I'm just trying to update an otherwise updatable recordset with the output (single value) of a union query.
(The solution provided at Access SQL Update One Table In Join Based on Value in Same Table (which is also provided below) does not work for this situation, contrary to what is indicated on the top of this page.)
This question is a reference to a previous question, data and code examples posted here:
Access SQL Update One Table In Join Based on Value in Same Table
Hi AYS,
In Access, an Update query needs to be run on a table.
As a UNION query is a combination of multiple sets of records, the result set is no longer a table, and cannot be the object of an Update query as the records in the result set are no longer uniquely identified with any one particular table (even if they theoretically could be). Access is hard-coded to treat every UNION query as read-only, which makes sense when there are multiple underlying tables. There are a number of other conditions (such as a sub-query in the SELECT statement) that also trigger this condition.
Think if it this way: if you were not using TOP 1 and your UNION query returned multiple results, how would JET know which result to apply to the unique record in your table? As such, JET treats all such cases the same.
Unfortunately, this is the case even when all of the data is being derived from the same table. In this case, it is likely that the JET optimizer is simply not smart enough to realize that this is the case and re-phrase the query in a manner that does not use UNION.
In this case, you can still get what you want by re-stating your query in such a way that everything references your base table. For example, you can use the following as a SELECT query to get the PO_NUM value of the previous SHP_CUSTOM_5 record:
SELECT
t1.SHP_CUSTOM_5
, t1.PO_NUM
, t1.SHP_CUSTOM_5 -1 AS PREV_RECORD
, (SELECT
t2.PO_NUM
FROM
tempSpring_ASN As t2
WHERE
t2.SHP_CUSTOM_5 = (t1.SHP_CUSTOM_5 -1)
) AS PREV_PO
FROM
tempSpring_ASN AS t1
;
You can then phrase this as an Update query as follows in order to perform the "LIN" updates:
UPDATE
tempSpring_ASN AS t1
SET
t1.RECORD_TYPE = "LIN"
WHERE
t1.PO_NUM=
(
SELECT
t2.PO_NUM
FROM
tempSpring_ASN As t2
WHERE
t2.SHP_CUSTOM_5 = (t1.SHP_CUSTOM_5 -1)
)
;
This code was successful in the tests I ran with dummy data.
Regarding your "HDR" updates, your are really performing two separate updates.
1) If the PO_NUM matches the previous record's PO_NUM, set RECORD_TYPE to "LIN"
2) If it is the first record, set RECORD_TYPE to "HDR"
It is not clear to me why there would be a benefit to performing these actions within one query. I would recommend performing the HDR update using the "TOP 1" by SHP_CUSTOM_5 method you used in your original SELECT query example, as this will be a relatively simple UPDATE query. It is possible to use IIF() within an Update query, but I do not know what additional benefit you would gain from the additional time and complexity that would be required (it would most likely only be much less readable).
Best of luck!

SQL error ORA 01427

I am trying to update one of the columns in my table by collecting the values from another table in the data store using this query
UPDATE tablename PT
SET DID = (select distinct(did) from datastore.get_dept_alias
where upper(ltrim(rtrim(deptalias))) = upper(ltrim(rtrim(PT."Dept Descr")))
AND cid = PT.CID)
Note: Both the column names in the table are the same as entered
I get ORA 01427 error. Any idea about the issue?
I am trying to understand the other posts of this ORA error
As you can see here
SQL Error: ORA-01427: single-row subquery returns more than one row
This means that your sub-query
select distinct(did) from datastore.get_dept_alias
where upper(ltrim(rtrim(deptalias))) = upper(ltrim(rtrim(PT."Dept Descr")))
AND cid = PT.CID)
is returning more than one row.
So, are you sure that distinct (did) is unique? Looks like it's not. I don't recommend using where rownum = 1 because you don't know which one of the values will be used to update; unless you use ORDER BY.
Your getting this error because your select statement can return more than one result. You can not update a single cell with a query that can potentially return more than one result.
A common approach to avoid this with many SQL languages is to use a top 1 or something like that to assure the engine that you will only return one result. Note that you have to do this even if you know the query will only return one result. Just because YOU know it doesn't mean that the engine knows it. The engine also has to protect you from future possibilities not just things as they are right this moment.
Update:
I noticed you updated your question to Oracle. So in that case you could limit the subquery to a single result using the where rownum = 1 clause. As other answer pointed out you'd have to use further logic to ensure that top 1 coming back is the right one. If you don't know which one is the right one then solve that first.
The thought also occurs to me that you might be misunderstanding what DISTINCT does. This ensures that the return results are unique - but there could still be multiple unique results.

Using correlated subquery in SQL Server update statement gives unexpected result

I'm introducing a primary key column to a table that doesn't have one yet. After I have added a normal field Id (int) with a default value of 0 I tried using the following update statement to create unique values for each record:
update t1
set t1.id = (select count(*) from mytable t2 where t2.id <> t1.id)
from mytable t1
I would expect the subquery to be executed for each row because I'm referencing t1. Each time the subquery would be executed the count should be one less but it doesn't work.
The result is that Id is still 0 in every record. I have used this before on other DBMS with success. I'm using SQL Server 2008 here.
How do I generate unique values for each record and update the Id field?
Trying to explain why it doesn't work as you expect:
I would expect the subquery to be executed for each row because I'm referencing t1.
It is executed and it can affect all rows. But an UPDATE stetement is one statement and it is executed as one statement that affects a whole table (or a part of it if you have a WHERE clause).
Each time the subquery would be executed the count should be one less but it doesn't work.
You are expecting the UPDATE to be executed with one evaluation of the subquery per row. But it is one statement that is first evaluated - for all affected rows - and then the rows are changed (updated). (A DBMS may do it otherwise but the result should be nonetheless as if it was doing it this way).
The result is that Id is still 0 in every record.
That's the correct and expected behaviour of this statement when all rows have the same 0 value before execution. The COUNT(*) is 0.
I have used this before on other DBMS with success.
My "wild" guess is that you have used it in MySQL. (Correction/Update: my guess was wrong, this syntax for Update is not valid for MySQL, apparently the query was working "correctly" in Firebird). The UPDATE does not work in the standard way in that DBMS. It works - as you have learned - row by row, not with the full table.
I'm using SQL Server 2008 here.
This DBMS works correctly with UPDATE. You can write a different Update statement that would have the wanted results or, even better, use an autogenerated IDENTITY column, as others have advised.
The SQL is updating every row with the number of records where the ID doesn't equal 0. As all the rows ID equal 0 then there are no rows that are not equal to 0, and hence nothing gets updated.
Try looking at this answer here:
Adding an identity to an existing column

UPDATE Table SET Field

This is my Very first Post! Bear with me.
I have an Update Statement that I am trying to understand how SQL Server handles it.
UPDATE a
SET a.vField3 = b.vField3
FROM tableName a
INNER JOIN tableName b on a.vField1 = b.vField1
AND b.nField2 = a.nField2 – 1
This is my query in its simplest form.
vField1 is a Varchar
nField2 is an int (autonumber)
vField3 is a Varchar
I have left the WHERE clause out so understand there is logic that otherwise makes this a nessessity.
Say vField1 is a Customer Number and that Customer has 3 records
The value in nField2 is 1, 2, and 3 consecutively.
vField3 is a Status
When the Update comes to a.nField2 = 1 there is no a.nField2 -1 so it continues
When the Update comes to a.nField2 = 2, b.nField2 = 1
When the Update comes to a.nField2 = 3, b.nField2 = 2
So when the Update is on a.nField2 = 2, alias b reflects what is on the line prior (b.nField2 = 1)
And it SETs the Varchar Value of a.vField3 = b.vField3
When the Update is on a.nField2 = 3, alias b reflects what is on the line prior (b.nField2 = 2)
And it (should) SET the Varchar Value of a.vField3 = b.vField3
When the process is complete –the Second of three records looks as expected –hence the value in vField3 of the second record reflects the value in vField3 from the First record
However, vField3 of the Third record does not reflect the value in vField3 from the Second record.
I think this demonstrates that SQL Server may be producing a transaction of some sort and then an update.
Question: How can I get the DB to Update after each transaction so I can reference the values generated by each transaction?
Thank you.
davlyo
Firstly, and most importantly, you are mistaken in your misconception that the transaction will logically implement some sort of defined loop because your records are held 'consecutively'. In fact the order of your records in your database is undefined and it makes no sense to at all to think of your table storage in this ordered manner. In fact you should try to rid yourself of it completely or else it will lead you into all sorts of traps and bad habits. Try instead to logically think of statements executing as set (in the mathematical sense) operations, not cursor traverses.
It is certainly true that in most relational databases the order in which records will be retrieved by a SELECT without an ORDER BY clause is in insertion order, but this is an implementation issue and in fact should never be relied on in any logic (always use an ORDER BY clause to retrieve data if you care about the order). To emphasis, according to ANSI SQL the order of retrieval of records from a database is undefined without an ORDER BY clause - technically it wouldn't even have to be consistent on sequential execution of the same SELECT statement.
It follows therefore that in order for an UPDATE operation on a relational database to yield consistent results any query must operate as a single transaction. The transaction grabs a snapshot of the records it will update, updates them in a consistent, atomic, manner, then applies the results back to the data. There simply is no logical conception of the SQL looping over records or whatever.
The whole update query is one operation - and one transaction if that's the only thing in the transaction. So, the query does not see it's own results. The query operates without any implied order - almost as if it all happens at once.
Also bear in mind that this is a self join, so what was originally the second/third record will not be after the query is run. One record will be "lost" - the original third record, and the record at value 1 duplicated.
E.g. you start with, Customer, aField2, aField3
mdma 1 A
mdma 2 B
mdma 3 C
After running your update, the values will be
mdma 1 A
mdma 2 A
mdma 3 B
Is that what you are seeing/expecting?