Can't update only 1 row. How to change 1 row? - sql

I found out that Oracle does not support LIMIT clause unlike to MySQL and to update only 1 row in table i've tried this:
UPDATE "Schedule"
SET "Position" = 'Manager'
WHERE "Position" IN
(SELECT "Position"
FROM "Schedule"
WHERE "Position"='Tester'
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY);
And 3 rows are updated.
When i run this:
SELECT "Position"
FROM "Schedule"
WHERE "Position"='Tester'
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY;
i get only one row (as it should be).
But updating 1 row doesn't work as i mentioned before. Are there any other methods to update only 1 specific row?

You need just a slight, provided that your DB's version is at least 12c, change by replacing the part WHERE Position IN (SELECT Position with WHERE ID IN (SELECT ID as having a primary column. The current case has no sense, since all testers will be converted to managers even replacing the IN <subquery> with ='Tester'. So use the following update statement by removing double quotes
UPDATE Schedule
SET Position = 'Manager'
WHERE ID IN
(SELECT ID
FROM Schedule
WHERE Position = 'Tester'
FETCH NEXT 1 ROW ONLY);
Demo

Ideally your "Schedule" table would have a primary key and you'd use that to perform your update. Assuming that "Schedule" doesn't have a primary key you can use ROWID:
UPDATE "Schedule"
SET "Position" = 'Manager'
WHERE ROWID IN (SELECT ROWID
FROM "Schedule"
WHERE "Position" = 'Tester'
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY);
Note that using ROWID like this is very poor practice, and all tables should have a primary key specified when they're created so that individual rows can be found reliably. For a column or set of columns to be a valid primary key they must be A) non-NULL, B) unique, and C) unchanging.
Also - I strongly suggest that you get in the habit of using names for tables and columns which do not require that they be quoted. Quoted identifiers are an opportunity for syntax errors that do nothing to make your life better. If you create a table named SCHEDULE you can still refer to is as Schedule, ScHeDuLe, schedule, or whatever other combination of upper and lower case letters you care to use in your code.

Related

Informix select for update with order by

In ESQL/C I need to do a statement like this (I know that "order by" clause is not allowed in a "select ... for update")
SELECT FIRST 1 fd_foglio, fd_box_cod_soc, NVL(fd_id_subfoglio, 0)
FROM informix.foglioddt
WHERE
fd_box_data_all = TO_CHAR(CURRENT YEAR TO DAY, '%iy%m%d') AND
fd_box_cod_soc = '*'
ORDER BY fd_foglio DESC;
EXEC SQL
OPEN ifx_cursor_foglioddt_for_pending_crates;
EXEC SQL
FETCH ifx_cursor_foglioddt_for_pending_crates
INTO :fd_foglio, :fd_box_cod_soc, :local_fd_id_subfoglio;
in which I need an "order by" clause inside a "select for update" — I know that isn't allowed — in order to use a cursor successively to increment the field fd_id_subfogli (after I have used it in another part of the code) with a statement like:
UPDATE informix.foglioddt SET fd_id_subfoglio = :new_value WHERE
CURRENT OF ifx_cursor_foglioddt_for_pending_crates
How can I rewrite the previous code in order to select only the first ordered record and update a field in it?
The necessary information isn't in the question, but making a semi-educated guess, the foglioddt table has a UNIQUE constraint on columns fd_foglio and fd_id_subfoglio, but the latter can be NULL (in which case, Informix only allows one row in the table with a given fd_foglio value and a NULL fd_id_subfoglio).
Assuming that if there are any rows for a given fd_foglio value where the fd_id_subfoglio value is not null, then all the rows for that given fd_foglio value have a non-null value for the fd_id_subfoglio value, you could rephrase the UPDATE as:
UPDATE informix.foglioddt SET fd_id_subfoglio = :new_value
WHERE fd_foglio = :fd_foglio
AND (fd_id_subfoglio = :local_fd_id_subfoglio OR
fd_id_subfoglio IS NULL)
The first part of the OR condition deals with the normal case, where you've got both values that make up the primary key, and you simply change the identified row. The second part of the OR condition deals with the case where the only row in the table has fd_id_subfoglio set null.
Clearly, if my interpretation of the data structure is incorrect, then this won't work. The solution will then be to select the values from the primary key columns so that you can update using the primary key to identify the row to be updated — that's why primary keys are important! They're the way you can address individual rows by a set of values. Indexes on primary keys are also important to speed up such access (and to enforce uniqueness).

