Easiest way to update the ids of rows in sql server? - sql

the primary key ID values in this table are being used in our 2 systems that were recently merged, however there is a large number of items in one of the systems that are pointing to the wrong id values, i need to update the ID(PK) values so that the 6 million existing items will be pointing to the correct row.
id like to update the id columns to the following:
ID
1 to 5
2 to 6
3 to 7
4 to 1
5 to 2
6 to 3
7 to 4

Well, assuming it is not an IDENTITY column (in which case you'll need to set IDENTITY_INSERT to on) then the following should work (see SQLFiddle for example)
UPDATE MyTable
SET ID =
CASE WHEN ID >= 4 SET ID - 3
ELSE ID + 4
END

Use update query with a case statement
Update tableName set PkId = Case PkId
When 1 then 5
When 2 then 6
When 3 then 7
When 4 then 1
When 5 then 2
When 6 then 3
When 7 then 4 End
Where PkId In (1,2,3,4,5,6,7)
If the values in your answer aer just a small subset of the values that need to be change (Do all 6 million need to change?), then you need to Create a mapping table that has the old incorrect value and the new correct value, and use that (with a join) instead of the case statement.
Update t set PkId = map.NewPkId
From tablename t
Join mappingTable m
On m.oldPkId = t.PkId

Related

Assign explicit version to existing rows of the table

I have a table where records are inserted and updated. In case of updates, a new row is inserted into the table. In order to track updates for a given record, there's a column added to the table called root_record_id which holds the id of the very first record in the update chain.
For eg: Consider the record table schema as follows:
id
root_record_id
other columns
1
1
...
2
2
...
3
1
...
4
1
...
5
2
...
In this case, a record with id=1 was inserted, which was then updated to id=3 and then to id=4. Similarly the record with id=2 was inserted and then updated to id=5.
I want to add a version column to this table, where version is incremented on each update and starts with 0.
id
root_record_id
version
other columns
1
1
0
...
2
2
0
...
3
1
1
...
4
1
2
...
5
2
1
...
I tried writing queries using group by clause on root_record_id but failed to accomplish the task.
If you are looking for the general sequence on how to add the column and then pre-fill the values, then follow this fiddle: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=5a04b49fbda3883a9605f5482e252a1b
Add the version column allowing nulls:
ALTER TABLE Records ADD version int null;
Update the version according to your logic:
UPDATE Records
SET version = lkp.version
FROM Records r
INNER JOIN (
SELECT Id, COUNT(root_record_id) OVER (partition by root_record_id ORDER BY id ASC)-1 as version
FROM Records
) lkp ON r.Id = lkp.Id;
Alter the version column to NOT allow nulls
ALTER TABLE Records ALTER COLUMN version int not null;
Finally, ensure that you increment the version column during new row inserts.
DBFIDDLE
This query produces the version that you can use (in an update, or in a trigger):
SELECT
id,
root_record_id,
RANK() OVER (partition by root_record_id ORDER BY id ASC)-1 version
FROM table1
ORDER BY id;
output:
id
root_record_id
version
1
1
0
2
2
0
3
1
1
4
1
2
5
2
1

Updating uniqueidentifier column with same value for rows with matching column value

I need a little help. I have this (simplified) table:
ID
Title
Subtype
RelatedUniqueID
1
My Title 1
1
NULL
2
My Title 2
1
NULL
3
My Title 3
2
NULL
4
My Title 4
2
NULL
5
My Title 5
2
NULL
6
My Title 6
3
NULL
What I am trying to accomplish is generating the same uniqueidentifier for all rows having the same subtype.
So result would be this:
ID
Title
Subtype
RelatedUniqueID
1
My Title 1
1
439753d3-9103-4d0e-9dd0-569dc71fd6a3
2
My Title 2
1
439753d3-9103-4d0e-9dd0-569dc71fd6a3
3
My Title 3
2
d0f08203-1197-4cc7-91bb-c4ca34d7cb0a
4
My Title 4
2
d0f08203-1197-4cc7-91bb-c4ca34d7cb0a
5
My Title 5
2
d0f08203-1197-4cc7-91bb-c4ca34d7cb0a
6
My Title 6
3
055838c6-a814-4bd1-a859-63d4544bb449
Requirements
One query to update all rows at once
The actual table has many more rows with hundreds of subtypes, so manually building a query for each subtype is not an option
Using SQL Server 2017
Thanks for any assist.
Because newid() is applied per-row, you have to generate the values first, so this has to involve the use of a temporary or permanent table to store the correlated ID>Subtype value.
So first you need to generate the GUID values per Subtype :
with subtypes as (
select distinct subtype
from t
)
select Subtype, NewId() RelatedId into #Id
from subtypes
And then you can use an updatable CTE to apply these to your base table:
with r as (
select t.*, id.RelatedId
from #id id
join t on t.subtype=id.Subtype
)
update r
set relatedUniqueId=RelatedId
See example DB<>Fiddle
You can use an updatable CTE with a window function to get this data:
with r as (
select t.*,
RelatedId = first_value(newid()) over (partition by t.Subtype order by ID rows unbounded preceding)
from t
)
update r
set relatedUniqueId = RelatedId;
db<>fiddle
I warn though, that newid() is somewhat unpredictable in when it is calculated, so don't try messing about with a joined update (unless you pre-save the IDs like #Stu has done).
For example, see this fiddle, the IDs were calculated differently for every row.
I have found the single query solution.
Pre-requirement for this to work is that RelatedUniqueID must already contain random values. (e.g. set default field value to newid)
UPDATE TestTable SET ForeignUniqueID = TG.ForeignUniqueID FROM TestTable TG INNER JOIN TestTable ON TestTable.SubType = TG.SubType
Update
As Stu mentions in the comments, this solution might affect performance on large datasets. Please keep that in mind.

Trigger to prevent update in postgresql

I want to create a trigger which will prevent update in a row from that table if that entry is present in another table too.
example
Table M
m_id title
1 abc
2 def
3 ghi
Table N
n_id m_id
1 2
2 3
so if try to update values 2 or 3 in table m it shouldn't allow that
whereas if i try to update value 1 from table it should do so.
You'll have to create a trigger BEFORE UPDATE ON m FOR EACH ROW that checks IF OLD.m_id <> NEW.m_id AND EXISTS (SELECT 1 FROM n WHERE n.m_id = OLD.m_id) and throws an exception in that case.

Oracle 12c - Insert values in a table using values from another table

I have a table (TABLEA) like so:
type_id level
1 7
2 4
3 2
4 5
And another table (TABLEB) like so:
seq_id type_id name order level
1 1 display 1 7
2 1 header 2
3 1 detail 3
4 2 display 1 4
5 2 header 2
6 2 detail 3
TABLEB.TYPE_ID is FK to TABLEA.TYPE_ID. Currently I am entering the data in TABLEB manually.
I have 2 new rows in TABLEA.. type_id 3 and 4.
How can I populate data which do not exist in TABLEB automatically using TABLEA? I would like all columns in TABLEB to be inserted automatically.
So, as you can see:
SEQ_ID will be sequential
When ORDER value is 1, NAME value will be "display", and LEVEL will be 7
When ORDER value is 2, NAME value will be "header"
When ORDER value is 3, NAME value will be "detail"
I am expecting after the insert:
seq_id type_id name order level
1 1 display 1 7
2 1 header 2
3 1 detail 3
4 2 display 1 4
5 2 header 2
6 2 detail 3
7 3 display 1 2
8 3 header 2
9 3 detail 3
10 4 display 1 5
11 4 header 2
12 4 detail 3
Any help is appreciated!
You can either:
Have your application code populate both tables, i.e.: INSERTs the appropriate records into to both tables.
(Sounds like you're leaning to) Have Oracle do the work behind the scenes, i.e.: Oracle does the INSERTs in TABLEB for you. The way to do this is by creating a TRIGGER on TABLEA. Here's an example that might get you started: https://stackoverflow.com/a/13356277/1680777
Some people will tell you that TRIGGERs might make debugging difficult because part of your logic is in the database. There's some validity to that criticism. I won't say always/never use triggers. Use them where they make sense: where the value they provide outweighs their complexity.
So this can be done in pure SQL: INSERT ALL which allows us to issue multiple inserts in the same statement.
insert all
into tableb (seq_id, type_id, name, order_id, level_id)
values(tableb_id_seq.nextval, type_id, 'display', 1, level_id)
into tableb (seq_id, type_id, name, order_id)
values(tableb_id_seq.nextval+1, type_id, 'header', 2)
into tableb (seq_id, type_id, name, order_id)
values(tableb_id_seq.nextval+2, type_id, 'detail', 3)
select a.type_id, a.level_id from tablea a
minus
select b.type_id, b.level_id from tableb b
/
The manipulation of the sequence is a bit funny: it is required because every call to NEXTVAL returns the same value in one statement. Obviously in order to make this work, the sequence needs to increment by three:
create sequence tableb_id_seq increment by 3;
This might be enough to rule out such an approach approach. As an alternative you could use (SEQ_ID, ORDER_ID) as a compound primary key but that's not nice either.
By the way, ORDER and LEVEL are keywords: you can't use them as column names.

Is it possible to update 3 sql rows in 1 sql update statement

I have a SQL table like this.
id Order
======== =========
1 4
2 3
3 5
4 1
5 2
Is it possible to update multiple rows in 1 sql statement?
i.e. I want to update id = 3, order = 1 and id = 5, order = 4 and id = 1, order = 1
I know how to do that in 3 update statements. But I would like to know if I can update 3 rows in 1 sql update statement.
Thank you.
You can do this with a single UPDATE statement, but I wouldn't bother.
It makes more sense to use three separate updates in this situation. Trying to do it with one statement makes your code less readable and more error-prone.
But if you really want the single statement, here you go:
UPDATE your_table
SET order = CASE id
WHEN 3 THEN 1
WHEN 5 THEN 4
WHEN 1 THEN 1
END
WHERE id IN (3, 5, 1)
Why do you want to update three rows in one statement?
If the rows must all be in sync with one another, you can do:
BEGIN TRANSACTION;
UPDATE... ;
UPDATE... ;
UPDATE... ;
COMMIT
That way, all work between the begin and commit, is either all completed or none of it is done. This is a key feature of SQL-based relational databases. Even SQLITE has this capability.
Try something like this:
update Orders
set
Order = (
case
when id = 3 then 1
when id = 5 then 4
when id = 1 then 1
end
where id in (3, 5, 1)
Depends on your database though.
You can use CASE if your DBMS supports it.