I have successfully solved situation regarding swapping rows by value here what still work excellent.
But by using this function I see some lacks of functionality in mean of
This handles the tabbing order by a set of triggers+ associated functions.
brred for a particular brkalk will cause all values between the old and new values to be rotated, either up or down.brred values above the OLD value (for the same brkalk) to be decremented (shifted down)brred values above the NEW value (for the same brkalk) to be incremented (shifted up)To avoid recursively updating forever, one extra bit of information per row is needed: the flipflag (which should only be touched by the triggers, not by the application code. It could be hidden from the application by means of a view)
ALTER TABLE kalksad1 ADD COLUMN flipflag boolean DEFAULT false;
-- This should be an UNIQUE constraint
-- , but that would need to be deferrable.
CREATE INDEX ON kalksad1 (brkalk,brred);
-- Trigger functions for Insert/update/delete
CREATE function rotate_brred()
RETURNS TRIGGER AS $body$
BEGIN
UPDATE kalksad1 fr
SET brred = brred +1
, flipflag = NOT flipflag -- alternating bit protocol ;-)
WHERE NEW.brred < OLD.brred
-- AND OLD.flipflag = NEW.flipflag -- redundant condition
-- AND OLD.brkalk = NEW.brkalk
AND fr.brkalk = NEW.brkalk
AND fr.brred >= NEW.brred
AND fr.brred < OLD.brred
AND fr.kalk_id <> NEW.kalk_id -- exlude the initiating row
;
UPDATE kalksad1 fr
SET brred = brred -1
, flipflag = NOT flipflag
WHERE NEW.brred > OLD.brred
-- AND OLD.flipflag = NEW.flipflag
-- AND OLD.brkalk = NEW.brkalk
AND fr.brkalk = NEW.brkalk
AND fr.brred <= NEW.brred
AND fr.brred > OLD.brred
AND fr.kalk_id <> NEW.kalk_id
;
RETURN NEW;
END;
$body$
language plpgsql;
CREATE function shift_down()
RETURNS TRIGGER AS $body$
BEGIN
UPDATE kalksad1 fr
SET brred = brred -1
, flipflag = NOT flipflag -- alternating bit protocol ;-)
WHERE fr.brred > OLD.brred
AND fr.brkalk = OLD.brkalk
;
RETURN NEW;
END;
$body$
language plpgsql;
CREATE function shift_up()
RETURNS TRIGGER AS $body$
BEGIN
UPDATE kalksad1 fr
SET brred = brred +1
, flipflag = NOT flipflag -- alternating bit protocol ;-)
WHERE fr.brred >= NEW.brred
AND fr.brkalk = NEW.brkalk
;
RETURN NEW;
END;
$body$
language plpgsql;
-- Triggers for Insert/Update/Delete
-- ONLY for the case where brkalk is NOT CHANGED
CREATE TRIGGER shift_brred_u
AFTER UPDATE OF brred ON kalksad1
FOR EACH ROW
WHEN (OLD.flipflag = NEW.flipflag AND OLD.brkalk = NEW.brkalk AND OLD.brred <> NEW.brred)
EXECUTE PROCEDURE rotate_brred()
;
CREATE TRIGGER shift_brred_d
AFTER DELETE ON kalksad1
FOR EACH ROW
EXECUTE PROCEDURE shift_down()
;
CREATE TRIGGER shift_brred_i
BEFORE INSERT ON kalksad1
FOR EACH ROW
EXECUTE PROCEDURE shift_up()
;
-- Test it
UPDATE kalksad1
SET brred = 2
WHERE brkalk = 2
AND brred = 4;
SELECT * FROM kalksad1
ORDER BY brkalk, brred;
RESULT:
UPDATE 1
kalk_id | brkalk | brred | description | flipflag
---------+--------+-------+---------------------------+----------
36 | 1 | 1 | text index 36 doc 1 row 1 | f
37 | 1 | 2 | text index 37 doc 1 row 2 | f
26 | 2 | 1 | text index 26 doc 2 row 1 | f
43 | 2 | 2 | text index 43 doc 2 row 4 | f
30 | 2 | 3 | text index 30 doc 2 row 2 | t
42 | 2 | 4 | text index 42 doc 2 row 3 | t
12 | 2 | 5 | text index 12 doc 2 row 5 | f
46 | 3 | 1 | text index 46 doc 3 row 1 | f
47 | 3 | 2 | text index 47 doc 3 row 2 | f
32 | 4 | 1 | text index 32 doc 4 row 1 | f
38 | 5 | 1 | text index 38 doc 5 row 1 | f
39 | 5 | 2 | text index 39 doc 5 row 2 | f
(12 rows)
Example 1:
update kalksad1 set brred=_brred
from (
select
row_number() over (
order by brred<2 desc, brred=4 desc, brred>=2 desc, brred
) as _brred,
kalk_id as _kalk_id
from kalksad1
where brkalk=2
order by _kalk_id
) as _
where kalk_id=_kalk_id and brred!=_brred;
Example 2:
update kalksad1 set brred=_brred
from (
select
row_number() over (
order by brred<4 desc, brred!=1 desc, brred>=4 desc, brred
) as _brred,
kalk_id as _kalk_id
from kalksad1
where brkalk=2
order by _kalk_id
) as _
where kalk_id=_kalk_id and brred!=_brred;
If you have unique index on (brkalk,brred) then it would be more complicated, as during renumbering there'll be duplicate brred.
But for many rows I'd recommend using something which was very useful in the days of BASIC language on 8bit computers - number your rows with gaps.
So instead of:
(26, 2, 1, 'text index 26 doc 2 row 1'),
(30, 2, 2, 'text index 30 doc 2 row 2'),
(42, 2, 3, 'text index 42 doc 2 row 3'),
(43, 2, 4, 'text index 43 doc 2 row 4'),
(12, 2, 5, 'text index 12 doc 2 row 5'),
use:
(26, 2, 1024, 'text index 26 doc 2 row 1'),
(30, 2, 2048, 'text index 30 doc 2 row 2'),
(42, 2, 3072, 'text index 42 doc 2 row 3'),
(43, 2, 4096, 'text index 43 doc 2 row 4'),
(12, 2, 5120, 'text index 12 doc 2 row 5'),
Then your examples would just look like:
update kalksad1 set brred=(2048+1024)/2 where kalk_id=43, which would change it to:
(26, 2, 1024, 'text index 26 doc 2 row 1'), (43, 2, 1536, 'text index 43 doc 2 row 4'), (30, 2, 2048, 'text index 30 doc 2 row 2'), (42, 2, 3072, 'text index 42 doc 2 row 3'), (12, 2, 5120, 'text index 12 doc 2 row 5'),
update kalksad1 set brred=(4096+3072)/2 where kalk_id=43, which would change it to:
(30, 2, 2048, 'text index 30 doc 2 row 2'), (42, 2, 3072, 'text index 42 doc 2 row 3'), (26, 2, 3584, 'text index 26 doc 2 row 1'), (43, 2, 4096, 'text index 43 doc 2 row 4'), (12, 2, 5120, 'text index 12 doc 2 row 5'),
Only when there's no gap between rows where the target should be, you'd need to first renumber rows using for example:
update kalksad1 set brred=_brred*1024
from (
select row_number() over (order by brred) as _brred, kalk_id as _kalk_id
from kalksad1
where brkalk=2
order by _brred desc
) as _
where kalk_id=_kalk_id;
This would be much aster than changing every row between source and target. But this'll only matter when there may be many rows to change.