SQL/DB2 SQLSTATE=23505 error when executing an UPDATE statement

I am getting a SQLSTATE=23505 error when I execute the following DB2 statement:
update SEOURLKEYWORD
set URLKEYWORD = REPLACE(URLKEYWORD, '/', '-')
where STOREENT_ID = 10701
and URLKEYWORD like '%/%';
After a quick search, a SQL state 23505 error is defined as follows:
AN INSERTED OR UPDATED VALUE IS INVALID BECAUSE THE INDEX IN INDEX SPACE CONSTRAINS COLUMNS OF THE TABLE SO NO TWO ROWS CAN CONTAIN DUPLICATE VALUES IN THOSE COLUMNS RID OF EXISTING ROW IS X
The full error I am seeing is:
The full error I am seeing is:
DB2 Database Error: ERROR [23505] [IBM][DB2/LINUXX8664] SQL0803N One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index identified by "2" constrains table "WSCOMUSR.SEOURLKEYWORD" from having duplicate values for the index key. SQLSTATE=23505
1 0
I'm not sure what the "index identified by '2'" means, but it could be significant.
The properties of the columns for the SEOURLKEYWORD table are as follows:
Based on my understanding of this information, the only column that is forced to be unique is SEOURLKEYWORD_ID, the primary key column. This makes it sound like the update statement I'm trying to run is attempting to insert a row that has a SEOURLKEYWORD_ID that already exists in the table.
If I run a select * statement on the rows I'm trying to update, here's what I get:
select * from SEOURLKEYWORD
where storeent_id = 10701
and lower(URLKEYWORD) like '%/%';
I don't understand how executing the UPDATE statement is resulting in an error here. There are only 4 rows this statement should even be looking at, and I'm not manually updating the primary key at all. It kind of seems like it's reinserting a duplicate row with the updated column value before deleting the existing row.
Why am I seeing this error when I try to update the URLKEYWORD column of these four rows? How can I resolve this issue?
IMPORTANT: As I wrote this question, I have narrowed down the problem to the last of the four rows in the table above, SEOURLKEYWORD_ID = 3074457345616973668. I can update the other three rows just fine, but the 4th row is causing the error, I have no idea why. If I run a select * from SEOURLKEYWORD where SEOURLKEYWORD_ID = 3074457345616973668;, I see only the 1 row.
The error is pretty clear. You have a unique index/constraint in the table. Say you have two rows like this:
STOREENT_ID
URLKEYWORD
10701
A/B
10701
A-B
When the first version is replaced by 'A-B', the result would violate a unique constraint on (STOREENT_ID, URLKEYWORD) or (URLKEYWORD) (do note that other columns could possibly be included in the unique constraint/index as well).
You could avoid these situations by not updating them. I don't know what columns the unique constraint is on, but let's say only on URLKEYWORD. Then:
update SEOURLKEYWORD
set URLKEYWORD = REPLACE(URLKEYWORD, '/', '-')
where STOREENT_ID = 10701 and
URLKEYWORD like '%/%' and
not exists (select 1 from SEOURLKEYWORD s2 where replace(s2.urlkeyword, '/', '-') = REPLACE(SEOURLKEYWORD.URLKEYWORD, '/', '-')
);
Note the replace() is required for both columns because you might have:
A-B/C
A/B-C
These only conflict after the replacement in both values.
To complement the answer given by #GordonLinoff, here is a query that can be used to find a table's unique constraints, with their IDs, and the columns included in them:
SELECT c.tabschema, c.tabname, i.iid AS index_id, i.indname, ck.colname
FROM syscat.tabconst c
INNER JOIN syscat.indexes i
ON i.indname = c.constname -- unique index name matches constraint name
AND i.tabschema = c.tabschema AND i.tabname = c.tabname
INNER JOIN syscat.keycoluse ck
ON ck.constname = c.constname
AND ck.tabschema = c.tabschema c.tabname = ck.tabname AND
WHERE c.type = 'U' -- constraint type: unique
AND (c.tabschema, c.tabname) = ('YOURSCHEMA', 'YOURTABLE') -- replace schema/table
ORDER BY i.iid, ck.colseq

