Oracle SQL update every nth row - sql

To do some testing on a new table field, I'd like to fake-up some values on existing records in my test database. I want to assign a value to every 8th record in a table. I can easily select every 8th record using this syntax:
select *
from
(select rownum rn
, jeffs_field_to_update
from jeff)
where mod(rn, 8) = 0;
However, I'm fairly new to SQL, and I can't seem to be able to convert this to an update statement. I see a lot of answers here about selecting nth records, but I've already got that. Any assistance would be appreciated.

You need to join this to UPDATE statement on any key in the table. For example, if you have an unique id column, update statement will look like this:
update jeff
set jeffs_field_to_update = value
where id in
(select id
from
(select rownum rn
, jeff.id
from jeff)
where mod(rn, 8) = 0)

A shorter answer:
UPDATE jeff
SET jeffs_field_to_update = value
WHERE mod(DBMS_ROWID.ROWID_ROW_NUMBER(ROWID), 8)=0;

Assuming your table has a unique id to identify each row, you can do something like this:
update jeff
set . . .
where id in (select id
from (select rownum as rn, id
from jeff
)
where mod(rn, 8) = 0
)
You should also be able to do this with an inline updatable view (see here):
update (select jeffs_field_to_update
from (select rownum rn, jeffs_field_to_update
from jeff)
where mod(rn, 8) = 0
) toupdate
set . . .

even without the key, you can use rowid in oracle for this purpose.
update mytable
set mycol = new_value
where rowid in
(select rowid from
(select rownum rn, id from mytable)
where mod(rn, 8) = 0)

Related

Update with Where, Order by and Limit does not work

