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

Свинья

Моя первая длительная работа была в фирме "Ниеншанц", царствие ей небесное. Она работала на самописной ERP, которую писали мы - группа из 3-4 человек. Это были 90е годы, мы варились в собственном соку и не слышали про QA. То есть, девелоперская версия кода у нас крутилась отдельно, а база была одна-единственная, она же PROD. В горячие времена мы могли испечь и выкатить несколько релизов в день, без всяких новомодных DevOps, CD, CI.

Вскоре после моего ухода одного из моих коллег нашли рыдающим на клавиатуре. Он повторял одно слово - свинья. К счастью, рыдал он от смеха. Отлаживаясь на PROD, я заводил документы и, чтобы не пересекаться с реальными документами, заводил фиктивных клиентов, товары и услуги.

Так возникла доверенность на некую Свинью, которая умудрилась получить доверенность на Хрюкание. Аудиторы тоже очень удивлялись. Но они чаще плакали.

О форматах данных

Немного повторюсь, эта история из моей работы в Америке, где я работал, в том числе, в стартапе под названием Softlock.

Как образовалась там вакансия DBA? Взлет Softlock совпал с продажей книги Стивена Кинга "Riding the bullet" в формате PDF за символический 1 доллар через Softlock. В час X должна пойти реклама. Разумеется, за несколько недель до часа X программисты днюют и ночуют на работе. За пять минут до рекламы DBA вносит самое последнее изменение. В команде DELETE он забывает WHERE и удаляет все данные в важной таблице. Бэкап! кричит он. Поздно, мы в эфире - говорят ему. Как идут продажи? спрашивают менеджеры.

Короче, кое что им удалось подправить, наделав кучу костылей. DBA все исправил, но, не выдержав позора, покинул фирму. Я же разгребал его костыли вместе с коллегами из страны, где очень любят петь и танцевать. Один коллега, например, хранил суммы в колонке varchar() примерно так: '$123.45'. Я спросил его - зачем? Он пояснил, что это очень удобно, когда сумму отображаешь для пользователя, то не надо делать никаких преобразований.

Шоу для французов

Во Франции вскоре после начала работы меня спросили, насколько я могу ускорить их систему. Я решил выглядеть профессионально, не говорить сразу - в 10 раз, а сказал - процентов на 30-40%. В итоге я ускорил систему более чем в 10 раз - у них для списков не было даже элементарного pagination, но это другая история.

Дело было в пятницу и французы что-то отмечали. Меня спросили, правда ли русские могут пить водку, как показывают в фильмах? Я налил водку в пластиковый стаканчик и продемонстрировал. О, сейчас других позовем - не уходи! И я показал еще и еще.

А потом на меня посмотрели и сказали - "он же сам домой не дойдет". Посадили в машину моего шефа, меня укачало, и я ее облевал. До сих пор стыдно. Зато меня зауважали как специалиста.

Динозавры

Время от времени мне доводилось консультировать разных клиентов. Среди них тогда, в 2000х, еще встречались "динозавры". Я их узнавал по двум характерным вещам:

  • Поставить базу в FULL RECOVERY, не делать бэкапов, дождаться, когда LDF файл станет 100Gb, спросить "почему база так быстро растет"

  • Жаловаться на "утечки памяти SQL", что он "занимает всю память" и перезапускать его все время

Сейчас таких уже почти не встречается, но тогда это были два самых частых вопроса

Рулетка

У одного клиента стояла задача читать из таблицы случайную запись. Это не причуда - это был сайт с тестами, и вопросы надо было рандомизировать. Это делалось замечательным кодом:

SELECT TOP 1 * FROM table ORDER BY newid()

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

Заветы Oracle

В одной конторе люди, которые танцуют и поют, очевидно, пришли из Oracle (да какая им разница на чем лабать код?). После вставки записи им надо было получить @@IDENTITY. Они пишут

SELECT @@IDENTITY

и дальше их клинит. Потому что в Oracle надо обязательно писать FROM, для этого есть даже специальная табличка DUAL. Первое что им приходит в голову - указать имя самой таблицы, допустим, это была таблица FACTS:

SELECT @@IDENTITY FROM FACTS

В этом случае значение IDENTITY копируется столько раз, сколько записей в таблице. Люди, которые танцуют и поют "элегантно" заметают это под ковер:

SELECT DISTINCT @@IDENTITY FROM FACTS

И все замечательно работает... Некоторое время. Пока записей не становится много, и все встает колом. SQL генерит огромный dataset каждый раз, сортирует его и делает distinct.

Параноя

С этим столкнулся мой коллега. Фирма - в России. Некая самописка плохо поддерживаемая. Тормоза. Быстрый анализ показывает, что очень помогут несколько индексов. Индексы создаются и... система отказывается стартовать!