How to generate auto generated sequence no in sql

I have one requirement. I already have a table named WorkOrder. In this table there is a column Named WorkorderId set as primary key and identity. The next one is voucherNumber. Now I want to generate voucherNumber automatically. The condition is voucher number will not repeat. E.g., first I insert 2 rows into the table and after that I delete the 2nd entry. The next time my voucher number should be 3. Again i insert 3 more entries then after that my voucher no should be 6. Then i delete one row from this table after that my voucher number should be 7. If i delete the last row (I mean 7) then next time the voucher number should the same.
Use IDENTITY(...) when creating the column. This will make a field auto-increment its value.
You'll have to drop the column first in case that it already exists. There is no (clean) way to make this happen on already existing columns.
For further information and examples you can check out http://www.w3schools.com/sql/sql_autoincrement.asp
Edit: Sorry, I have overlooked the info that you are already using IDENTITY(...) on the PK column. Unfortunately SQL-Server can only have a single column with the IDENTITY property per table... So in this case you'll have to make use of a trigger.
This is an example:
CREATE TRIGGER CountRows
ON TestCount
AFTER UPDATE
AS
UPDATE TestCount SET Cnt = Cnt +1 WHERE ID IN (SELECT ID from inserted)
GO
In case you want to enter an IDENTIFIER to the record, it is best to use uniqueIdentifier type column. It is a string constant in the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, in which each x is a hexadecimal digit in the range 0-9 or a-f. For example, 6F9619FF-8B86-D011-B42D-00C04FC964FF is a valid uniqueidentifier value.
On insertion, you can simply proceed as follows;
Insert into MyTable
(WorkorderId, WorkName) values (NewId(), 'Test')
Using this, you can be sure the Id is globally unique.

Query for first occurence of null value in SQLite

I have a table which I dynamically fill with some data I want to create some statistics for. I have one value which has some values following a certain pattern, so I created an additional column where I map the values to other values so I can group them.
Now before I run my statistics, I need to check if I have to remap these values which means that I have to check if there are null values in that column.
I can do a select like this:
select distinct 1
from my-table t
where t.status_rd is not null
;
The disadvantage is, that this returns exactly one row, but it has to perform a full select. Is there some way that I can stop the select for the first encounter? I'm not interested in the exact row, because when there is at least one row, I have to run an update on all of them anyway, but I would like to avoid running the update unnecessarily everytime.
In Oracle I would do it with rownum, but this doesn't exist in SQLite
select 1
from my-table t
where t.status_rd is not null
and rownum <= 1
;
Use LIMIT 1 to select the first row returned:
SELECT 1
FROM my_table t
WHERE t.status_rd IS NULL
LIMIT 1
Note: I changed the where clause from IS NOT NULL to IS NULL based on your problem description. This may or may not be correct.

MySQL -- mark all but 1 matching row

