Update foreign key values in foreign table? - sql

Hi I want to update foreign key values in a table in the following manner:
FK
1 to 5
2 to 6
3 to 7
4 to 1
5 to 2
6 to 3
7 to 4
However if I go through the above sequentially as so:
update table set fk = 5 where fk=1
When I want to update value 5 then id be mixing values with the earlier update statement result which had changed value 1 to 5.
How can I update these values without mixing them with the earlier results.

You could temporarily add a column Updated BIT to the table, and when you update a row's value, set this bit to 1 (True - already updated) - and avoid updating those rows that have already been updated!
Step 1: add new column with values 0 (false - not updated yet):
ALTER TABLE dbo.YourTable
ADD Updated BIT NOT NULL DEFAULT(0) WITH VALUES
Step 2: do your updates - respect the Updated flag!
UPDATE dbo.YourTable
SET fk = 5, Updated = 1
WHERE fk = 1 AND Updated = 0
Step 3 : once everything is updated - drop the column again
ALTER TABLE dbo.YourTable
DROP COLUMN Updated

You could use an additional column as FreshPrince suggested. Add new column, set it to the appropriate values, and change the key to the new column.
Another approach may be to use a set of values outside that range. This would eliminate the need to add and remove columns. So...
update table set fk = 15 where fk=1
1 to 15
2 to 16
3 to 17
4 to 11
5 to 12
6 to 13
7 to 14
And then a straight update to subtract 10 from all rows.
update table set fk = fk - 10
You will need to remove and reinstate the foreign key constraint as well.

You could also use a CASE expression to specify the new values in a single UPDATE statement:
UPDATE YourTable
SET fk=CASE fk
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 fk IN (1,2,3,4,5,6,7)
In this particular case, you could also use:
UPDATE YourTable
SET fk=(fk+3)%7+1
WHERE fk BETWEEN 1 AND 7
Razvan

Related

Delete duplicate row in Teradata?

This is a tricky one:
How can I delete 1 instance of a duplicate row from a table with only 1 column.
Eg.. Input Table: temp
myColumn
1
2
2
3
4
I want to delete one instance of 2 without using a temporary table.
Output Table:
myColumn
1
2
3
4
NOTE: You cannot use temporary table and SQL should be written in Teradata syntax only.

SQLITE: Transaction to check constraints after commit

I'm currently working on an sqlite table where I have to do the following:
ID | Name | SortHint
---|-------|---------
0 | A | 1
1 | B | 2
2 | C | 3
ID is the primary key and SortHint is a column with the UNIQUE-constaint. What I have to do is to modify the table, for example:
ID | Name | SortHint
---|-------|---------
0 | A | 3
1 | B | 1
2 | C | 2
The Problem: Because of the UNIQUE I can't simply update one row after another. I tried:
BEGIN TRANSACTION;
UPDATE MyTable SET SortHint = 3 WHERE ID= 0;
...
COMMIT;
But the first update query immideatly fails with:
UNIQUE constraint failed: MyTable.SortHint Unable to fetch row
So, is there a way to "disable" the unique constaint for a transaction and only check all of them once the transaction is committed?
Notes:
I can't modify the table
It works if I only use SortHint values that are not already in the table
I know how to "workaround" this problem, but I would like to know if there is a way to do this as described above
One possibility is to drop the unique constraint and then add it again. That is a little bit expensive, though.
Another would be to set the values to negative values:
UPDATE MyTable
SET SortHInt = - SortHint;
UPDATE MyTable
SET SortHint = 3
WHERE ID = 0;
. . .
If you cannot modify the table, you are not able to remove the constraint.
A workaround could be to change the SortHint to a range that is not in use.
For example you could add 10,000 to all of them. Commit.
Then change to the right number at once which have become free now.
Maybe test afterwards that no numbers of 10,000 or higher exist anymore.

Constraint check with variable values

I have a table called TEST:
PAR CHLD
---------------------- ----------------------
1 2
1 3
1 4
2 5
3 6
The PAR and CHLD column form a composite primary key.
My requirement is that if I'm updating a value in the CHLD column it should only accept any of the existing values in the column.
This should fail because value 7 is not in column CHLD:
UPDATE TEST SET CHLD = 7 WHERE PAR = 3;
This should succeed because value 4 is in column CHLD
UPDATE TEST SET CHLD = 4 WHERE PAR = 3;
Please note I cannot have check constraint for fixed values as I dont know the list of values during table design.
Create another table, e.g. CHILDREN, which contains the valid values (2,3,4,5,6). Then add a referential constraint from your TEST table to CHILDREN.