Анализ SQL profiler показывает код, который при старте системы считывает все из sysobjects, sysindexes, syscolumns, и параноидально вычисляет checksums всего, чтобы не дай бог не изменили колонку или таблицу. Ну и индексы в том числе.

Представляю как, наверное, девелопер расчесал свое ЧСВ этим кодом!

Suicide jobs

Нет, это не о камикадзе. Очень интересная система, опять написаная друзьями с юга, имела функциональность асинхронных расчетов на SQL. Тут без сарказма - ребята молодцы, создавали SQL agent jobs, которые выполняли расчеты и потом выпиливались (не ребята, а SQL jobs) - да, в SQL job может удалить саму себя!

Все это работало, но когда количество порождений jobs превысило 3-5 в секунду, пошли deadlocks со стороны SQL agent - он просто не расчитан на такой стиль использования. Я все это переписал на статические асинхронные "worker threads"

Безумная сортировка

Очередная тормозящая система. Порядковые номера документов хранятся почему-то в char(9) центрированными - да, центрированными! То есть '127' будет выглядеть как <три пробела>127<еще три пробела подразумеваются>. А значит, на лету:

ORDER BY convert(int,ltrim(Docnum))

со всеми сопутствующими тормозами. Попутно дата-время хранилось не в datetime, а в двух колонках - одна дата в datetime с уcеченным временем, а время по моему в виде текста. Чтобы найти документ после какого-то точного времени надо писать:

(DATE>'дата' OR (DATE='дата' AND TIME>'время')

со всем радостным, что приносит для поиска OR. Тот, кто ко мне обратился, говорил, что систему писали голландцы (видимо, под веществами)

И еще абсолютно убойный случай

Один раз сотрудник нашей компании (назовем этого парня <NDA>), где я сейчас работаю, умудрился <NDA>, да так, что <NDA>, а потом еще <NDA>, в итоге его самого <NDA>, а потом догнали, и еще <NDA> по самые <NDA>. Кто работает у нас, разумеется, знает эту историю (совсем как "анекдот номер 187... ну нельзя же при женщинах"), а я ее рассказать сейчас не могу.

Может лет через 10 расскажу... А пока почитайте предыдущую статью.

Комментарии (19)


  1. Rusty_Fox
    29.12.2021 22:48
    +5

    SQL имеет весьма низкий порог вхождения и подкупает простотой, отсюда такие "перлы". А какой-нибудь Oracle за счёт мощного оптимизатора ещё и "прощает" подобное. До поры до времени.

    Сам ни раз писал видел запросы, от которых волосы на спине начинают шевелиться.


    1. aegoroff
      29.12.2021 23:30

      А какой-нибудь Oracle за счёт мощного оптимизатора ещё и "прощает" подобное.

      Сейчас все оптимизаторы примерно одинаковы, и вряд ли современный SQL сервер вообще уступает Ораклу, возможно даже наоборот. Оракл это тоже набор костылей - не знаю как сейчас а в версии 9 по моему, сделать запрос SELECT TOP 10 с условиями и сортировкой приходилось через одно место - деталей уже не помню но если надо могу поднять.


      1. BugM
        30.12.2021 01:34

        Это вы легаси проектов живущих на каком-нибудь mysql 5.5 еще не видели.

        Их и мигрировать на более новый mysql сложно, и оптимизатора запросов там по сути нет. Вот и приходится поднимать пыльные архивы so и вспоминать как же построитель запросов работал в те годы. А работал он иногда очень неочевидным образом.


        1. aegoroff
          30.12.2021 08:14

          Это вы легаси проектов живущих на каком-нибудь mysql 5.5 еще не видели.

          Видел и работал даже с версией и 3 и 4. Но вообще MySQL не любил никогда. В версии 3 кажется, некторые типы таблиц (ISAM кажется) даже не умели ACID нормально, а InnoDB были экспериментальными и дико тормозили на чтение. После 4 версии к счастью не сталкивался с этой СУБД более.


      1. Rusty_Fox
        30.12.2021 04:40
        +1

        вряд ли современный SQL сервер вообще уступает Ораклу

        Насколько я в курсе, MS SQL никогда не ругали за плохой оптимизатор, как раз наоборот, хвалили, как и Oracle.

        Удивляться люди начинают, когда тащат "ораклячие" запросы например на PostgreSQL - на малых данных подвоха не заметно, зато на проде все внезапно встаёт колом. Хотя на Oracle работало. Такое видел, хотя Pg активно развивается, может, уже и такого не бывает.

        сделать запрос SELECT TOP 10

        В Oracle, если не изменяет память, версии так с 12-ой (которая 2013 года), добавили limit и offset, так что можно обойтись без извращений с rownum. 9-ка это что-то совсем уж старое.


        1. aegoroff
          30.12.2021 08:07

          В Oracle, если не изменяет память, версии так с 12-ой (которая 2013 года), добавили limit и offset, так что можно обойтись без извращений с rownum. 9-ка это что-то совсем уж старое.

          Да все верно. А максимальная версия с которой я работал - была как раз 10-ка и там приходилось делать такой изврат.

          Удивляться люди начинают, когда тащат "ораклячие" запросы например на PostgreSQL

          Тут в общем то не зависит ничего от того куда чего тащат - тут важно фундаментальное понимание того как все работает, а не конкретный диалект СУБД. Если человек не понимает что такое индекс, что такое FULL SCAN, не умеет делать и читать планы построения запросов - везде будет плохо


    1. mvv-rus
      30.12.2021 06:06

      SQL имеет весьма низкий порог вхождения

      Смотря с какой стороны входить. Если со стороны «настольных» файловых БД (Clipper, FoxPro, Paradox и им подобных), да на Delphi, то MS SQL не прощал принятый в них стиль работы: «встать» на какую-нибудь запись, вывести ее на интерфейс и не спеша менять: MS SQL (точнее, его драйвер в BDE) вешал на эту запись блокировку, и у всех других, кто на нее натыкался, программа показывала песочные часы.
      А вот Interbase, который в комплекте с Delpi шел, такое прощал — потому что поддерживал версионность, наверное в те времена — единственный: и в MS SQL некая поддержка версионности появилась сильно позже, и PostgreSQL тогда в версионность не умел…


      1. Rusty_Fox
        30.12.2021 09:23
        +3

        Под низким порогом вхождения имелась ввиду простота синтаксиса языка структурированных запросов, а вовсе не реализацию драйверов под конкретные СУБД в бородатых годах :)


    1. Gallemar
      30.12.2021 10:22
      +1

      Низкий порог в SQL эти иллюзия. Недавно разговаривал за жизнь с преподавателем курсов по DBA/SQL PostgreSQL - все кто приходил к нему "со знаниями SQL" очень быстро понимали, что весь объем их знаний рассказывается в первые три часа до обеда и не стоит ничего.


      1. hard_sign
        30.12.2021 11:06
        +2

        Не надо путать «порог вхождения» и «эффект Даннинга-Крюгера» :)


      1. Rusty_Fox
        30.12.2021 12:55
        +1

        Низкий порог в SQL эти иллюзия

        Порог вхождения != знание языка в целом. Легкие запросы научиться писать легко, языковых конструкций и ключевых слов не много, синтаксис тривиальный. Сложности начинаются много позже.

        с преподавателем курсов по DBA/SQL PostgreSQL

        Вашего преподавателя зовут Том Кайт Майкл Стоунбрейкер? Отдает каким-то бахвальством, честное слово.


    1. red_baron_fiat
      30.12.2021 12:33

      Все зависит от того, какие таблицы образуют базу данных


  1. red_baron_fiat
    30.12.2021 12:23

     '$123.45' - это действительно удобно!

    Сам так часто делаю, если чмсло 123,45 является "финишным" :)


    1. qw1
      30.12.2021 13:21
      +5

      Никогда не знаешь, какие завтра требования появятся к системе.
      Заказчик вдруг заявит: а посчитайте мне суммы «финишных» значений в разрезах кварталов.
      Что? Не суммируются? Но вот же они, мои финишные цифры, в чём проблема просуммировать?


      1. red_baron_fiat
        30.12.2021 13:38

        Полностью согласен.

        Под "финишниым" мне следовало уточнить, наверное, что это уже данные в "витрине" :)


  1. DenisTrunin
    30.12.2021 14:05

    Попутно дата-время хранилось не в datetime, а в двух колонках - одна дата в datetime с уcеченным временем, а время по моему в виде текста

    Так а что в этом плохого? Т.е. ваш пример(найти все документы после какой-то даты и времени) наверное будет не очень оптимален, но как правило документы ищут просто по конкретной дате. В этом случае хранение даты будет как раз более оптимальным.

    Хранение номеров документов в char является с лидирующими нулями, это как бы тоже типовое и оптимальное решение(т.е. номер накладной у вас будет что-то вроде Накл0001), нужно как раз для сортировки


    1. Tzimie Автор
      30.12.2021 14:46

      Там было ЦЕНТРИРОВАНИЕ, а не правое выравнивание, то есть, если пробел изображать нулем, то 5555 было 005555, а 44444 было 0044444


  1. sshmakov
    30.12.2021 15:58
    +3

    • Жаловаться на "утечки памяти SQL", что он "занимает всю память" и перезапускать его все время

    В те времена MS SQL имел багу в Notification Service, время от времени не завершалась какая-то транзакция от этого сервиса. В результате auto shrink не работал, база росла на диске, а процесс сервиса в памяти. Периодическое прибивание сервиса, но не самого SQL, спасало ситуацию.


  1. polearnik
    30.12.2021 16:01
    +1

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