This is similar to this question, but it seems like some of the answers there aren't quite compatible with MySQL (or I'm not doing it right), and I'm having a heck of a time figuring out the changes I need. Apparently my SQL is rustier than I thought it was. I'm also looking to change a column value rather than delete, but I think at least that part is simple...
I have a table like:
rowid SERIAL
fingerprint TEXT
duplicate BOOLEAN
contents TEXT
created_date DATETIME
I want to set duplicate=true for all but the first (by created_date) of each group by fingerprint. It's easy to mark all of the rows with duplicate fingerprints as dupes. The part I'm getting stuck on is keeping the first.
One of the apps that populates the table does bulk loads of data, with multiple workers loading data from different sources, and the workers' data isn't necessarily partitioned by date, so it's a pain to try to mark these all as they come in (the first one inserted isn't necessarily the first one by date). Also, I already have a bunch of data in there I'll need to clean up either way. So I'd rather just have a relatively efficient query I can run after a bulk load to clean up than try to build it into that app.
Thanks!
MySQL needs to be explicitly told if the data you are grouping by is larger than 1024 bytes (see this link for details). So if your data in the fingerprint column is larger than 1024 bytes you should use set the max_sort_length variable (see this link for details about values allowed, and this link about how to set it) to a larger number so that the group by wont silently use only part of your data for grouping.
Once you're certain that MySQL will group your data properly, the following query will set the duplicate flag so that the first fingerprint record has duplicate set to FALSE/0 and any subsequent fingerprint records have duplicate set to TRUE/1:
UPDATE mytable m1
INNER JOIN (SELECT fingerprint
, MIN(rowid) AS minrow
FROM mytable m2
GROUP BY fingerprint) m3
ON m1.fingerprint = m3.fingerprint
SET m1.duplicate = m3.minrow != m1.rowid;
Please keep in mind that this solution does not take NULLs into account and if it is possible for the fingerprint field to be NULL then you would need additional logic to handle that case.
How about a two-step approach, assuming you can go offline during a data load:
Mark every item as duplicate.
Select the earliest row from each group, and clear the duplicate flag.
Not elegant, but gets the job done.
Here's a funny way to do it:
SET #rowid := 0;
UPDATE mytable
SET duplicate = (rowid = #rowid),
rowid = (#rowid:=rowid)
ORDER BY rowid, created_date;
First set a user variable to zero, assuming this is less than any rowid in your table.
Then use the MySQL UPDATE...ORDER BY feature to ensure that the rows are updated in order by rowid, then by created_date.
For each row, if the current rowid is not equal to the user variable #rowid, set duplicate to 0 (false). This will be true only on the first row encountered with a given value for rowid.
Then add a dummy set of rowid to its own value, setting #rowid to that value as a side effect.
As you UPDATE the next row, if it's a duplicate of the previous row, rowid will be equal to the user variable #rowid, and therefore duplicate will be set to 1 (true).
Edit: Now I have tested this, and I corrected a mistake in the line that sets duplicate.
Here's another way to do it, using MySQL's multi-table UPDATE syntax:
UPDATE mytable m1
JOIN mytable m2 ON (m1.rowid = m2.rowid AND m1.created_date < m2.created_date)
SET m2.duplicate = 1;
I don't know the MySQL syntax, but in PLSQL you just do:
UPDATE t1
SET duplicate = 1
FROM MyTable t1
WHERE rowid != (
SELECT TOP 1 rowid FROM MyTable t2
WHERE t2.fingerprint = t1.fingerprint ORDER BY created_date DESC
)
That may have some syntax errors, as I'm just typing off the cuff/not able to test it, but that's the gist of it.
MySQL version (not tested):
UPDATE t1
SET duplicate = 1
FROM MyTable t1
WHERE rowid != (
SELECT rowid FROM MyTable t2
WHERE t2.fingerprint = t1.fingerprint
ORDER BY created_date DESC
LIMIT 1
)
Untested...
UPDATE TheAnonymousTable
SET duplicate = TRUE
WHERE rowid NOT IN
(SELECT rowid
FROM (SELECT MIN(created_date) AS created_date, fingerprint
FROM TheAnonymousTable
GROUP BY fingerprint
) AS M,
TheAnonymousTable AS T
WHERE M.created_date = T.created_date
AND M.fingerprint = T.fingerprint
);
The logic is that the innermost query returns the earliest created_date for each distinct fingerprint as table alias M. The middle query determines the rowid value for each of those rows; it is a nuisance to have to do this (but necessary), and the code assumes that you won't get two records for the same fingerprint and timestamp. This gives you the rowid for the earlist record for each separate fingerprint. Then the outer query (the UPDATE) sets the 'duplicate' flag on all those rows where the rowid is not one of the earliest rows.
Some DBMS may be unhappy about doing (nested) sub-queries on the table being updated.