Мои пожелания к СУБД будущего, а также к Росреестру в части транзакционности -5





Клиент взаимодействует с базой данных.
С сайта http://corchaosis.ru, автор картины Jonathan Tiong.

Помимо того, что я являюсь программистом (преимущественно, это Delphi + всякие разные СУБД, в последнее время ОРАКЛ, + немного PHP), у меня есть хобби — это купля и продажа квартир. Я покупаю квартиру на этапе строительства от более менее надёжного застройщика по вкусной цене (например, сейчас таким застройщиком является Самолёт, квартиры возле м. Некрасовка продаются), дожидаюсь сдачи дома (часто на два года позже, с недорогими предложениями такое случается), делаю в ней ремонт и затем продаю за 95-100% её рыночной цены.

Так вот, я (как и все) столкнулся с проблемой отсутствия у РосРеестра транзакционности.

Проблема отсутствия у Росреестра транзакционности сделок

В программировании «Транзакция», а в недвижимости это «Сделка с альтернативой» (а также, как её часть, «Договор о банковской ячейке»), и там всё немного более сложно. Рассказываю.

Вася пришёл на просмотр квартиры, которую продаёт Петя. И Васе всё очень понравилось, в том числе и цена, но у Васи денег нет. Так начинается наша история.

Вася имеет свою недвижимость, которая имеет какие-то не особо нужные для него ценности — в соседнем доме жил Ломоносов, высота потолков семь с половиной метров, поблизости находится плодовощная база и рынок Садовод, можно дойти пешком на Аэроэкспресс, под квартирой есть подвал высотой 1 метр, над квартирой есть чердак удобный для астрономических наблюдений. Вася понимает что эти особенности повышают цену его квартиры, но не для него самого. И он решает квартиру Пети купить, а свою квартиру — продать. Но продать именно для того чтобы купить квартиру Пети, а не просто. На языке риэлторов это называется — «Альтернатива подобрана».

Теперь посмотрим на эту ситуацию со стороны Пети. Дело в том, что Пете тоже не интересно сидеть на обесценивающихся деньгах, он продаёт квартиру ради того, чтобы купить себе квартиру в эльфийском городе Валинор, но какую именно — ещё не смотрел. На языке риэлторов это называется — «Сделка с альтернативой».

Два эльфа Средиземья, Маглор и Маэдрос, обладают подходящей (критериям Пети) недвижимостью в городе Валинор, которую срочно распродают, так как отправляются служить Мелькору. На языке риэлторов это называется — «Свободная продажа».

Итак, Вася находит клиента Серёжу. Теперь, Петя находит два подходящих ему варианта в городе Валинор. Выходим на оформление сделки. Допустим для простоты, что никто из участников сделки не использует ипотеку и не имеет долевым собственником несовершеннолетних. Таким образом, теперь должны совершиться следующие действия:
1. Серёжа передаёт деньги Пете.
2. Вася передаёт свою квартиру Серёже.
3. Петя передаёт свою квартиру Васе.
4. Или Маглор, или Маэдрос, передают свою квартиру в Валиноре Пете и получают деньги Серёжи.
5. Малкор и Маэдрос идут в Мордор служить Мелькору.

Идеально было бы передать в Росреестр на выполнение следующий скрипт:

START TRANSACTION
Квартиру Васи отдать Серёже.
Квартиру Пети отдать Васе.
begin
Квартиру Малкора отдать Пете
Деньги Серёжи отдать Малкору
ЕСЛИ_ОШИБКА:
Квартиру Маэдроса отдать Пете
Деньги Серёжи отдать Маэдросу
end
COMMIT TRANSACTION

Это упрощённый скрипт сделки с альтернативой, предполагающий, что у всех квартир один взрослый (и дееспособный) собственник, что их стоимости равны, и что оплата риэлторов (если они есть) оплачивается вне привязки к этапам сделки.