I am using SQLite 3.
When I input the following query
UPDATE MyTable Set Flag = 1 WHERE ID = 5 Order By OrderID DESC LIMIT 1;
I will always get an error:
near Order By, syntax error
I cannot figure out what is the problem with my query
To use LIMIT and ORDER BY in an UPDATE or DELETE statement, you have to do two things:
Build a custom version of the sqlite3.c amalgamation from source, configuring it with the --enable-update-limit option.
Compile that custom sqlite3.c into your project with SQLITE_ENABLE_UPDATE_DELETE_LIMIT defined to 1.
"Order By OrderID DESC LIMIT 1" is for selecting top one ordered result
so you should use it in select query.
you should do a subquery where you first get the id and then update it:
UPDATE MyTable
SET Flag = 1
WHERE (ID,OrderID) IN (SELECT ID,OrderID
FROM MyTable
WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
Demo
You could use ROWID:
UPDATE MyTable
SET Flag = 1
WHERE ROWID IN (SELECT ROWID FROM MyTable WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
db<>fiddle demo
or (ID,OrderID) tuple:
UPDATE MyTable
SET Flag = 1
WHERE (ID, ORDERID) IN (SELECT ID, ORDERID FROM MyTable WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
db<>fiddle demo2
And if you need to do it in bulk for every ID(SQLite 3.25.0):
WITH cte AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY OrderID DESC) AS rn FROM tab
)
UPDATE tab
SET Flag = 1
WHERE (ID, OrderID) IN (
SELECT ID, OrderID
FROM cte
WHERE rn = 1
);
Order By statement will not work in update query.
You have to use alternate way
UPDATE MyTable Set Flag = 1 WHERE ID = 5
and OrderId = (select max(OrderId) from MyTable where Id = 5);
If you have used the query like above it will work.
Introduction
Below I'm presenting two solutions to solve the issue and perform a proper UPDATE. At the end of each solution there's a live example on sample data included.
First does not require you to input any id and works for the entire table by picking the latest orderid for each id and changing it's flag to 1
Second requires you to input an id and works only for updating one id on the run
I'd personally go with first solution, but I am not sure about your requirement, thus posting two possibilities.
First solution - update entire table
Explanation here, for code scroll down.
For this we will use Row Value construction (id, orderid) just like for the second solution.
It will find the latest row based on orderid and update only that row for given (id, orderid) pair. More on that is included in Explanation of second solution.
We will also need to simulate row_number function to assign each row ranking numbers, to find out which row has the latest orderid for every id and mark then as 1 to be able to pull only those for update. This will allow us to update multiple rows for different ids in one statement. SQLite will have this functionality built in version 3.2.5 but for now, we will work with a subquery.
To generate row numbers we will use this:
select
*,
(select count(*) from mytable m1 where m1.id = m2.id and m1.orderid >= m2.orderid) as rn
from mytable m2
Then we just need to filter the output on rn = 1 and we have what we need.
That said, the whole UPDATE statement will look like:
Code
update mytable
set flag = 1
where (id,orderid) in (
select id, orderid
from (
select *, (select count(*) from mytable m1 where m1.id = m2.id and m1.orderid >= m2.orderid) as rn
from mytable m2
) m
where
m.rn = 1
and m.id = mytable.id
);
Live DEMO
Here's db fiddle to see this solution live on sample data.
Second solution - update only one ID
If you know your ID to be updated and want to run UPDATE statement for only one id, then this will work:
Code
update mytable
set flag = 1
where (id,orderid) in (
select id, orderid
from mytable
where id = 5
order by orderid desc
limit 1
);
Explanation
(id, orderid) is a construction called Row Value for which SQLite compares scalar values from left to right.
Example taken from documentation:
SELECT (1,2,3) = (1,2,3) -- outputs: 1
Live DEMO
Here's db fiddle to see this solution live on sample data.
SQL
UPDATE MyTable
SET Flag = 1
WHERE ID = 5
AND OrderID IN (SELECT OrderID
FROM MyTable
WHERE ID = 5
ORDER BY OrderID DESC
LIMIT 1);
Demo
SQLFiddle demo: http://sqlfiddle.com/#!5/6d596/2

If conditions in SQL

Clarification. I want to have a query that finds me the row with newest Timestamp, but if that row has column deleted set to 1 then I want to get back NULL. That is what I am trying to do.
I would like to make a query with sub query in WHERE clause something like:
SELECT *
FROM table
WHERE id = (SELECT id FROM table WHERE ts > 'x' ORDER BY ts LIMIT 1)
But the thing is there is a column called DELETED in table, and if DELETED = 1 I would like to get just this
SELECT *
FROM table
WHERE id = NULL
How could I do that?
I am working with MariaDB, but any SQL would help.
I understand that you want the "DELETED" result if there is any. Else you want the row with a ts greater than x. Assuming that "DELETED is 0 if not 1, you can use Gordons SQL like this:
SELECT t.*
FROM table t
WHERE (t.ts > 'x' or t.deleted = 1)
ORDER BY t.deleted desc, t.ts
LIMIT 1;
Check this one:
SELECT *
FROM table
WHERE id = (SELECT id FROM table WHERE ts > 'x' ORDER BY ts LIMIT 1)
AND deleted = 0;
I don't think this is related to your question, but why not just execute the query as:
SELECT t.*
FROM table t
WHERE t.ts > 'x'
ORDER BY t.ts
LIMIT 1;
It seems simpler than your version of the query -- assuming that id is unique in the table.

How to update a column of a table to a scaling value

I'm trying to update a column in my table to use the values 1 through (a max number decided by a count of records).
I don't know if I'm explaining this right, so I set up a SQLFiddle with the data I'm trying to update.
SQL FIDDLE
I want to set the Version column to 1 through (the max number).
Is there some way to rewrite this query to a scale the Version number?
As in, I want the first record to use 1, the second record to use 2, and so on...
UPDATE Documents
SET Version = 1
You can do it with a CTE and no joins:
with RankedDocument as
(
select *
, rn = row_number() over (order by ID)
from Documents
)
update RankedDocument
set Version = rn
SQL Fiddle with demo.
From what I can tell, you want every record from Documents to have a version number which is a number moving from 1 ..... N.
You could use a temporary table and ROW_NUMBER technique to get the incremental version and then UPDATE it back to your original table.
CREATE TABLE #Temp (ID int, Version int)
INSERT INTO #Temp (ID, Version)
SELECT ID, ROW_NUMBER() OVER (ORDER BY ID ASC)
FROM Documents
UPDATE Doc
SET Version = TT.Version
FROM Documents AS Doc INNER JOIN #Temp AS TT ON Doc.ID = TT.ID
DROP TABLE #Temp
If I understand you correctly..
Try this:
;WITH list AS (
SELECT
ID
, Version = ROW_NUMBER() OVER( ORDER BY VersionID ASC )
FROM Documents
)
UPDATE d SET
d.Version = x.Version
FROM Documents AS d
INNER JOIN list as x ON d.ID=x.ID
SELECT * FROM Documents
You can change the order ( ORDER BY VersionID ASC )
to the one you need.

Oracle SQL - update ids in oracle sql to be in sequential order

I have a table in Oracle SQL whose ids are in increasing, sequential order, but there are gaps in the ids due to editing, e.g. the ids are currently something like
22
23
24
32
33
44
...etc
I'd like to fix these gaps by just going through each row in the table and update them so there are no gaps. What's the best way to do this?
I think the following will work in Oracle:
update (select t.*, row_number() over (order by id) as newid) toupdate
set id = newid
The above answer was accepted a long time ago. It doesn't work. I think the answer should have code that does work, so:
merge into t dest using
(select t.*, row_number() over (order by id) as newid from t) src
on (dest.rowid = src.rowid)
when matched then update set id = newid;
You can do this with a single SQL statement as follows:
create table t as
select rownum * 2 id
from dual
connect by level <= 10;
update t set id = (
with tab as (
select id, rownum r
from (select id from t order by id)
)
select r from tab where t.id = tab.id
);
select * from t;
ID
----------
1
2
3
4
5
6
7
8
9
10
This will result in a full scan of t for every row in it, so will be very slow if t is "large". As the commenters have said, think very carefully before doing this; there are better ways to solve this "problem".
use this one:
update mytable set id = ROWNUM;
I think U Should Execute this code
DECLARE
CURSOR A
IS
SELECT ROWID DD
FROM YOUR_TABLE;
B NUMBER;
BEGIN
B := 1;
FOR I IN A
LOOP
UPDATE YOUR_TABLE
SET COLUMN_NAME = B
WHERE ROWID = I.DD;
B := B + 1;
END LOOP;
END;
Just Replace COLUMN_NAME with your actual column name having wrong ids and then execute this and see the column values which are sorted accuratley.Thanks

how to Update duplicate rows without primary key

i have a table with NO Primary Key.
name age sex
a 12 m
b 61 m
c 23 f
d 12 m
a 12 m
a 12 m
f 14 f
i have exactly 3 similar rows-row-1,row-5 and row-6.
i want to update row-5 without affecting row-1 and row-6.
Pls help me out, how to achieve this.
Your real problem is that you have no row numbers, and no way to distiguish identical rows. If your data is still in the order in which it was inserted you have simply been lucky so far. SQL Server gives no guarantees of row ordering and could randomize the order without notice. To preserve your ordering you can add an identity column to the table.
ALTER TABLE TableWithNoPrimaryKey
ADD RowNum int IDENTITY(1,1) PRIMARY KEY
Its not possible to use ROW_NUMBER function in SQL as this duplicates can be spread over thousands of record.
ROW_NUMBER is use to get the row number with OVER clause and as the storage of such data is non clustred its not possible to delete.
The only option is to add some identity or unique column to the table and then delete the special record and if you dont want table with the new index or new column you can delete that cloumn from the table.
There is a way to do what you wish. It is not recommended though.
;WITH cte AS
(
SELECT *, RowNum = ROW_NUMBER() OVER (ORDER BY GETDATE())
FROM [table]
)
UPDATE cte
SET age = age + 1
WHERE (RowNum = 5)
AND (name = 'a' AND age = 12 AND sex = 'm');
I have considered your table name as T1 and updated one of the rows. I don't want to order the result set but still want to generate the row numbers, so I used a dummy subquery - Select 0. Below query works in Oracle and IBM Netezza. You can try using rowid if it exists in SQL SERVER or any other equivalent to rowid should work.
UPDATE T1 SET name = a1, age = 21, sex = 'm'
FROM (SELECT name, age, sex, rowid,
row_number() over(partition by name, age, sex ORDER BY(SELECT 0)) as rn
FROM T1)A
WHERE T1.name = A.name
AND T1.age = A.age
AND T1.sex = A.sex
AND T1.rowid = A.rowid
AND A.rn = 1;