ordered SQL update

Given a table like this, where x and y have a unique constraint.
id,x,y
1, 1,1
2, 1,2
3, 2,3
4, 3,5
..
I want to increase the value x and y in a set of rows by a fixed amount with the UPDATE statement. Suppose I'm increasing them both by 1, it seems UPDATE follows id order and gives me an error after updating the row with 1 2, since it collides with the next row, 2 3, which hasn't been updated to 3, 4 yet.
Googling around, I can't find a way to force UPDATE to use an order. To do it in reverse would be enough for my application. I am also sure the set is consistent after the whole update.
Any solutions? Some way to force an order into the update, or any way to make it postpone the constraint check until it's finished?
This is meant for a Django application, and it's meant to be compatible with all supported databases. I know some databases have atomic transactions and this problem won't happen, some have features to avoid this problem, but I need a strictly standard SQL solution.
For PostgreSQL you can define the primary key constraint as "deferrable" and then it would be only evaluated at commit time.
In PostgreSQL this would look like this:
postgres=>create table foo (
postgres(> id integer not null,
postgres(> x integer,
postgres(> y integer
postgres(>);
CREATE TABLE
postgres=>alter table foo add constraint pk_foo primary key (id) deferrable initially deferred;
ALTER TABLE
postgres=> insert into foo (id, x,y) values (1,1,1), (2,1,1), (3,1,1);
INSERT 0 3
postgres=> commit;
COMMIT
postgres=> update foo set id = id + 1;
UPDATE 3
postgres=> commit;
COMMIT
postgres=> select * from foo;
id | x | y
----+---+---
2 | 1 | 1
3 | 1 | 1
4 | 1 | 1
(3 rows)
postgres=>
For Oracle this is not necessary as it will evaluate the UPDATE statement as a single atomic operation, so that works out of the box there.
For the sake of reference, MS SQL Server will not present a problem with this either. UPDATE is a single atomic operation.
ALTER TABLE [table_name]
ADD CONSTRAINT unique_constraint
UNIQUE(x)
ALTER TABLE [table_name]
ADD CONSTRAINT unique_constraint2
UNIQUE(y)
update [table_name]
set x = x+1,
y = y+1
Should pose no problem at all.

Swap unique indexed column values in database

I have a database table and one of the fields (not the primary key) is having a unique index on it. Now I want to swap values under this column for two rows. How could this be done? Two hacks I know are:
Delete both rows and re-insert them.
Update rows with some other value
and swap and then update to actual value.
But I don't want to go for these as they do not seem to be the appropriate solution to the problem.
Could anyone help me out?
The magic word is DEFERRABLE here:
DROP TABLE ztable CASCADE;
CREATE TABLE ztable
( id integer NOT NULL PRIMARY KEY
, payload varchar
);
INSERT INTO ztable(id,payload) VALUES (1,'one' ), (2,'two' ), (3,'three' );
SELECT * FROM ztable;
-- This works, because there is no constraint
UPDATE ztable t1
SET payload=t2.payload
FROM ztable t2
WHERE t1.id IN (2,3)
AND t2.id IN (2,3)
AND t1.id <> t2.id
;
SELECT * FROM ztable;
ALTER TABLE ztable ADD CONSTRAINT OMG_WTF UNIQUE (payload)
DEFERRABLE INITIALLY DEFERRED
;
-- This should also work, because the constraint
-- is deferred until "commit time"
UPDATE ztable t1
SET payload=t2.payload
FROM ztable t2
WHERE t1.id IN (2,3)
AND t2.id IN (2,3)
AND t1.id <> t2.id
;
SELECT * FROM ztable;
RESULT:
DROP TABLE
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ztable_pkey" for table "ztable"
CREATE TABLE
INSERT 0 3
id | payload
----+---------
1 | one
2 | two
3 | three
(3 rows)
UPDATE 2
id | payload
----+---------
1 | one
2 | three
3 | two
(3 rows)
NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index "omg_wtf" for table "ztable"
ALTER TABLE
UPDATE 2
id | payload
----+---------
1 | one
2 | two
3 | three
(3 rows)
I think you should go for solution 2. There is no 'swap' function in any SQL variant I know of.
If you need to do this regularly, I suggest solution 1, depending on how other parts of the software are using this data. You can have locking issues if you're not careful.
But in short: there is no other solution than the ones you provided.
Further to Andy Irving's answer
this worked for me (on SQL Server 2005) in a similar situation
where I have a composite key and I need to swap a field which is part of the unique constraint.
key: pID, LNUM
rec1: 10, 0
rec2: 10, 1
rec3: 10, 2
and I need to swap LNUM so that the result is
key: pID, LNUM
rec1: 10, 1
rec2: 10, 2
rec3: 10, 0
the SQL needed:
UPDATE DOCDATA
SET LNUM = CASE LNUM
WHEN 0 THEN 1
WHEN 1 THEN 2
WHEN 2 THEN 0
END
WHERE (pID = 10)
AND (LNUM IN (0, 1, 2))
There is another approach that works with SQL Server: use a temp table join to it in your UPDATE statement.
The problem is caused by having two rows with the same value at the same time, but if you update both rows at once (to their new, unique values), there is no constraint violation.
Pseudo-code:
-- setup initial data values:
insert into data_table(id, name) values(1, 'A')
insert into data_table(id, name) values(2, 'B')
-- create temp table that matches live table
select top 0 * into #tmp_data_table from data_table
-- insert records to be swapped
insert into #tmp_data_table(id, name) values(1, 'B')
insert into #tmp_data_table(id, name) values(2, 'A')
-- update both rows at once! No index violations!
update data_table set name = #tmp_data_table.name
from data_table join #tmp_data_table on (data_table.id = #tmp_data_table.id)
Thanks to Rich H for this technique.
- Mark
Assuming you know the PK of the two rows you want to update... This works in SQL Server, can't speak for other products. SQL is (supposed to be) atomic at the statement level:
CREATE TABLE testing
(
cola int NOT NULL,
colb CHAR(1) NOT NULL
);
CREATE UNIQUE INDEX UIX_testing_a ON testing(colb);
INSERT INTO testing VALUES (1, 'b');
INSERT INTO testing VALUES (2, 'a');
SELECT * FROM testing;
UPDATE testing
SET colb = CASE cola WHEN 1 THEN 'a'
WHEN 2 THEN 'b'
END
WHERE cola IN (1,2);
SELECT * FROM testing;
so you will go from:
cola colb
------------
1 b
2 a
to:
cola colb
------------
1 a
2 b
I also think that #2 is the best bet, though I would be sure to wrap it in a transaction in case something goes wrong mid-update.
An alternative (since you asked) to updating the Unique Index values with different values would be to update all of the other values in the rows to that of the other row. Doing this means that you could leave the Unique Index values alone, and in the end, you end up with the data that you want. Be careful though, in case some other table references this table in a Foreign Key relationship, that all of the relationships in the DB remain intact.
I have the same problem. Here's my proposed approach in PostgreSQL. In my case, my unique index is a sequence value, defining an explicit user-order on my rows. The user will shuffle rows around in a web-app, then submit the changes.
I'm planning to add a "before" trigger. In that trigger, whenever my unique index value is updated, I will look to see if any other row already holds my new value. If so, I will give them my old value, and effectively steal the value off them.
I'm hoping that PostgreSQL will allow me to do this shuffle in the before trigger.
I'll post back and let you know my mileage.
In SQL Server, the MERGE statement can update rows that would normally break a UNIQUE KEY/INDEX. (Just tested this because I was curious.)
However, you'd have to use a temp table/variable to supply MERGE w/ the necessary rows.
For Oracle there is an option, DEFERRED, but you have to add it to your constraint.
SET CONSTRAINT emp_no_fk_par DEFERRED;
To defer ALL constraints that are deferrable during the entire session, you can use the ALTER SESSION SET constraints=DEFERRED statement.
Source
I usually think of a value that absolutely no index in my table could have. Usually - for unique column values - it's really easy. For example, for values of column 'position' (information about the order of several elements) it's 0.
Then you can copy value A to a variable, update it with value B and then set value B from your variable. Two queries, I know no better solution though.
Oracle has deferred integrity checking which solves exactly this, but it is not available in either SQL Server or MySQL.
1) switch the ids for name
id student
1 Abbot
2 Doris
3 Emerson
4 Green
5 Jeames
For the sample input, the output is:
id student
1 Doris
2 Abbot
3 Green
4 Emerson
5 Jeames
"in case n number of rows how will manage......"