Как перестать делать одно и то же

    Вы любите из раза в раз повторять рутинные операции? Вот и я нет. Но каждый раз в SQL-клиенте при работе с хранилищем Ростелекома приходилось прописывать все джойны между таблицами ручками. И это притом, что в 90% случаев поля и условия соединения таблиц совпадали от запроса к запросу! Казалось бы, любой SQL-клиент имеет функции автозаполнения, но для хранилищ оно не всегда работает: в них редко заводятся unique constraint и foreign key в целях повышения производительности, а без этого программе не узнать, как между собой связаны сущности и что она может тебе предложить.



    Пройдя через отрицание, гнев, торг, депрессию и приближаясь к принятию, я решил — а почему бы самому не попробовать реализовать автозаполнение с блекджеком и как положено? Я пользуюсь клиентом dbeaver, написанным на java, у него есть комьюнити версия с открытым исходным кодом. Созрел нехитрый план:

    1. Найти в исходном коде классы, отвечающие за автозаполнение
    2. Переориентировать их на работу с внешними метаданными и подтягивать оттуда информацию о джойнах
    3. ??????
    4. PROFIT

    С первым пунктом достаточно быстро разобрался — нашел в багтрекере запрос на корректировку автозаполнения и в связанном коммите обнаружил класс SQLCompletionAnalyzer. Посмотрел код — то, что надо. Осталось переписать его так, чтобы все работало. Дождался свободного вечера и начал продумывать реализацию. Правила связей таблиц (метаданные) решил вести в json. У меня не было практического опыта работы с этим форматом и текущая задача виделась возможностью это упущение исправить.

    Для работы с json решил использовать библиотеку json-simple от гугла. Тут начались сюрпризы. Как выяснилось, dbeaver, как труъ-приложение, написан на платформе эклипса с использованием OSGi-фреймворка. Для опытных разработчиков эта штука дает удобство управления зависимостями, для меня же больше была похожа на темную магию, к которой я был явно не готов: как обычно прописываю импорт нужных мне классов из библиотеки json-simple в шапке редактируемого класса, указываю ее в pom.xml, после чего проект категорически отказывается нормально собираться и валится с ошибками.

    Исправить ошибки сборки в итоге получилось: прописал библиотеку не в pom.xml, а в манифесте manifest.mf, как того требует OSGI, при этом указав ее как import-package. Не самое красивое решение, но зато работает. Тут появился следующий сюрприз. Если ты ведешь разработку в intellij idea, нельзя просто так взять и запустить дебаг своего проекта, основанного на платформе eclipse: неопытный разработчик должен страдать не меньше, чем аналитик без автодополнения запросов. На помощь пришли сами разработчики бобра, указавшие в wiki все танцы с бубном, которые надо проделать. Самое обидное, что даже после всех этих приседаний проект не хотел запускаться в дебаге с подключенной через import-package библиотекой json (притом, что в готовый продукт он по-прежнему успешно собирался).

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

    Пример метаданных
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <tableRelations>
        <tableRelation>
            <leftTable>dim_account</leftTable>
            <rightTable>dim_partner</rightTable>
            <joinColumnPair leftColumn="partner_key" rightColumn="partner_key"/>
            <joinColumnPair leftColumn="src_id" rightColumn="src_id"/>
        </tableRelation>
        <tableRelation>
            <leftTable>dim_account</leftTable>
            <rightTable>dim_branch</rightTable>
            <joinColumnPair leftColumn="src_id" rightColumn="src_id"/>
            <joinColumnPair leftColumn="branch_key" rightColumn="branch_key"/>
        </tableRelation>
    </tableRelations>


    В результате я внес изменения в классы SQLUtils и SQLCompletionAnalyzer. Идея такая: если проге не удалось подобрать подходящие предложения автозаполнения по базовой логике, то она проверяет наличие возможных джойнов по внешнему файлу xml. В самом файле хранятся пары таблиц с указанием полей, по которым эти таблицы нужно связывать. Ограничения на технические даты действия записей eff_dttm и exp_dttm и флаг логического удаления deleted_ind при этом проставляются по умолчанию.

    Когда правки в код были внесены, появился вопрос — кто будет наполнять файл с метаданными? Сущностей в хранилище много, самому все связи прописывать накладно. В итоге решил повесить эту задачу на своих коллег-аналитиков. Файл метаданных выложил в svn, откуда делается чекаут в локальную директорию с программой. Принцип такой: в хранилище появилась новая сущность? Один аналитик вносит возможные джойны в файл, коммитит изменения, остальные делают чекаут к себе и наслаждаются работающим автозаполнением: комьюнити, накопление знаний и все такое. Провел для коллег воркшоп по использованию проги, написал статью в конфлюенс — теперь в компании одним удобным инструментом больше.

    Работа над этой фичей дала мне понимание, что не стоит бояться ковырять опенсорсные проекты — как правило, у них понятная архитектура, и даже базовых знаний языка будет достаточно для экспериментов. А при определенной доле упорства даже получится избавиться от ненавистных рутинных операций, сэкономив себе время на новые эксперименты.
    Ростелеком
    Территория возможностей

    Comments 11

      0
      приходилось прописывать все джойны между таблицами ручками. И это притом, что в 90% случаев поля и условия соединения таблиц совпадали от запроса к запросу

      view?
        +2
        Увы мне, боярин.
        Допустим, есть пять таблиц, связанных друг с другом. В одном запросе данные нужны из 1 и 2, в другом — из 3, 4 и 5, а потом из всех вместе. И так далее. Слишком много комбинаций.
        +2
        Потом пилить автоподбор нужной вьюхи придется)
        А если серьезно, то все дополнительно осложняется двухуровневым партиционированием части сущностей — сложно сделать оптимизированную вьюху, при этом удобную всем
      • UFO just landed and posted this here
          +1
          в них редко заводятся unique constraint и foreign key в целях повышения производительности, а без этого программе не узнать, как между собой связаны сущности и что она может тебе предложить

          Не знаю как в других базах, но в Oracle ключи можно создать и сделать их DISABLED
            0
            Я об этом тоже думал! Согласен, классный был бы вариант. Пробовал такое сделать в песочнице, но тема не взлетела. Хотя возможно просто не хватило навыков админа
              0
              А в чё проблема?
              ALTER TABLE products
              ADD CONSTRAINT products_supplier_fk
                FOREIGN KEY (supplier_id)
                REFERENCES supplier(id)
              ;
              ALTER TABLE products
              DISABLE products_supplier_fk
                 constraint_name
              ;
              

              А потом SELECT из DBA_CONSTRAINTS для составления правильных соединений

              Вот доки:
              docs.oracle.com/cd/B28359_01/server.111/b28310/general005.htm#ADMIN11537

              Только что узнал что оказывается raise CONSTRAINT'ов можно направлять в таблицы!
              Типа:
              ALTER TABLE dept ENABLE PRIMARY KEY EXCEPTIONS INTO EXCEPTIONS;
              

              а потом при возникновении исключения делать селекты и выдавать человеческие ошибки, какие строки вывалились в исключения:
              SELECT * FROM EXCEPTIONS;
              fROWID               OWNER      TABLE_NAME      CONSTRAINT
              ------------------  ---------  --------------  -----------
              AAAAZ9AABAAABvqAAB  SCOTT      DEPT            SYS_C00610 
              AAAAZ9AABAAABvqAAG  SCOTT      DEPT            SYS_C00610 
              
              SELECT deptno, dname, loc FROM dept, EXCEPTIONS
                  WHERE EXCEPTIONS.constraint = 'SYS_C00610'
                  AND dept.rowid = EXCEPTIONS.row_id;
              
              DEPTNO     DNAME             LOC
              ---------- --------------    -----------
              10         ACCOUNTING        NEW YORK
              10         RESEARCH          DALLAS
              

                0
                И кстати, насчёт отказа от внешних ключей, крайне плохая идея: издержки мизерные, проблем много. Вот тут Кайт приводит размышления с тестами:
                Эффективное проектирование приложений Oracle / Это база данных, а не свалка данных
                www.rsdn.org/article/db/goodoraapp.xml#EZTAE

                В последней задаче я как раз оптимизировал загрузку данных в БД, средняя нагрузка ~300-400 записей в секунду + ключи/индексы + триггеры + бизнес-логика
                с такой нагрузкой база иногда не справлялась
                удалось довести до скорости обработки 50000 записей за 6-7сек, все ключи, индексы и бизнес-логика осталась, при отключении БЛ, 50000 записей залетало за 0,7 сек в целевую таблицу с ключами и индексами.
                Oracle хорошо умеет массово обрабатывать данные. Обычно проблема производительности в этом и заключается, что этим не пользуются.
              0
              Поясните, пожалуйста, чем xml лучше json при редактировании метаданных.
                0
                метаданные предполагалось редактировать вручную большому количеству аналитиков. Чем более читаемым окажется формат, тем меньше вероятности допустить ошибку редактирования и сделать файл невалидным.
                Для наглядности можно зайти на какой-нибудь online конвертер и сравнить в два формата. Я решил, что количество скобок json неподготовленного пользователя может запутать.

              Only users with full accounts can post comments. Log in, please.