Postgresql: row number changes on update
the row number is changed
There is no such thing as a "row number" in a relational table.
I think when a row is updated, it should not be moved from its place
As a row doesn't have a "place", there is no such thing as "moving" it out of that either.
Rows in a relational table are not sorted.
Think of them like balls in a basket.
Without an ORDER BY
the DBMS is free to return the rows in any order it thinks is OK.
If you need to have the rows ordered in a certain way the only (really: the only) way to get that is to use an ORDER BY
statement.
SQL update records with ROW_NUMBER()
Let me assume that cards
has a primary key. Then you can use join
:
update cards c
set position = c2.seqnum
from (select c2.*, row_number() over () as seqnum
from cards c2
) c2
where c2.pkid = c.pkid;
I should note that the over ()
looks strange but Postgres does allow it. Normally an order by
clause would be included.
Updating a table with row number in postgresql
If you still want to update the table from some source result set, you can use update ... from ...
with join condition specified in where
clause:
create table t
as
select q, null::int as w
from unnest(array[1,2,3]) as q
with s as (
select row_number() over(order by q desc) as rn
, ctid as row_id
from t
)
update t
set w = rn
from s
where t.ctid = s.row_id
select *
from t
q | w
-: | -:
3 | 1
2 | 2
1 | 3
db<>fiddle here
How to Update in Postgresql without affecting the row number?
A database table is an unordered set of tuples (also known as a relation), so you cannot rely on the order of rows returned from a SELECT * FROM tablename
.
You need to enforce an ordering with an ORDER BY
clause if you need it.
The internal reason why the location of a row changes after an update is that PostgreSQL actually writes a new version of the row, which in this case is appended at the end. But you cannot rely on that either: if there is free space in the middle of the table, the new row version can be added there.
Using Row_Number() with Update Statement
You are trying to update a column (countrow
) that doesn't exist in your table so you will need to add this column to your original table and join to the subquery:
/* add the column */
ALTER TABLE usertable ADD countrow INT;
/* do the update */
UPDATE usertable
SET countrow = countrow_new
FROM (
SELECT userid,
createddate,
bookid,
pagenumber,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY userid, bookid ORDER BY bookid) = 1
THEN SUM(pagenumber) OVER (PARTITION BY userid, bookid)
END AS countrow_new
FROM usertable
) x
JOIN usertable u
ON u.bookid = x.bookid AND u.createddate = x.createddate AND u.userid = x.userid;
The above assumes {bookid,createddate,userid}
will always be unique
However, storing calculations in a table this way is very bad design and bad practice - you will have to keep the column up to date with a trigger on every insert / update / delete which will be terrible for performance.
A better approach would be to take your original query and create a view for it which can then be used as though it was a table:
create the view:
CREATE VIEW vwMyView
AS
SELECT userid,
bookid,
pagenumber,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY userid, bookid ORDER BY bookid) = 1
THEN SUM(pagenumber) OVER (PARTITION BY userid, bookid)
END AS countrow
FROM usertable;
Select from the view:
SELECT * FROM vwMyView;
How to update a column via Row_Number with a different value for each row?
You can update all the rows in one statement using a CTE as below.
;WITH T
AS (SELECT IdDatoLegal,
Row_number() OVER (ORDER BY IdCliente ) AS RN
FROM dbo.DatosLegales)
UPDATE T
SET IdDatoLegal = RN
How to update a column value with ROW_NUMBER() OVER value based on distinct values of a column
You can use dense_rank()
for this purpose:
select name, id, dense_rank() over (order by minid) as seqno
from (select t.*, min(id) over (partition by name) as minid
from table t
) t;
If you wanted to do this just with row_number()
:
select t.name, t.id, tt.seqnum
from table t join
(select t.name, row_number() over (order by min(id)) as seqno
from table t
group by t.name
) tt
on t.name = tt.name;
However, I don't know why you would want to do that.
Related Topics
Performance Tuning on Inner Join with Between Condition
Ora-00904 Invalid Identifier on Decode Alias
How to Fill Missing Dates and Values in Partitioned Data
How to Execute Dynamic SQL in Teradata
Dynamic Table Name in Select Statement
Select Unique Column Names from a List of Joined Tables
Sql: Finding the Closest Lat/Lon Record on Google Bigquery
Delete Records Which Are Considered Duplicates Based on Same Value on a Column and Keep the Newest
How to Import an Excel Spreadsheet into SQL Server 2008R2 Database
Rows to Columns in SQL Server 2000
How Best Execute Query in Background to Not Freeze Application (.Net)
Postgres Won't Accept Table Alias Before Column Name
Ef and Transactionscope for Both SQL Server and Oracle Without Escalating/Spanning to Dtc