Однако, Росреестр не поддерживает транзакционность. Все действия будут выполняться последовательно и независимо, друг за другом, без отката транзакции в целом если не выполнилось одно из них. Максимум, что можно достичь — учитывая, что Росреестр и МФЦ не работают с передачей наличных средств — это заложить деньги в банковскую ячейку, с условиями доступа к ним Васи, Пети, Серёжи (если вообще никакая сделка не зарегистрирована), и иных действующих лиц, по факту предъявления ими зарегистрированных Росреестром договоров. (И кстати, банки самостоятельно проверку подлинности договоров не осуществляют, то есть доверяют подлинности бумаг участников сделки).

Кроме рисков неполного выполнения транзакции, другая проблема в том, что если другие участники могут въехать в своё новое жильё не дожидаясь полного оформления (привет, вопрос недоплаты коммунальных платежей!), то Маглор и Маэдрос нескоро отправятся служить Мелькору, и возможно, Маглор не сможет подержать в своих руках сильмариллы, он просто не успеет. Сделки с недвижимостью выполняются последовательно, и оформление каждой сделки будет длиться не менее чем 9 рабочих дней.

Кроме этого, Росреестр не поддерживает обременение строящегося по ДДУ жилья, а мог бы, это элементарное действие в отношении простого фьючерса.

Теперь перейдём к недостаткам и моим хотелкам про СУБД

1) Первое — это отсутствие системы контроля версий. Если со стороны Delphi я веду разработку в своей песочнице, и сделанные мной изменения не появятся у других программистов до момента их коммита, то с СУБД не так. И даже если мне доверяют полный (по крайней мере, в рамках нужного для поставленной передо мной задачи) доступ к боевой БД, а такое случается, я не могу на ней разрабатывать. Пока я буду отлаживаться, всё рухнет. Это что за каменный век??? Сделайте песочницу разработчикам.

2) Второе — это отсутствие предустановленных стандартизированных таблиц, описывающих реальный мир. В каждой компании, где я работал, свой собственный формат таблицы, описывающий названия (на русском и (по крайней мере) английском языке, в разных падежах русского языка) двенадцати месяцев!

3) Третье — и тут я воспользуюсь терминологией Оракла — отсутствует возможность вызвать простой скрипт Insert или Update, использующий Returning, так, как мы вызываем Select. Возможно, это не проблемы Оракла, а проблемы стыка Delphi + Oracle.

4) Четвёртое — необходимость назначения создаваемым мной процедурам и функциям полномочий там, где я делать этого не хочу. Я не хочу задавать, а потом менять, полномочия пользователей процедуре и функции. Почему, если я явно не написал Grant-ы, система не могла бы сама посмотреть на задействованные объекты, и в соответствии с правами на действия с ними наделять или нет тех или иных пользователей правом на вызов функции? Я готов написать для этого при написании функций и процедур одно ключевое слово. Или, ещё лучше, пусть пользователь начнёт выполнение, а если ветка алгоритма приведёт его к запросу на который у пользователя нет прав, то выкинет с ошибкой.




