powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Странный результат последовательности delete + update
13 сообщений из 13, страница 1 из 1
Странный результат последовательности delete + update
    #38975214
Фотография Tonal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Наткнулся на странное поведение при последовательных delete и update в одной транзакции.

Есть табличка с деревом и 2мя порядками для детей:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
CREATE TABLE SYMPTOMS (
  ID D_ID,
  PARENT_ID D_ID_OR_NULL,
  ORD_NUM_EN D_NUM,
  ORD_NUM_RU D_NUM,
  -- Много всякого
  PRIMARY KEY (ID),
  FOREIGN KEY (PARENT_ID) REFERENCES SYMPTOMS (ID) ON DELETE CASCADE
);


В порядках не должно быть дырок.
Вот один из наборов детей:

Код: sql
1.
2.
3.
4.
select r.ID, r.PARENT_ID, r.ORD_NUM_EN, r.ORD_NUM_RU
from SYMPTOMS r
where r.PARENT_ID = 470108
order by r.ORD_NUM


ID PARENT_ID ORD_NUM_EN ORD_NUM_RU
653027 470108 1 1
653026 470108 2 2
653025 470108 3 3
653024 470108 4 4
653023 470108 5 5
653022 470108 6 10
653021 470108 7 13
653020 470108 8 17
470109 470108 9 19
653019 470108 10 9
653018 470108 11 11
470110 470108 12 6
470111 470108 13 7
653017 470108 14 12
653016 470108 15 18
470113 470108 16 14
653015 470108 17 15
653014 470108 18 16
470114 470108 19 8


Одна из операций - удаление детей по некоторому условию.
Если условие выполняется, строка удаляется, а у остальных корректируется порядки.
Вся операция происходит в одной транзакции.
(псевдокод):
Код: python
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
def del_some_child(connection, parent_id, mast_del):
  curs = connection.Cursor()
  curs.execute('select ID, ORD_NUM_EN, ORD_NUM_RU from SYMPTOMS where PARENT_ID = ? order by ORD_NUM_EN', [parent_id])
  childs = [(cid, ne, nr) for cid, ne, nr in curs]
  for cid, n in childs:
    if mast_del(cid):
      curs.execute('delete from SYMPTOMS where ID = ?', [cid])
      curs.execute(
        'update SYMPTOMS set ORD_NUM_EN = ORD_NUM_EN - 1 where PARENT_ID = ? and ORD_NUM_EN > ?', [parent_id, ne])
      curs.execute(
        'update SYMPTOMS set ORD_NUM_RU = ORD_NUM_RU - 1 where PARENT_ID = ? and ORD_NUM_RU > ?', [parent_id, nr])
  connection.commit()


В результате порядки получаются несколько странные:
ID PARENT_ID ORD_NUM_EN ORD_NUM_RU
470109 470108 5 11
470110 470108 6 3
470111 470108 7 4
470113 470108 8 9
470114 470108 10 4

Если идти по детям в обратном порядке ( order by ORD_NUM_EN desc ), то ORD_NUM_EN получается правильный, а ORD_NUM_RU - какой попало...

Кто-нибудь может объяснить такое странное поведение?
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975232
Фотография Tonal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Забыл указать.
Пакет: firebird2.5-super
Версия: 2.5.2.26540.ds4-9ubuntu1
ОС
Код: plaintext
1.
2.
3.
4.
$ lsb_release -drc
Description:    Ubuntu 14.04.2 LTS
Release:        14.04
Codename:       trusty
База в 3-им диалекте.
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975241
Таблоид
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
TonalВерсия: 2.5.2.26540Видимо, это всё относится к http://tracker.firebirdsql.org/browse/CORE-3362 (курсор видит свои же изменения).
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975253
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Таблоид,

вряд ли. Это же не на стороне сервера происходит. Питон наверняка материализует результаты выборки.
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975287
miwaonline
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Симонов Денис,

+1 насчет материализации.

Tonal,

Я бы эти три запроса вывел в отдельную процедуру и из клиента делал только
Код: python
1.
2.
    if mast_del(cid):
      curs.execute('execute procedure clean_sympthoms(?)', [cid])


чтобы исключить "неестественный интеллект" (© DS) промежуточных слоев доступа к базе данных.
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975320
Фотография Tonal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для проверки сделал execute block , аналогичный приведённому коду на python:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
execute block
as
declare id integer;
declare ne integer;
declare nr integer;
declare pid integer = 470108;
begin
  for select r.ID, r.ORD_NUM as ORD_NUM_EN, r.ORD_NUM_RU
    from SYMPTOMS r
    where r.PARENT_ID = :pid -- and r.ID < 471000
    order by r.ORD_NUM
  into :id, :ne, :nr do begin
    if (:id > 471000) then begin
      delete from SYMPTOMS where ID = :id;
      update SYMPTOMS set ORD_NUM = ORD_NUM - 1 where PARENT_ID = :pid and ORD_NUM > :ne;
      update SYMPTOMS set ORD_NUM_RU = ORD_NUM_RU - 1 where PARENT_ID = :pid and ORD_NUM_RU > :ne;
    end
  end