Комментарии (37):

  1. hard_sign
    /#21931810

    1. Edition-based redefinition
    2. TO_CHAR(SYSDATE,'MONTH','NLS_LANGUAGE=RUSSIAN')
    3. Начиная с 11g точно есть, раньше – лень смотреть
    4. Такое поведение по умолчанию некорректно, но если очень надо, можно за полчаса написать скрипт, выдающий соответствующие полномочия, с использованием USER-DEPENDENCIES

    • DragonSoft
      /#21931856

      >> 2. TO_CHAR(SYSDATE,'MONTH','NLS_LANGUAGE=RUSSIAN')

      Иногда нужен не именительный падеж. «Второго апреля», «Восьмого марта».

      >> 3. Начиная с 11g точно есть, раньше – лень смотреть

      Returning без into? Просто выдающий данные, как это делает Select? Можно пример?
      У нас в текущей системе как раз 11g.

      • hard_sign
        /#21931924

        2. Вот тут непонятно, какой вопрос к разработчикам СУБД. Не так много языков, где есть понятие «падежа», и не во всех языках с падежами изменяется само слово

        3. returning без into – очень странный запрос. Но если есть суровая революционная необходимость, то копайте в сторону ref_cursor или pipelined functions

        • DragonSoft
          /#21931958

          2. Так это всё равно все фирмы делают, но во всех проектах по-разному. Почему не встроить в БД? В одном городе снесли все ограды и дорожки, а затем по протоптанным людьми тропинкам положили новые. Вот, если все люди топают по одной и той же тропинке, почему её не проложить как надо, организованно и стандартно?

          3. Не думаю, что создать запись и в той же команде узнать её id (создаваемый автоинкрементно, в случае оракла через сиквенс) это странный запрос. Это самый распространённый запрос. Который хочется написать в PL/SQL Developer в одну строку, без создания переменной, вывода её значения и прочих шаманских танцев.

          • hard_sign
            /#21932010

            2. Потому что это не относится к СУБД. Управление НСИ – это совершенно отдельная область знаний.

            3. Автоинкремент – это плохой шаблон, триггер – тоже плохой шаблон. Так писали 20 лет назад, а сейчас так писать не надо. Гораздо лучше взять из базы значение последовательности, а идентификатор новой записи вычислять как S*1000+i, где i – номер вставляемой записи. Как тысячу записей вставили – взяли новый элемент последовательности.

            • DragonSoft
              /#21932028

              >> Гораздо лучше взять из базы значение последовательности, а идентификатор новой записи вычислять как S*1000+i, где i – номер вставляемой записи. Как тысячу записей вставили – взяли новый элемент последовательности.

              Если так сделать, order by id не будет обеспечивать исторический порядок вставок в таблицу. Плюс проблемы со скоростью из-за дырок. Придётся делать order by datetime_creation, каковое поле может отсутствовать, это более ресурсоёмко, плюс учитывайте что время на компьютере меняется квантами (в винде в зависимости от настройки это от 10 до 55 милисекунд) то есть близкие по времени записи будут не в том порядке.

              • hard_sign
                /#21932210

                Использование первичного ключа для чего-то кроме первичного ключа – это тоже очень плохой шаблон. Судя по вашему описанию, там вся система – костыль на велосипеде копипейстом погоняет, а требования – к СУБД будущего…

                • DragonSoft
                  /#21932522

                  Я вам назвал 4 причины, вы вместо того чтобы ответить продолжаете навешивать ярлыки. Вы институтский преподаватель?

                  Имеющий дыры — промежутки праймари кей работает медленнее — это из практики, не теории. Что вы выигрываете своим методом? Скорость добавления в базу? Для закачки больших массивов данных используют BCP.

                  • hard_sign
                    /#21932628 / +1

                    Вообще-то тему «будущего» вы сами подняли в заголовке. СУБД будущего ориентируются всё-таки на современные подходы к разработке, а не на поддержку дремучего legacy. Как по мне, так будущее за KV-хранилищами (потому что при использовании JPA навороченный SQL не очень-то нужен) и умными сетевыми СХД (типа Amazon Aurora).

                    Своим методом я выигрываю уменьшение конкуренции за последовательность. Это не магический объект, а просто строка в системной таблице, обёрнутая «синтаксическим сахаром».

                    А вот мне даже любопытно, какая операция выполняется медленнее, если в PK есть промежутки? У меня фантазии не хватает.

                    • DragonSoft
                      /#21932748

                      Селекты, конечно. по id = ..., по id in(...,...,...), по id between.
                      Я не знаю точно, почему так.

                      • hard_sign
                        /#21933002

                        Фундаментальных причин для этого нет. Ответ на вопрос «почему так» надо искать в условиях тестирования:

                        • насколько прогрет кэш в первом и втором случае
                        • какие диапазоны идентификаторов (работа с длинными идентификаторами может быть чуть медленнее, чем с короткими, но заметно это на сценариях типа NUMBER vs VARCHAR2)
                        • какие параметры PCTFREE/PCTUSED у индекса и у таблицы
                        • сколько записей в таблицах
                        • не было ли массовых удалений из таблицы

                        Ну и так далее.

              • akryukov
                /#21932786

                order by id не будет обеспечивать исторический порядок вставок в таблицу.

                Вам правильно говорят, что у id не должно быть отношения порядка.
                Да, придется делать поле datetime_creation, если вам нужно по нему выбирать.


                С какой целью вам вообще нужно выбирать по дате вставки?

                • DragonSoft
                  /#21932826

                  Чтобы договор номер 2 не создавался раньше договора номер 1?
                  Чтобы логировать и показывать события в правильной последовательности?
                  Чтобы 18 марта 2019 года фирма заключала договора от 20115 до 20267, а не перечислять их номера через запятые?

                  Поле datetime_creation всё равно делать, чтобы помнить дату и время. Но это не гарантирует правильный порядок, как я показал выше.

                  • akryukov
                    /#21932866

                    Чтобы договор номер 2 не создавался раньше договора номер 1?

                    Это какие-то особенные договора? Может быть надо более явно отразить эту логику в коде и требованиях? Предположу, что договор 1 например о покупке товара, а договор 2 о продаже товара. Может их надо связать с какой-то сущностью "сделки"?
                    Может сделать в договоре 2 сделать ссылку на договор 1? Что-то в духе "на основании такого то".


                    Показывать события в нужной последовательности можно и с помощью datetime_creation.


                    Выборку договоров за 18 марта 2019 года можно тоже сделать по полю datetime_creation.


                    Кстати, а как вы без даты создания узнаете что диапазон договоров за эту дату начинается именно с 20115 и заканчивается на 20267?

                    • DragonSoft
                      /#21932888

                      Кстати, а как вы без даты создания узнаете что диапазон договоров за эту дату начинается именно с 20115 и заканчивается на 20267?
                      Никак. Но мне не придётся передавать в функцию список id. Я передам два числа, от и до.
                      Показывать события в нужной последовательности можно и с помощью datetime_creation.
                      сказано выше:
                      время на компьютере меняется квантами (в винде в зависимости от настройки это от 10 до 55 милисекунд) то есть близкие по времени записи будут не в том порядке.
                      Например, менеджер заполнил форму о заключённых 5 договорах и нажал ОК. В системе они появились одновременно, но с порядком следования. Порядок следования будет потерян.

                      • akryukov
                        /#21932916

                        Но мне не придётся передавать в функцию список id. Я передам два числа, от и до.

                        Почему это лучше, чем передавать две даты?


                        Например, менеджер заполнил форму о заключённых 5 договорах и нажал ОК. В системе они появились одновременно, но с порядком следования. Порядок следования будет потерян.

                        Тут вопросы уже к заполняющей системе. Почему эти договоры заключаются одновременно, а не последовательно?
                        Что если в одном из договоров ошибка, а в других все нормально?

                        • DragonSoft
                          /#21932926

                          Они не заключаются одновременно, а вводятся. Например при заливке выполненных работ в систему учёта рабочего времени.

                          Про даты: а если той же функцией надо обработать 1 договор?

                          >> Что если в одном из договоров ошибка, а в других все нормально?

                          Это не повод для delete. В серьёзных системах никогда ничего не delete. Выставляют состояние — ошибка, удалён, клиент передумал, менеджер выдумал договор который не заключался, и всё такое.

                          • akryukov
                            /#21932950

                            а если той же функцией надо обработать 1 договор?

                            Я так понимаю, вы делаете одно и то же действие как над пачкой договоров, так и над одним.
                            Значит у вас будет функция обработки одного договора, которая принимает один id и другая функция, которая принимает диапазон дат и вызывает первую функцию.


                            Предполагаю, что следующее возражение будет о том, что у вас там глобальные переменные меняются и side-эффекты есть. Я угадал?

                            • DragonSoft
                              /#21932968

                              Представьте что функция упаковывает переданные в неё договора в email письмо и отсылает директору. Зачем ему 250 писем вместо одного письма? Если он попросил один договор прислать, то ему пришлют. При чём тут глобальные переменные…

                              • akryukov
                                /#21933070

                                Значит все таки side-эффекты.
                                Это все у вас в СУБД делается что ли?


                                Ну тогда три функции. Одна с фильтром по диапазону дат, другая с указанием конкретного договора. Обе вызывают третью, в которой уже отправка емейла.


                                Это ведь наверняка два разных сценария использования. Там точно нужен один и тот же емейл?
                                Может быть при отправка одного договора "на посмотреть", его надо как-то иначе оформить?

              • akryukov
                /#21932900

                Кстати если вы этот номер потом где-нибудь печатаете, то разглашаете часть бизнес-информации. Контрагент может понять сколько у вас было других договоров за какой-то период времени.

                • DragonSoft
                  /#21932996

                  Вы тоже. Это решается иначе.

                  • akryukov
                    /#21933072

                    Как?

                    • DragonSoft
                      /#21933232

                      Самое простое — Назначать клиенту с названием «ООО Рога и Копыта» буквенный номер договора, образованный транслитом по первым 5 согласным названия: Договор RgKpt-0001, договор RgKpt-0015…
                      Сокращение хранить в базе как атрибут клиента, который редактируется вручную и должен быть уникальным.

                      А там, по задаче. Если не всегда есть интернет — формировать UID, если делать примитивно — то xor 1234567, и так далее по обстоятельствам.

                      Если надо показать себя крутым — то *1000+rand(1000) :)

                      • akryukov
                        /#21933342

                        Ну то есть вы сделаете другое поле, которое будет служить суррогатным ключом, а id будете держать чтобы помнить очередность заключения договоров?
                        Не проще ли сокращение сделать первичным ключом, а очередность помнить по дате?


                        атрибут клиента, который редактируется вручную

                        Вот это тоже непонятно. Зачем нам его редактировать?
                        Ну то есть вы его распечатали на какой-то официальной бумажке с печатями и подписями, а потом менеджер может просто взять и изменить идентификатор в базе и концы в воду.

                        • DragonSoft
                          /#21933378

                          ОК, редактируется вручную при первом вводе клиента, далее не редактируется. Всё равно договор привязан к клиенту не по RgKpt а по id. Да, при такой конфигурации в таблице два уникальных поля. Но вы же не будете ссылаться на эту таблицу из других таблиц через поле-текст?

                          • akryukov
                            /#21933542

                            Но вы же не будете ссылаться на эту таблицу из других таблиц через поле-текст?

                            Ну тут два варианта.


                            1. Либо вы поддерживаете два уникальных поля, одно из которых нужно для определения порядка, потому что в системе могут заноситься несколько договоров одновременно.
                            2. Либо вы используете один единственный id в целях уникальной идентификации и сокрытия бизнес-информации, а дату — для определения порядка заключения.

                            Вы серьезно думаете, что экономя на джойне по строковому id вы что-то выигрываете в первом варианте?

                            • DragonSoft
                              /#21933586

                              Обычно есть «номер договора» — который печатают в документах и в нём могут быть буквы, и id договора — целочисленное поле. Оба поля уникальны в таблице договоров (либо поле номер не уникально, если в таблице хранятся и те договора, номер которых формирует контрагент — тогда есть шанс что два разных контрагента одинаково назовут договор с вами). Это более-менее стандарт…

                              Дату нельзя использовать для определения порядка следования, я дважды написал, почему.

                              Если менеджер носит свой ноутбук и заключает договора в тех местах где не всегда ловит интернет, добавьте UID.

          • akryukov
            /#21932800

            Так это всё равно все фирмы делают, но во всех проектах по-разному.

            Не все и "по разному" тоже не просто так взялось. Почему вам нужна именно таблица для падежей месяцев? Почему не функция с case when then… end?

  2. m03r
    /#21931830

    1) Когда вы разрабатываете локально, то у вас есть локальная копия кода (и даже не одна), она и работает песочницей. С базами тоже можно так сделать, и даже не одним способом. Если этого никто не делает, это проблема администратора.


    2) Боюсь, список таких таблиц будет слишком велик. Вон, половина Википедии описывает реальный мир… А для дат, чисел и валют есть, допустим, libicu


    3) и 4) выглядят как-то совсем локальными проблемами. По поводу 4) в MySQL, например, работает SECURITY DEFINER/INVOKER, как определено в SQL2003, в PostgreSQL, кажется, тоже работает.

  3. gBear
    /#21933086

    Идеально было бы передать в Росреестр на выполнение следующий скрипт:
    А причем, я извиняюсь, тут РР?! РР фиксирует результаты в виде записей КУ и ГРП. Если вы каким-то образом сумеете провернуть такую сделку — результаты её РР зарегистрирует. Имхо, тут вопрос не столько «транзакционности» — которую, кстати, РР вполне даже поддерживает, как минимум, в плане ГРП — сколько фондирования такой сделки.

    Кроме этого, Росреестр не поддерживает обременение строящегося по ДДУ жилья, а мог бы, это элементарное действие в отношении простого фьючерса.
    ?! Каким образом вы представляете себе обременение права, которого (пока ещё) нет? Можно даже «на пальцах». ДДУ — сам по себе уже эдакий «фьючерс права». И начинать надо, наверное, не с РР, а с наличия отсутствия КФО, готовых участвовать в таком — скажем так — деле :-)

    На всякий случай, если я вас не так понял. Если же речь об обременении самого ДДУ — то РР это вполне себе поддерживает.

    • DragonSoft
      /#21933142

      Речь об обременении самого ДДУ. Значит, у меня старые сведения.

    • DragonSoft
      /#21933482

      А причем, я извиняюсь, тут РР?! РР фиксирует результаты в виде записей КУ и ГРП.
      Вот именно. По одной и раздельно. А у меня в примере — транзакция, с вложенной транзакцией внутри неё, и блоком TRY-EXCEPT. И этого он не может. И я описал негативные последствия.

      • gBear
        /#21933662

        Что «вот именно»?! РР — внезапно — не проводит *сделки*. Сделки проводят субъекты. Сами ли, с участием ли третьих сторон — не суть важно. Важно то, что РР лишь фиксирует *результат* этих сделок. Т.е. хотеть от РР всякого типа: «Деньги X отдать Y» и «ЕСЛИ_ОШИБКА» — по меньшей странно. Этих вещей с точки зрения КУ/ГРП (область деятельности РР) просто не существует.

        Если уж и «хотеть» такого рода «транзакционность», то надо хотеть это на той стороне, что проводит сделки… вводить какого-либо рода «гаранта», в лице банка, или чего-то типа «дом.рф» и уже там, «наворачивать» с «Деньги X передать Y» и прочими «ЕСЛИ_ОШИБКА».

        А РР и пять лет назад (точно, даже 7… может и раньше) мог обрабатывать обращение, в котором несколько связанных заявлений (в вашем случае — их будет три) о «переходе права».

        • DragonSoft
          /#21933940

          Связанных транзакцией? Это означает, что если хоть одна из трёх операций не выполнится, то не выполнится никакая?

          Ну, не знаю… Начнём с того, что Росреестр не ведёт приём граждан. Приём граждан ведёт МФЦ. Я пытался через МФЦ подать одновременно две операции — снять обременение и зарегистрировать куплю-продажу, мне отказали. Сказали, что сначала требуется выполнить одно, а затем другое.

          • Barsik68
            /#21950224

            Правильно Вам ответили. Как вы себе представляете параллельное выполнение этих операций?
            Сначала снимается обременение и только потом появляется возможность проводить сделки с объектом недвижимости. И не забывайте о Регламенте работы РР. Ваши заявления обрабатываются не 1сек, а несколько дней-недель, а то и месяцев.

            • DragonSoft
              /#21950786

              Транзакция не означает что операции в ней выполняются параллельно. (Тем более что данный случай это и не транзакция.) Зачем я должен дожидаться пока снятие обременения дойдёт обратно до МФЦ, я его получу, и только потом смогу совершить сделку купли продажи, потратив ещё время на передачу из МФЦ в РосРеестр нового запроса?

              Когда Delphi программа посылает на сервер две команды, не обязательно связанные транзакцией, это всё равно лучше делать одним запросом, чтобы не удвоить издержки на обмен данными.