end


Результат его выполнения совпадает с результатом скрипта.
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975324
Фотография Tonal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Т. е. похоже именно CORE-3362
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975329
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Tonal,

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
execute block
as
declare id integer;
declare ne integer;
declare nr integer;
declare pid integer = 470108;
begin
  for select r.ID, r.ORD_NUM as ORD_NUM_EN, r.ORD_NUM_RU
    from SYMPTOMS r
    where r.PARENT_ID = :pid -- and r.ID < 471000
    order by r.ORD_NUM+0
  into :id, :ne, :nr do begin
    if (:id > 471000) then begin
      delete from SYMPTOMS where ID = :id;
      update SYMPTOMS set ORD_NUM = ORD_NUM - 1 where PARENT_ID = :pid and ORD_NUM > :ne;
      update SYMPTOMS set ORD_NUM_RU = ORD_NUM_RU - 1 where PARENT_ID = :pid and ORD_NUM_RU > :nr;
    end
  end
end



так попробуй. Хотя я пока никак не въеду что ты тут собираешься сделать.
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975330
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Tonalorder by r.ORD_NUM
Замени на "order by r.ORD_NUM+0".
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975351
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Симонов Денися пока никак не въеду что ты тут собираешься сделать.
Бездырочную нумерацию он делает. Проктостоматолог, что с него возьмёшь...
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975403
Фотография Tonal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В общем дошло - сам дурак. :)
Значения ORD_NUM-ов берутся старые, до того как начали отрабатывать delete/update.
Чтобы заработало как задумано нужны актуальные. т. е. их нужно перезапрашивать.
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
execute block
as
declare id integer;
declare ne integer;
declare nr integer;
declare pid integer = 470108;
begin
  for select r.ID from SYMPTOMS r where r.PARENT_ID = :pid order by r.ORD_NUM
  into :id do begin
    if (:id > 471000) then begin
      select r.ORD_NUM, r.ORD_NUM_RU
        from SYMPTOMS r where r.ID = :id
        into :ne, :nr;
      delete from SYMPTOMS where ID = :id;
      update SYMPTOMS set ORD_NUM = ORD_NUM - 1 where PARENT_ID = :pid and ORD_NUM > :ne;
      update SYMPTOMS set ORD_NUM_RU = ORD_NUM_RU - 1 where PARENT_ID = :pid and ORD_NUM_RU > :nr;
    end
  end
end


Ну а на Python-е я вовсе по другому сделал - собрал всех оставшихся в список, отсортировал как надь и восстановил нумерацию.
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975406
Фотография Tonal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry SibiryakovБездырочную нумерацию он делает. Проктостоматолог, что с него возьмёшь...

Поддерживаю.
Когда-то было такое дурное решение принято. Теперь смысла нет всё переделывать...
...
Рейтинг: 0 / 0
Странный результат последовательности delete + update
    #38975497
afgm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не столько ответ, сколько для истории
TonalЗначения ORD_NUM-ов берутся старые, до того как начали отрабатывать delete/update.
Чтобы заработало как задумано нужны актуальные. т. е. их нужно перезапрашивать.
Эта проблема была бы замечена если в исходном сообщении был бы пример лямбды mast_del, который не передаёт конкретный id, а передаёт условие cid > ...
Можно и по данным догадаться, но я вот, например, попался :)
На Питон грешить тут действительно нечего, потому как при
Код: python
1.
childs = [(cid, ne, nr) for cid, ne, nr in curs]

выборка полностью фетчится и материализуется (особенности списочных выражений в Питоне, сам драйвер так и делает при fetchall).
TonalНу а на Python-е я вовсе по другому сделал - собрал всех оставшихся в список, отсортировал как надь и восстановил нумерацию.
Я не знаю природы данных и их распределения для таких запросов, но может получится, что будет много апдейтов уже "приговорённых" записей, подпадающих под условие. Может лучше сделать процедуру ..._renumerate(id), которая в 2 прохода перенумерует записи. Для FB3.0 можно будет писать так:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
merge into symptoms s
using (
    select s.id, count(*) over (order by s.ord_num_en) as ord_num_en, count(*) over (order by s.ord_num_ru) as ord_num_ru from symptoms s
) as s_new on s_new.id = s.id
when matched
  -- 1. дополнительное условие если нужно исключить холостые update
  -- 2. is distinct from для корректной обработки null-ов (не в данном случае, а вообще)
  and ( s.ord_num_en is distinct from s_new.ord_num_en
        or
        s.ord_num_ru is distinct from s_new.ord_num_ru
      )
  then update
    set
    s.ord_num_en = s_new.ord_num_en,
    s.ord_num_ru = s_new.ord_num_ru
...
Рейтинг: 0 / 0
13 сообщений из 13, страница 1 из 1
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Странный результат последовательности delete + update
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]