Глобалы — мечи-кладенцы для хранения данных. Деревья. Часть 2

    Начало — см. часть 1.

    3. Варианты структур при использовании глобалов


    Такая структура как упорядоченное дерево имеет разные частные случаи. Рассмотрим те, которые имеют практическую ценность при работе с глобалами.

    3.1 Частный случай 1. Один узел без ветвей



    Глобалы можно использовать не только подобно массиву, но и как обычные переменные. Например как счётчик:

    Set ^counter = 0  ; установка счётчика
    Set id=$Increment(^counter) ;  атомарное инкрементирование

    При этом глобал, кроме значения, может ещё иметь ещё и ветви. Одно не исключает второго.

    3.2 Частный случай 2. Одна вершина и множество ветвей


    Вообще — это классическая key-value база. А если в качестве значения мы будем сохранять кортеж значений, то получим самую обыкновенную таблицу с первичным ключом.



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

    1. символы-разделители.
      Set ^t(id1) = "col11/col21/col31"
      Set ^t(id2) = "col12/col22/col32"
    2. жёсткую схему, по которой каждое поле занимает заранее определённое число байт. Как и делается в реляционных БД.
    3. специальную функцию $LB (есть в Cache), которая составляет строку из значений.

      Set ^t(id1) = $LB("col11", "col21", "col31")
      Set ^t(id2) = $LB("col12", "col22", "col32")

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

    Давайте создадим индексный глобал по первой колонке.

    Set ^i("col11", id1) = 1
    Set ^i("col12", id2) = 1

    Теперь для быстрого поиска информации по первой колонке нам предстоит заглянуть в глобал ^i и найти первичные ключи (id) соответствующие нужному значению первой колонки.

    При вставке значения мы можем сразу создавать и значение и индексные глобалы по нужным полям. А для надёжности обернём всё это в транзакцию.

    TSTART
    Set ^t(id1) = $LB("col11", "col21", "col31")
    Set ^i("col11", id1) = 1
    TCOMMIT

    Подробноcти как сделать на M таблицы на глобалах, эмуляцию вторичных индексов.

    Работать такие таблицы будут также быстро как и в традиционных БД (или даже быстрее), если функции вставки/обновления/удаления строк написать на COS/M и скомпилировать.
    Это утверждение я проверял тестами на массовых INSERT и SELECT в одну двухколоночную таблицу, в том числе с использованием команд TSTART и TCOMMIT (транзакций).

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

    Без использования транзакций скорость инсертов была на миллионе значений 778 361 вставок/секунду.
    При 300 миллионов значений — 422 141 вставок/секунду.

    При использовании транзакций — 572 082 вставок/секунду на 50М вставок. Все операции проводились из скомпилированного M-кода.
    Жёсткие диски обычные, не SSD. RAID5 с Write-back. Процессор Phenom II 1100T.

    Для аналогичного тестирования SQL-базы нужно написать хранимую процедуру, которая в цикле будет делать вставки. При тестировании MySQL 5.5 (хранилище InnoDB) по такой методике получал цифры не больше, чем 11К вставок в секунду.

    Да, реализация таблиц на глобалах выглядит более сложной, чем в реляционных БД. Поэтому промышленные БД на глобалах имеют SQL-доступ для упрощения работы с табличными данными.

    Вообще, если схема данных не будет часто меняться, скорость вставки некритична и всю базу можно легко представить в виде нормализованных таблиц, то проще работать именно с SQL, так как он обеспечивает более высокий уровень абстракции.
    В этом частном случае я хотел показать, что глобалы могут выступать как конструктор для создания других БД. Как ассемблер, на котором можно написать другие языки. А вот примеры, как можно создать на глобалах аналоги key-value, списков, множеств, табличных, документо-ориентированных БД.
    Если нужно создать какую-то нестандартную БД минимальными усилиями, то стоит взглянуть в сторону глобалов.

    3.3 Частный случай 3. Двухуровневое дерево, у каждого узла второго уровня фиксированное число ветвей


    Вы, наверное, догадались: это альтернативная реализация таблиц на глобалах. Сравним эту реализацию с предыдущей.
    Таблицы на двухуровневом дереве vs. на одноуровневом дереве.
    Минусы Плюсы
    1. Медленнее на вставку, так как нужно устанавливать число узлов равное числу колонок.
    2. Больше расход дискового пространства. Так как индексы глобала (в понимании как индексы у массивов) с названиями колонок занимают место на диске и дублируются для каждой строки.

    1. Быстрее доступ к значениям отдельных колонок, так как не нужно парсить строку. По моим тестам быстрее на 11,5% на 2-х колонках и более на большем числе колонок.
    2. Проще менять схему данных
    3. Нагляднее код

    Вывод: на любителя. Поскольку скорость — одно из самых ключевых преимуществ глобалов, то почти нет смысла использовать эту реализацию, так как она скорее всего будет работать не быстрее таблиц на реляционных базах данных.

    3.4 Общий случай. Деревья и упорядоченные деревья


    Любая структура данных, которая может быть представлена в виде дерева, прекрасно ложится на глобалы.

    3.4.1 Объекты с подобъектами




    Это область традиционного применения глобалов. В медицинской сфере огромно число болезней, лекарств, симптомов, методов лечения. Создавать на каждого пациента таблицу с миллионом полей нерационально. Тем более, что 99% полей будут пустыми.

    Представьте SQL БД из таблиц: «пациент» ~ 100 000 полей, «Лекарство» — 100 000 полей, «Терапия» — 100 000 полей, «Осложнения» — 100 000 полей и т.д. и т.п. Либо можно создать БД из многих тысяч таблиц, каждая под определённый тип пациента (а ведь они могут пересекаться!), лечения, лекарства, и ещё тысячи таблиц для связей между этими таблицами.

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

    На глобалах удобно делать БД с данными о людях, когда важно накопить и систематизировать максимум разнообразной информации о клиенте. Это востребовано в медицине, банковской сфере, маркетинге, архивном деле и других областях
    .
    Безусловно, на SQL тоже можно эмулировать дерево всего несколькими таблицами (EAV, 1,2,3,4,5,6,7,8,9,10), однако это существенно сложнее и будет медленнее работать. По сути пришлось бы написать глобал работающий на таблицах и спрятать всю работу с таблицами под слоем абстракции. Неправильно эмулировать более низкоуровневую технологию (глобалы) средствами более высокоуровневой (SQL). Нецелесообразно.

    Не секрет, что изменение схемы данных на гигантских таблицах (ALTER TABLE) может занять приличное время. MySQL, например, делает ALTER TABLE ADD|DROP COLUMN полным копированием информации из старой в новую таблицу (тестировал движки MyISAM, InnoDB). Что может повесить рабочую базу с миллиардами записей на дни, если не недели.

    Изменение структуры данных, если мы используем глобалы, нам ничего не стоят. В любой момент мы можем добавить любые нужные нам новые свойства к любому объекту, на любом уровне иерархии. Изменения связанные с переименованием ветвей можно запускать в фоновом режиме на работающей БД.

    Поэтому, когда речь идёт о хранении объектов с огромным количеством необязательных свойств, глобалы — отличный выбор.

    Причём, напомню, доступ к любому из свойств — моментален, так как в глобале все пути представляют собой B-tree.

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

    1. Размер документа. Единицей хранения является текст в формате JSON (точнее BSON) максимального объёма около 16МБ. Ограничение сделано специально, чтобы JSON-база не тормозила при парсинге, если в ней сохранят огромный JSON-документ, а потом будут обращаться к нему по полям. В этом документе должна быть сосредоточена вся информация о пациенте. Мы все знаем какими толстыми могут быть карты пациентов. Максимальный размер карты в 16МБ сразу ставит крест на пациентах, в карту болезней которых включены файлы МРТ, сканы ренгенографии и других исследований. В одной ветви глобала же можно иметь информацию на гигабайты и терабайты. В принципе на этом можно поставить точку, но я продолжу.
    2. Время сознания/изменения/удаления новых свойств в карте пациента. Такая БД должна считать в память всю карту целиком (это большой объём!), распарсить BSON, внести/изменить/удалить новый узел, обновить индексы, запаковать в BSON, сохранить на диск. Глобалу же достаточно только обратиться к конкретному свойству и произвести с ним манипуляции.
    3. Быстрота доступа к отдельным свойствам. При множестве свойств в документе и его многоуровневой структуре доступ к отдельным свойствам будет быстрее за счёт того, что каждый путь в глобале это B-tree. В BSON же придётся линейно распарсить документ, чтобы найти нужное свойство.

    3.3.2 Ассоциативные массивы


    Ассоциативные массивы (даже с вложенными массивами) прекрасно ложатся на глобалы. Например такой массив из PHP отобразится в первую картинку 3.3.1.

    $a = array(
      "name" => "Vince Medvedev",
      "city" => "Moscow",
      "threatments" => array(
        "surgeries" => array("apedicectomy", "biopsy"),
        "radiation" => array("gamma", "x-rays"),
        "physiotherapy" => array("knee", "shoulder")
      )
    );


    3.3.3 Иерархические документы: XML, JSON


    Также легко хранятся в глобалах. Для хранения можно разложить разными способами.

    XML
    Самый простой способ раскладки XML на глобалы, это когда в узлах храним атрибуты тэгов. А если будет нужен быстрый доступ к аттрибутам тэгов, то мы можем их вынести в отдельные ветви.

    <note id=5>
    <to>Вася</to>
    <from>Света</from>
    <heading>Напоминание</heading>
    <body>Позвони мне завтра!</body>
    </note>

    На COS этому будет соответствовать код:

    Set ^xml("note")="id=5"
    Set ^xml("note","to")="Саша"
    Set ^xml("note","from")="Света"
    Set ^xml("note","heading")="Напоминание"
    Set ^xml("note","body")="Позвони мне завтра!"

    Замечание: Для XML, JSON, ассоциативных массивов можно придумать много разных способов отображения на глобалах. В данном случае мы не отразили порядок вложенных тэгов в тэге note. В глобале ^xml вложенные тэги будут отображаться в алфавитном порядке. Для строго отражения порядка можно использовать, например, такое отображение:


    JSON.
    На первой картинке из раздела 3.3.1 показано отражение этого JSON-документа:

    var document = {
      "name": "Vince Medvedev",
      "city": "Moscow",
      "threatments": {
        "surgeries": ["apedicectomy", "biopsy"],
        "radiation": ["gamma", "x-rays"],
        "physiotherapy": ["knee", "shoulder"]
      },
    };


    3.3.4 Одинаковые структуры, связанные иерархическими отношениями


    Примеры: структура офисов продаж, расположение людей в МЛМ-структуре, база дебютов в шахматах.

    База дебютов. Можно в качестве значения индекса узла глобала использовать оценку силы хода. Тогда, чтобы выбрать самый сильный ход достаточно будет выбрать ветвь с наибольшим весом. В глобале все ветви на каждом уровне будут отсортированы по силе хода.



    Структура офисов продаж, структура людей в МЛМ. В узлах можно хранить некие кеширующие значения отражающие характеристики всего поддерева. Например, объём продаж данного поддерева. В любой момент мы можем получить цифру, отражающую достижения любой ветви.



    4. В каких случаях наиболее выгодно использовать глобалы


    В первой колонке представлены случаи, когда вы получите существенный выигрыш в скорости при использовании глобалов, а во второй когда упростится разработка или модель данных.
    Скорость Удобство обработки/представления данных
    1. Вставка [с автоматической сортировкой на каждом уровне], [индексированием по главному ключу]
    2. Удаление поддеревьев
    3. Объекты с массой вложенный свойств, к которым нужен индивидуальный доступ
    4. Иерархическая структура с возможностью обхода дочерних ветвей с любой, даже не существующей
    5. Обход поддеревьев в глубину
    1. Объекты/сущности с огромным количеством необязательных [и/или вложенных] свойств/сущностей
    2. Безсхемные данные (schema-less). Когда часто могут появляться новые свойства и исчезать старые.
    3. Нужно создать нестандартную БД.
    4. Базы путей и деревья решений. Когда пути удобно представлять в виде дерева.
    5. Удаление иерархических структур без использования рекурсии


    Продолжение «Глобалы — мечи-кладенцы для хранения данных. Разреженные массивы. Часть 3».

    Disclaimer: данная статья и мои комментарии к ней является моим мнением и не имеют отношения к официальной позиции корпорации InterSystems.
    InterSystems
    68,50
    InterSystems IRIS: СУБД, ESB, BI, Healthcare
    Поддержать автора
    Поделиться публикацией

    Комментарии 192

      +2
      Скажите пожалуйста а ваше утверждение, что у варианта «Двухуровневое дерево» будет «Быстрее доступ к значениям отдельных колонок, так как не нужно парсить строку.» проверено на каких объёмах данных и на каких паттернах данных?
        0
        Это теоретическое предположение, основанное на понимании внутренней структуры глобалов.

        Я предполагаю, что оно будет справедливо на числе колонок большем, чем 10. Или на объёме колонки с несколькими столбцами большем 10KБ.

        Будет интересно увидеть комментарии от сотрудников InterSystems, которые имеют дело с глобалами каждый день.
          +1
          Как раз теоритически всё будет зависить от паттерна данных и настроек хранения глобалов(думаю что их в кашэ можно менять каки в gt.m).

          Может проведёте тесты?
            0
            А почему бы и нет. Уже на 2-х колонках выигрыш 11,4%. При чтении 100M значений. Чем больше колонок или объём полей, тем больше будет выигрыш 2-х ярусного дерева. Тестировал в GT.M.
              0
              Тестовый код на M такой:

              mytest1
              
               set ^a(1)="abcd bla bla   #asdf bla bla   "
               for i=0:1:100000000 do
               . set b=$piece(^a(1),"#",2)

              mytest2
              
               set ^a(1)="abcd#asdf"
               set ^a(1,1)="abcd bla bla   "
               for i=0:1:100000000 do
               . set b=^a(1,1)

              Второй подход побеждает.
                0
                Но это кажется неверный пример для сравнения. Должно быть что-то типа:

                mytest1
                
                 for i=0:1:100000000 do
                 . set ^a(i) = "bla bla bla" _ "#" _  i
                
                
                 for i=0:1:100000000 do
                 . set b=$piece(^a(i),"#",2)
                
                
                mytest2
                
                 for i=0:1:100000000 do
                 . set ^a(i,1) = "bla bla bla"
                 . set ^a(i,2) = i
                
                 for i=0:1:100000000 do
                 . set b=^a(i,1)
                
                
                  –1
                  В моём случае я сравнивал скорости чтения.
                  Если делать по вашей методе — мы включим в общее время ещё и время вставки.

                  На таких значениях никакие кеши уже не сработают и поэтому разница будет микроскопическая.
                  Так как начнут шуршать винты, которые будут конкретно замедлять систему.

                  Если рассматривать с точки зрения, что у нас настолько большая БД, что никакие данные не закешированы, то разница будет, дай бог, в 1-2%.

                  В своём тесте я специально читал одну ячейку, чтобы исключить фактор винтов и проверить только скорость чтения свойства.

                  То, что на вставку будет медленнее второй вариант — очевидно.
                    0
                    Вообще-то, в большинстве баз данных такие чтения одной ячейки миллион раз кэшируются, неужели нет в языке возможности замерить время конкретных циклов чтения? Каких нибудь getTime функций?
                      0
                      В моём тесте тоже всё закешировалось. И в первом и во втором, поэтому я посчитал, что можно доверять. Тем более практика совпала с теорией.

                      Мы исключили время на перемещение головок и чтение с поверхности, и осталось только время разбора структуры глобала и парсинга строки.
                        +2
                        Тогда что вы вообще мерили? В первом тесте запрос к дереву один, но идет обработка результатов в памяти, во втором запросов к дереву два (точнее запрос идет к двум деревьям), но нет обработки результатов в памяти.

                        Естественно, если все запросы к деревьям закешировались, второй будет всегда работать быстрее, так как в нем просто читается значение кэша, а в первом читается значение кэша и идет ещё какая-то обработка. Да, логично что получить константу из памяти проще чем получить константу и ещё с ней что-то делать, только как часто в реальном приложении нужно миллион раз работать с одной константой?

                        Так же естественно, что в реальном приложении скорее всего проиграет второй запрос, который будет дважды перемещать головки и читать с диска, в то время как первый лишь один раз, а дальше работать только с памятью.
                          –2
                          Вы что-то путаете. Тот код, который вы прокомментировали, предложил 4dmonster. Мой код — первый в треде.

                          У глобалов кешируются не значения, а блоки базы.

                          Вопрос был в том, что быстрее сработает — проход по двухуровневому дереву, или по одноуровневому с парсингом строки.

                          А измерялось это для того, чтобы доказать моё утверждение о том, что доступ к отдельным свойствам у второго типа таблиц на глобалах быстрее. В реальных базах кеш широко используется. И все частоиспользуемые области в нём.
                            0
                            Ещё раз, категорически неверно брать значение одной ячейки миллион раз и считать что это тест, так как этот тест ничего реального не покажет. Ведь вы же не берете в реальных приложениях значение одной ячейки столько раз? Поэтому сначала заполняете миллион значений в таблице, а потом тестируйте чтение из каждой ячейки. Чтобы чтение не влияло, заполняете таблицу ДО замеров времени теста.
                              0
                              Я тестировал скорость доступа к отдельному свойству без влияния HDD.

                              Доступа к закешированному значению. Это было сознательно.

                              Если влезет винт, то разница будет маленькой, незаметной. Это вопрос чисто теоретический, нужный только для сравнительной таблицы.
                                0
                                Вы не тестировали вообще НИЧЕГО. Плохо, что не понимая даже основ тестирования, вы пытаетесь писать статьи и что-то кому-то доказывать.
                                  +1
                                  Вот код:
                                  Заголовок спойлера
                                  cat tests.m 
                                  alltests
                                   DO mytest1
                                   DO mytest2
                                   DO msrtest1
                                   DO msrtest2
                                   DO cleanUp
                                   QUIT
                                  
                                  cleanUp
                                   set start=$PIECE($HOROLOG,",",2)
                                   kill ^a
                                   kill ^aa
                                   kill ^ma
                                   kill ^na
                                   set stop=$PIECE($HOROLOG,",",2)
                                   write "for cleanUP  "_(stop-start),!
                                   QUIT
                                  
                                  
                                  repeatsCount()
                                   QUIT 100000000
                                  
                                  mytest1
                                   set start=$PIECE($HOROLOG,",",2)
                                   set ^a(1)="abcd bla bla   #asdf bla bla   "
                                   set repeats=$$repeatsCount()
                                   for i=0:1:repeats do
                                   . set b=$piece(^a(1),"#",2)
                                   set stop=$PIECE($HOROLOG,",",2)
                                   write "for mytest1 "_(stop-start),!
                                   QUIT
                                  
                                  mytest2
                                   set start=$PIECE($HOROLOG,",",2)
                                   set repeats=$$repeatsCount()
                                   set ^aa(1)="abcd#asdf"
                                   set ^aa(1,1)="abcd bla bla   "
                                   for i=0:1:repeats do
                                   . set b=^aa(1,1)
                                   set stop=$PIECE($HOROLOG,",",2)
                                   write "for mytest2 "_(stop-start),!
                                   QUIT
                                  
                                  msrtest1
                                   set start=$PIECE($HOROLOG,",",2)
                                   set repeats=$$repeatsCount()
                                   for i=0:1:repeats do
                                   . set ^ma(i)="bla bla bla"_"#"_i
                                   set stop=$PIECE($HOROLOG,",",2)
                                   write "for msrtest1 write "_(stop-start),!
                                   set start=$PIECE($HOROLOG,",",2)
                                   for i=0:1:repeats do
                                   . set b=$piece(^ma(i),"#",2)
                                   set stop=$PIECE($HOROLOG,",",2)
                                   write "for msrtest1 read "_(stop-start),!
                                   QUIT
                                  
                                  msrtest2
                                   set start=$PIECE($HOROLOG,",",2)
                                   set repeats=$$repeatsCount()
                                   for i=0:1:repeats do
                                   . set ^na(i,1)="bla bla bla"
                                   . set ^na(i,2)=i
                                   set stop=$PIECE($HOROLOG,",",2)
                                   write "for msrtest2 write "_(stop-start),!
                                   set start=$PIECE($HOROLOG,",",2)
                                   for i=0:1:repeats do
                                   . set b=^na(i,1)
                                   set stop=$PIECE($HOROLOG,",",2)
                                   write "for msrtest2 read "_(stop-start),!
                                   QUIT
                                  



                                  Вот результаты:
                                  GTM>DO alltests^tests
                                  for mytest1 62
                                  for mytest2 45
                                  for msrtest1 write 646
                                  for msrtest1 read 88
                                  for msrtest2 write 1041
                                  for msrtest2 read 89

                                  Я не спец в MUMPS, подскажите где я ошибся в коде.
                                  Иначе выходит на выбранном вами паттерне $PIECE выгоднее.
                                    –1
                                    Я тоже не спец в M. Вообще в вашем тесте есть методологическая погрешность: дело в том, что нужно чтобы M полностью сбрасывал буфера перед тем как проводить чтение. Во втором случае буфера значительно больше, и поэтому они сбрасываются во время чтения, что приводит к тому, что на чтение второй подход проиграл секунду.

                                    Т.е. нужно было сделать программки на заполнение глобалов отдельными.

                                    Тест на запись: нужно мерять время не внутренними функциями, а командой линукса time. Тоже из-за буферов. Так как ваш тест может показать что работа выполнена, а на самом деле буфера ещё не сброшены.

                                    Далее: вообще-то из ваших тестов видно, что mytest2 быстрее, как я и говорил.

                                    То что на вставку 2-я реализация таблиц будет медленнее — это я тоже говорил.

                                    Теперь смотрим результат по чтению: 88 и 89 секунд — слишком мало отличие. Если сделать тесты на чтение отдельными программками, то тогда вы увидите истинный результат.
                                      +1
                                      mytest — это ваш подход и по нему кажется что $PIECE отстаёт.
                                      А msrtest — мой подход. И кажется наоборот.

                                      Я учту пункты про буферы и малое различие в выигрыше $PIECE. И выложу что получилось.
                                      +2
                                      FWIW

                                      Вот результаты того-же самого на Haswell-ULT ноутбуке (в режиме экономии батареи) под Cache' 2015.2

                                      DEVLATEST:12:22:59:USER>do ^gtmtests
                                      for mytest1 34
                                      for mytest2 31
                                      for msrtest1 write 335
                                      for msrtest1 read 152
                                      for msrtest2 write 520
                                      for msrtest2 read 185
                                      for cleanUP  0
                                      
                –1
                Это я очень осторожно ответил. На самом деле я думаю, что выигрыш уже будет с 2-х колонок. Так как для поиска нужного индекса (substring) достаточно линейно просмотреть несколько байт (так устроен глобал), а для парсинга строки придётся выделить память под всю строку, распарсить строку, выделить память под нужное поле.
                +2
                Работать такие таблицы будут также быстро как и в традиционных БД (или даже быстрее), если функции вставки/обновления/удаления строк написать на COS/M и скомпилировать.

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

                Если нужно создать какую-то нестандартную БД минимальными усилиями, то стоит взглянуть в сторону глобалов.

                В чем преимущество глобалов по сравнению с другими БД для «создания нестандартных БД»? Предположим у меня в проекте уже есть три разнотипных БД (Event Store, RDBMS, key-value) — что я выиграю, добавив в этот зоопарк еще и глобалы?

                Это область традиционного применения глобалов. В медицинской сфере огромно число болезней, лекарств, симптомов, методов лечения. Создавать на каждого пациента таблицу с миллионом полей нерационально. Тем более, что 99% полей будут пустыми. Представьте SQL БД из таблиц: «пациент» ~ 100 000 полей, «Лекарство» — 100 000 полей, «Терапия» — 100 000 полей, «Осложнения» — 100 000 полей и т.д. и т.п.

                Типично неправильно декомпонованная предметная область. Не нужно там тысяч полей. Достаточно представить ту же информацию не в виде дерева, а в виде E-R, как станет видно, как ее реализовывать.

                Поэтому, когда речь идёт о хранении объектов с огромным количеством необязательных свойств, глобалы — отличный выбор.


                Ага. Вот только

                Общая длина ключа (сумма длин всех индексов) может достигать 511 байт [...] Число уровней в дереве (число измерений) — 31.


                Максимальный размер карты в 16МБ сразу ставит крест на пациентах, в карту болезней которых включены файлы МРТ, сканы ренгенографии и других исследований. В одной ветви глобала же можно иметь информацию на гигабайты и терабайты.

                Да-да-да-да, конечно же.

                Общая длина [...] значения 3.6 МБ для Caché.


                При множестве свойств в документе и его многоуровневой структуре доступ к отдельным свойствам будет быстрее за счёт того, что каждый путь в глобале это B-tree. В BSON же придётся линейно распарсить документ, чтобы найти нужное свойство.

                Ну так индексы же.

                Но в принципе, спор «иерархический kv против документо-ориентированой БД», очевидно, определяется сценарием использования. Если обычно документ достается для обработки целиком (например, возможно, врач открывает карту пациента перед приемом), то ДОБД неплохо справится. Если чаще нужны конкретные части — то нужна декомпозиция, и дальше начинаются варианты (кстати, ДОБД тоже неплохо декомпонуются, тут все упирается в транзакционную семантику).

                Иерархические документы: XML, JSON [...] Также легко хранятся в глобалах.

                Вот только в них легко можно встретить вложенность больше 31 уровня. И что делать?

                Нужно создать нестандартную БД.

                И все-таки я не понимаю, что такое «нестандартная БД». Ну или наоборот, что такое «стандартная»?
                  –1
                  В прошлом посте я приводил свои тесты на вставку на 1M и 100M значений. Это аналогично двухколоночной таблице.

                  Выигрыш: в конце статьи есть таблица, где показано для каких случаев вы получите выигрыш.

                  Вот только в них легко можно встретить вложенность больше 31 уровня. И что делать?


                  Хранить целиком, без декомпозиции на глобал. Как и делают JSON СУБД. Или использовать специализированную JSON СУБД.
                    +1
                    В прошлом посте я приводил свои тесты на вставку на 1M и 100M значений. Это аналогично двухколоночной таблице.

                    С индексами? А если полей порядка пятидесяти, индексов полтора десятка, и из них половина — покрывающие (что и активно используется запросами)?

                    Хранить целиком, без декомпозиции на глобал.

                    А ограничение на размер?
                      0
                      Тут всё просто. В двухколоночном варианте достаточно 1-го дерева. В этом варианте глобалы выигрывают по вставке. А в таблице также строится 1 индекс (тоже B*-tree)

                      Для эмуляции каждого дополнительного индекса потребуется ещё дерево. Но оно также будет строиться для индекса в реляционной базе.

                      Число индексов и индексных глобалов будет одинаковым. Вывод: глобал будет быстрее.

                      Глобал, сам по себе, более низкоуровневая технология, поэтому тут нет ничего удивительного.
                        0
                        Не, не убедили. Вы сравниваете яблоки с бочками, и говорите, что яблоки дешевле.

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

                        Во-вторых, вы совершенно забываете про сценарии массовых операций, где в глобалах вам придется иметь дело с каждой записью отдельно, а в реляционной БД — группой, со всеми вытекающими отсюда оптимизациями.

                        Так что тесты и только тесты, и ничего кроме тестов.

                        (И это мы еще не затронули тот занимательный вопрос, сколько кода вам придется написать, чтобы это реализовать, и сколько ошибок будет при его поддержке. Кто говорил «не надо разрабатывать свою БД»?)
                          –1
                          Во-первых, вы исходите из того, что деревья в глобалах и индексы в реляционных базах работают с одинаковой скоростью


                          Уже скоро язык отвалится объяснять. Когда я делал таблицу с одним индексом и глобал из одного дерева глобал был в разы быстрее.

                          Во-вторых, вы совершенно забываете про сценарии массовых операций, где в глобалах вам придется иметь дело с каждой записью отдельно, а в реляционной БД — группой, со всеми вытекающими отсюда оптимизациями.


                          Всё я помню. С группой — это только в SQL. А уровнем ниже будет тот же поиск строк по дереву и та же обработка строк.

                          И это мы еще не затронули тот занимательный вопрос, сколько кода вам придется написать, чтобы это реализовать


                          Если бы вы прочитали мою статью, то увидели бы, что я рекомендую работать с табличными данными через SQL.
                            +1
                            Уже скоро язык отвалится объяснять. Когда я делал таблицу с одним индексом и глобал из одного дерева глобал был в разы быстрее.

                            И почему вы считаете, что этот ваш опыт масштабируется?

                            С группой — это только в SQL. А уровнем ниже будет тот же поиск строк по дереву и та же обработка строк.

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

                            Если бы вы прочитали мою статью, то увидели бы, что я рекомендую работать с табличными данными через SQL.

                            Что и говорит нам о том, что не надо реализовывать табличные структуры на глобалах, ибо незачем. А SQL-решений вокруг и так хватает, чего уж.
                              –1
                              И почему вы считаете, что этот ваш опыт масштабируется?


                              Тоже уже объяснил. Каждый индекс эквивалентен индексному глобалу. Если на одном выигрывает, то и на 10 выиграет.

                              Какая обработка строк, о чем вы?


                              Например UPDATE t SET a=a+b WHERE b=5

                              Нужно найти строки по индексу b (если есть), а далее каждую строку обработать. Какие тут можно сделать улучшения, которые невозможны для глобалов?
                                +1
                                Тоже уже объяснил. Каждый индекс эквивалентен индексному глобалу. Если на одном выигрывает, то и на 10 выиграет.

                                Вы почему-то считаете, что это линейная зависимость. Почему бы? Никаких доказательств.

                                Тесты, только тесты.

                                Нужно найти строки по индексу b (если есть), а далее каждую строку обработать. Какие тут можно сделать улучшения, которые невозможны для глобалов?

                                Например, получить в качестве результата прохода по индексу не значение основного ключа, а сразу физический адрес, где это лежит.

                                Вот вам контрпример:

                                INSERT Customers (Name, City)
                                SELECT CustomerName, CustomerCity
                                FROM OtherSystemCustomers
                                


                                По City — индекс IX_City. Первичный ключ не указан и создается автоматически. Так вот, вместо того, чтобы делать столько рандомных вставок в IX_City, сколько у нас записей вставлено, мы можем собрать массив туплов (City, ID), сгруппировать его по City, а затем вставить получившиеся группы, тем самым уменьшив количество проходов по индексу.

                                Вы, конечно, можете сделать это на глобалах. Руками. Для каждой отдельной операции, с которой вы столкнетесь.
                                  0
                                  Для каждой отдельной операции, с которой вы столкнетесь.


                                  Я точно также могу получить выборку отсортированную по City и последовательно её вставить. Никаких проблем.

                                  Только я таблицы на глобалах буду использовать в крайнем случае, когда у меня 99% глобалов и тут нужен 1% таблиц.

                                  Руками кодить не хочу. Делать таблицы на глобалах — это принципиальная возможность, а не рекомендованная. В статье об этом написано.
                                    0
                                    Я точно также могу получить выборку отсортированную по City и последовательно её вставить. Никаких проблем.

                                    … и дальше снова будет вопрос, почему вы считаете, что ваши операции (которые в пользовательском коде) равноэффективны операциям в ядре RDBMS.

                                    Тесты, тесты, тесты. Ничего кроме тестов.
                                      –2
                                      Это не пользовательский код. А код, который компилируется в машинные коды у GT.M, и в байт-коды в Caché (там как и в Java есть своя виртуальная машина).

                                      Обычный SQL — это интерпретатор (хранимые процедуры у всех по разному). А COS/M компилируется. Две большие разницы.
                                        0
                                        А код, который компилируется в машинные коды у GT.M, и в байт-коды в Caché (там как и в Java есть своя виртуальная машина).

                                        Он все равно пользовательский, а не системный.

                                        Обычный SQL — это интерпретатор (хранимые процедуры у всех по разному). А COS/M компилируется. Две большие разницы.

                                        А обычный SQL тут никого не волнует. Обычный SQL закончился в тот момент, когда мы сказали INSERT, а парсер на той стороне это обработал. А все индексы — они уже внутри ядра, сильно позже и на другом уровне.
                                          –2
                                          Он все равно пользовательский, а не системный.


                                          Чем отличается скомпилированный в машинные коды пользовательский код от системного?
                                            –1
                                            Оптимизациями, конечно. Вы же утверждали, в свое время, что программа, написанная на ассемблере, быстрее, чем программа, написанная на ЯПВУ. А компилируются они в одни и те же машинные коды. Вот и тут то же самое: то, на чем вы пишете работу с глобалами — это ЯПВУ.

                                            (не говоря уже о том, что пользовательский код работает только с дозволенными абстракциями, а системный — с теми, с которыми хочет системный программист)
                                              0
                                              Код, который написан на COS/М, компилируется в высокооптимизированный системными программистами код. Было бы иначе, я бы не получил 789К/сек инсертов в «пользовательском» коде.
                                                0
                                                Вы это серьезно? Вы правда не понимаете, что ваш ^SET компилируется просто в вызов системной (предоставленной движком) функции?
                                                  –2
                                                  А такое ощущение, что вы не можете догнать. Даже, если и вызывается функция движка, а не inline она в коде, то эта функция и есть высокооптимизированный системными программистами код.
                                                    0
                                                    А теперь задумайтесь о разнице между десятью вызовами «высокооптимизированного кода», и одним вызовом «высокооптимизированного кода» (выполняющего такую же работу, как те десять). Во втором случае, очевидно, больше пространство для оптимизаций.

                                                      –1
                                                      Вообще неочевидно.

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

                                                      Больше пространство для оптимизаций там, куда можно залезть руками.

                                                      Ну и глобалы работали бы медленнее таблиц. Но по моим тестам всё не так. Так что тестируйте, тестируйте…
                                                        –2
                                                        В таком случае бы все высокоуровневые языки, где каждая команда выполняет много работы, были бы быстрее низкоуровневых.

                                                        Вообще никак не связано.

                                                        Больше пространство для оптимизаций там, куда можно залезть руками.

                                                        Так там и залезли.

                                                        Как по вашему, что эффективнее:

                                                        foreach(var item in source)
                                                           target.Add(item);
                                                        


                                                        или

                                                        target.AddRange(source);
                                                        
                                                          –1
                                                          Если первое написать написать на ASM, а второе на Basic, то первое.

                                                          У какого SQL, из которых вы знаете, есть компиляция SQL-запросов в машинные коды? Или хотя бы в байт-коды с возможностью сохранения между сессиями?
                                                            0
                                                            Если первое написать написать на ASM, а второе на Basic, то первое.

                                                            Это один и тот же язык, если что.

                                                            У какого SQL, из которых вы знаете, есть компиляция SQL-запросов в машинные коды?

                                                            А зачем мне компилировать SQL-запрос в машинные коды?

                                                            Вот смотрите. У меня есть таблица на, скажем, полсотни полей, с парой десятков индексов. Я делаю в нее один инсерт: INSERT INTO Target VALUES (...). Это одна команда, пятьдесят один аргумент (имя таблицы + значения колонок). Эта команда превращается в один вызов системной функции блаблаблаPerformInsert с этими же 51 аргументом. Дальше внутри этой функции программист БД (который, наверное, умнее меня и лучше знает ее внутренности) может делать что угодно, применять любые оптимизации, напрямую работать с физическими адресами и так далее. Его воля.

                                                            А теперь все то же самое для псевдотаблиц на глобалах: нужно сделать порядка пятидесяти вставок в глобал с данными, плюс пара десятков вставок в «индексы». Порядка семидесяти независимых вызовов системной функции «вставка в глобал», где каждый из них не знает о соседнем (и, как следствие, не может делать кросс-оптимизации).
                                                              –1
                                                              70 вставок я вот делать не буду. Согласно моему тексту это медленный подход.
                                                              Я сделаю 1 вставку с сочетании с функцией $LB. А на одной вставке, как я уже многократно рассказывал, глобалы быстрее.
                                                                0
                                                                Я сделаю 1 вставку с сочетании с функцией $LB.

                                                                А как же индексы?

                                                                Ну и после этого вы (по вашим же утверждениям) потеряете гибкость схемы и скорость чтения отдельных элементов.
                                                              0
                                                              Простой вопрос: что быстрее, команда INSERT из Caché SQL, или аналогичная по функциональности «процедура», написанная вами на M или COS/M?

                                                              (естественно, мы считаем, что функциональность идентична — один набор и структура данных, индексы, транзакции, блаблабла)
                                                                0
                                                                Скорее всего так:

                                                                Если требуется абсолютная функциональная идентичность — они будут одинаково работать.
                                                                А если достаточно того, чтобы потом Caché SQL мог бы сделать SELECT — тогда написанная на M или COS/M.

                                                                  +3
                                                                  Недавно проводил тестирование «для одного большого банка», где, конечно же, мы проверяли в очередной раз разницу между CacheSQL vs Direct Global Access. В 2015.1 разница, для того простого случая что мы смоделировали, в разы (x2-x3), для 2015.2 на примерно 15%.

                                                                  Т.е. сейчас, в 2015.2, я легко пожертвую 15% ухудшения по сравнению с идеальным случаем на голом глобале, но буду использовать более высокий уровень абстракции. Ибо так проще в долговременном плане.
                                                                    0
                                                                    Т.е. CacheSQL медленнее? Это при полном сохранении функциональности?
                                                                      –1
                                                                      А что удивительного в том, что более высокоуровневое средство (CacheSQL) медленнее низкоуровневого прямого доступа к глобалам?
                                                                        –1
                                                                        Я уже объяснял это выше, повторяться не буду.
                                                                        +1
                                                                        Да, в случае голой заливки данных (я не говорю про запросы), INSERT в CacheSQL всегда будет медленнее чем прямая манипуляция глобалом, т.к. добавляется некоторый объем дополнительной работы необходимый для эмуляции ACID. Программист, работающий с глобалом напрямую обычно игнорирует некоторую транзакционную строгость, выигрывая по скорости. Иногда, при первичной загрузке данных, или в монопольном режиме на это можно закрыть глаза.
                                                                        (Ну т.е. нет, конечно, полного сохранения функциональности, COS/M программист обычно дяже не знает о таких деталях о которых должен заботиться транслятор SQL в Cache' для правильной поддержки транзакционной целостности в SQL)

                                                                        Еще раз, относитесь к прямому доступу к глобалам в Cache'/GT.M как к «ассемблеру», а SQL/объектной обертке в Cache' как C/C++. Большие системы правильнее писать, применяя абстракции более высокого уровня, но иногда, на критичном участке, «можно и на ассемблере пописать».
                                                                          +2
                                                                          Да, в случае голой заливки данных (я не говорю про запросы), INSERT в CacheSQL всегда будет медленнее чем прямая манипуляция глобалом, т.к. добавляется некоторый объем дополнительной работы необходимый для эмуляции ACID. [...] (Ну т.е. нет, конечно, полного сохранения функциональности, COS/M программист обычно дяже не знает о таких деталях о которых должен заботиться транслятор SQL в Cache' для правильной поддержки транзакционной целостности в SQL)

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

                                                                          Работать такие таблицы будут также быстро как и в традиционных БД (или даже быстрее), если функции вставки/обновления/удаления строк написать на COS/M и скомпилировать.


                                                                          Программист, работающий с глобалом напрямую обычно игнорирует некоторую транзакционную строгость, выигрывая по скорости.

                                                                          Вот этот компромис мне очень хорошо понятен, да. Одним пожертвовали, другое выиграли. Меня просто второй пост подряд удивляют (неявные) утверждения, что глобалы — это выиграй все, пожертвовав ничем.
                                                                            –2
                                                                            Вы сами признали уровень изоляции аналогичным snapshot (что весьма высокий уровень), а также с ACID (вопрос не в том, есть ли в глобалсах ACID. Вопрос в том, какой именно).

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

                                                                            Понятно, что если мы выкинем оптимизации под чтение и ACID

                                                                            Никто ACID не выкидывает. И про какие оптимизации, которые на уровне глобалов, сделать принципиально невозможно тоже не ясно.
                                                                              +2
                                                                              Ну вы уж как-нибудь обсудите это с tsafin, который утверждает, что INSERT добавляет дополнительный объем работы именно на ACID, и что в его тестах функциональность INSERT и прямой работы с глобалами отличается.

                                                                              Что возвращает нас к вопросу — а какова же будет сравнительная скорость «ручных» операций на глобалах и INSERT с одинаковой функциональностью? (не умозрительно, а в реальных тестах)

                                                                              Я не отказываюсь от своих слов, просто вы почему-то считаете, что если вы в одном сценарии доказали, что ACID достижим, то он (ACID) автоматически будет везде. Удивительно, но реальный сотрудник InterSystems утверждает обратное.
                                                                                0
                                                                                Вероятно, tsafin имел в виду ACID для слоя SQL а inetstar ACID для самих глобалов.
                                                                                  0
                                                                                  А в чем фундаментальные отличия?
                                                                                    0
                                                                                    SQL слой накручен поверх глобалов и, вероятно, требует дополнительных условий / проверок для своей работы.
                                                                                    Например, для ссылочной целостности.
                                                                                      +1
                                                                                      До ссылочной целостности мы даже не доходили еще, пока ограничились одной таблицей и индексами.

                                                                                      Я, на всякий случай, повторю предмет спора: inetstar утверждал, что таблицы, написанные на самих глобалах вручную (примеры есть в статье), не медленнее, а то и быстрее, чем таблицы в традиционных БД. Я утверждаю, что при сохранении функциональности для этого нет никаких логических оснований, и я хотел бы увидеть подтверждающие утверждение inetstar тесты.

                                                                                      Ключевой пункт тут — при сохранении функциональности. tsafin, приводя свои результаты, признал, что функциональность различна.

                                                                                        –1
                                                                                        Это зависит от того какая функциональность имеется в виду. Ну и конечно от того, ограничиваемся ли мы одной таблицей и её индексами.
                                                                                          +2
                                                                                          Это зависит от того какая функциональность имеется в виду.

                                                                                          Бинго. Что возвращает нас к вопросу методики тестирования (впрочем, чего уж, тестов-то нет никаких).
                                                                                            0
                                                                                            Именно!

                                                                                            Вот для MVC фреймворков на JavaScript придумали задачу ToDo, и на ней демострируют.
                                                                                            Тут надо также.
                                                                                            Может автор в следующей статье дойдёт до конкретных примеров и можно будет сравнить что-то.
                                                                                          0
                                                                                          Я в статью вставил спойлер, где несколько раскрыл на основании каких тестов я утверждаю, что таблицы на глобалах будут работать также или быстрее.

                                                                                          И я нигде не постулировал абсолютно идеентичную функциональность таблиц в SQL-базах и таблиц на глобалах, на которую вы так напираете.

                                                                                          Например, в моих тестах я не делал защиты от вставки строки с тем же ID. Это, наверное, бы замедлило бы мои тесты на несколько процентов.

                                                                                          Глобалы — низкоуровневое средство, дающее отличные скоростные характеристики.

                                                                                          Я специально сказал в статье, что с таблицами лучше работать на SQL. Удобнее.

                                                                                          Программист на глобалах должен знать, что о всех их потенциальных возможностях. В том числе и о той, что на глобалах можно сделать таблицы.

                                                                                          СтОит или не стоит — вопрос уже другой. Если нужно сверхбыстро сохранять информацию, идущую от какого-то источника, то похоже что стоит.

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


                                                                                            Угу.

                                                                                            Это утверждение я проверял тестами на массовых INSERT и SELECT в одну таблицу, в том числе с использованием команд TSTART и TCOMMIT (транзакций).

                                                                                            Без использования транзакций скорость инсертов была на миллионе значений 778 361 вставок/секунду.
                                                                                            При 300 миллионов значений — 422 141 вставок/секунду.

                                                                                            При использовании транзакций — 572 082 вставок/секунду. Все операции проводились из скомпилированного M-кода.


                                                                                            И с чем вы это сравниваете?

                                                                                            И я нигде не постулировал абсолютно идеентичную функциональность таблиц в SQL-базах и таблиц на глобалах, на которую вы так напираете.

                                                                                            Тогда какой смысл говорить, что вы реализовали «таблицы» или вообще сравнивать производительность?

                                                                                            Ну да, есть какая-то операция вставки чего-то куда-то, которая в глобалах выполняется за x. Есть какая-то другая операция вставки чего-то куда-то, которая в другой СУБД выполняется за y. x < y… ну и что? Апельсин легче танкера с нефтью, это правда.

                                                                                            СтОит или не стоит — вопрос уже другой. Если нужно сверхбыстро сохранять информацию, идущую от какого-то источника, то похоже что стоит.

                                                                                            Зачем для этого «таблицы»? Для таких задач лучше всего append-only хранилища.

                                                                                            Незачем программировать вручную, то что уже запрограммировано в SQL.

                                                                                            Бинго.
                                                                                              –1
                                                                                              Добавил в спойлер сравнение с MySQL. Хранимая процедура с циклом, котрая делает вставки — 11К вставок в секунду.
                                                                                                +1
                                                                                                Операции вставки в глобалах и MySQL семантически идентичны? Если нет, то в этих цифрах нет никакого смысла.
                                                                                                  0
                                                                                                  Идеентичны.

                                                                                                  Set ^a(10) = «blabla»
                                                                                                  INSERT INTO a(id, txt) Values(10, «blabla»)
                                                                                                    +1
                                                                                                    Во-первых, для RDBMS это вырожденный пример, в котором она заведомо лишена смысла. Сравнивайте с KV, потому что это все, что вы тут делаете. Чтобы говорить о «таблицах», нужно реализовывать поведение реляционной системы.

                                                                                                    Во-вторых, выше этого сравнения вы говорите об имитации индексов через глобалы. Ваша фраза «Работать такие таблицы будут также быстро как и в традиционных БД (или даже быстрее)» относится к каким «таким» таблицам — с имитацией индексов или без?
                                                                                                      0
                                                                                                      К обоим вариантам. Но таблицы с несколькими индексами я не тестировал.
                                                                                                        +2
                                                                                                        Но таблицы с несколькими индексами я не тестировал.

                                                                                                        … и возвращаемся к началу — к вопросу о том, почему вы считаете, что если вы протестировали часть функциональности, то вы имеете право распространять свои выводы о производительности на всю функциональность.
                                                                                                        0
                                                                                                        В любом случае таблица с 2-мя индексами быстрее работать не станет, а вставка в глобал замедлится в 2 раза. Допустим даже в 3, с учётом транзакции.

                                                                                                        Учитывая 70-ти кратное преимущество по скорости при одной вставке в глобал, результат будет таким же — глобалы победят. В сравнении с MySQL.
                                                                                                          0
                                                                                                          Учитывая 70-ти кратное преимущество по скорости при одной вставке в глобал, результат будет таким же — глобалы победят. В сравнении с MySQL.

                                                                                                          Угу, во-первых, в сравнении с MySQL. Во-вторых, только до определенного количества индексов. Ну и в-третьих — только если ваши предположения о динамике верны.
                                                                                          –1
                                                                                          Ещё я хочу напомнить, что я говорил о скорости эмулированных таблиц, я говорил не про CacheSQL, подразумевая чистый MUMPS.

                                                                                          Я предполагаю что CacheSQL в интерактивном режиме — как и любой SQL — интерпретаторный язык. Не вижу ничего удивительного, что он на 15% медленнее глобалов, программы для работы с которыми компилируемы.
                                                                                            +1
                                                                                            То есть вы утверждаете, что 15% разницы (а в предыдущей версии — 200-300% разницы) между CacheSQL и прямым обращением к глобалам возникают только из-за разницы «интерпретаторный-компилируемый»?
                                                                                              0
                                                                                              Думаю, что это одна из причин. Я не знаю как внутри работает CacheSQL.
                                                                                              0
                                                                                              А еще CacheSQL может исполняться во встроенном режиме, где, как утверждается," SQL statements are converted to optimized, executable code at compilation time".

                                                                                              Интересно, какой из режимов тестировал tsafin, и есть ли существенная разница по производительности между одни и другим.
                                                                                                +4
                                                                                                Ок, попытаюсь объяснить.

                                                                                                Высокоуровневая идея всегда применяемая во всех надстройках Cache' — любой другой язык должен d bnjut транслироваться в INT-код (Cache' ObjectScript) и объектный код, вне зависимости от количества препроцессинга и кодогенерации. Будь это SQL, манифест инсталлятора или описание домена iKnow. В случае компиляции встроенного SQL работает макро-препроцессор (так получилось исторически), в большинстве других случаев работают методы генераторы.
                                                                                                (Вам не удастся встроиться в препроцессор, но генераторы, если надо, Вам помогут реализовать еще какой встроенный доменный язык для любой вашей цели. Степеней свободы достаточно)

                                                                                                Возвращаясь к SQL — кроме упомянутого уже выше и ниже встроенного SQL (embedded SQL), компилируемого препроцессором в низкоуровневый COS в момент компиляции MAC программы или класса, есть еще и динамический SQL, компилируемый в похожую программу в момент первого исполнения.

                                                                                                Если вам интересно посмотреть, то вот упрощенный пример:

                                                                                                1. Создадим персистентный класс

                                                                                                Class User.Habra.BankData Extends %Persistent
                                                                                                {
                                                                                                
                                                                                                Property Amount As %Numeric;
                                                                                                
                                                                                                Property Account As %String;
                                                                                                
                                                                                                }
                                                                                                


                                                                                                И для такого простого метода со встроенным SQL выражением:

                                                                                                ClassMethod InsertAsSQL(Value As %Numeric, AccountName As %String) As %Status
                                                                                                {
                                                                                                    &sql(insert into User_Habra.BankData(account, amount) values (:AccountName,:Value))
                                                                                                    return $$$OK
                                                                                                }
                                                                                                


                                                                                                после компиляции получаем следующий сгенерированный код (Ctrl+Shift+V в Студии):

                                                                                                ...
                                                                                                zInsertAsSQL(Value,AccountName) [ AccountName,SQLCODE,Value ] public { New %ROWCOUNT,%ROWID,%msg,SQLCODE 
                                                                                                     ;---&sql(insert into User_Habra.BankData(account, amount) values (:AccountName,:Value))
                                                                                                      ;--- ** SQL PUBLIC Variables: %ROWCOUNT, %ROWID, %msg, AccountName, SQLCODE, Value
                                                                                                    Do %0Fo
                                                                                                    return 1 }
                                                                                                 q
                                                                                                %0Fo try { n sqldata5d
                                                                                                 	if $zu(115,1)=1||('$TLEVEL&&($zu(115,1)=2)) { TSTART  s sqldata5d=1 }
                                                                                                 	n %i
                                                                                                 	s %i(2)=$g(AccountName),%i(3)=$g(Value)
                                                                                                 	s %ROWID=##class(User.Habra.BankData).%SQLInsert(.%i,$c(0,0,0,0,0,0,0),,,'$g(sqldata5d)),%ROWCOUNT='SQLCODE
                                                                                                 	if $zu(115,1)=1,$g(sqldata5d) { TCOMMIT:SQLCODE'<0  TROLLBACK:SQLCODE<0 1 }
                                                                                                 }
                                                                                                 catch { d SQLRunTimeError^%apiSQL($ze,.SQLCODE,.%msg) if $zu(115,1)=1,$g(sqldata5d) { TROLLBACK 1 } }
                                                                                                 quit  // From %0Fo
                                                                                                ...
                                                                                                %SQLInsert(%d,%check,%inssel,%vco,%tstart=1,%mv=0)
                                                                                                	new bva,%ele,%itm,%key,%l,%n,%nc,%oper,%pos,%s,sqlcode,sn,subs,icol set %oper="INSERT",sqlcode=0,%l=$c(0,0,0)
                                                                                                	if $a(%check,7) { new %diu merge %diu=%d }
                                                                                                	if $d(%d(1)),'$zu(115,11) { if %d(1)'="" { set SQLCODE=-111,%msg=$$FormatMessage^%occMessages(,"%SQL.Filer","SQLFiler6",,"ID","User_Habra"_"."_"BankData") QUIT ""  } kill %d(1) } 
                                                                                                	if '$a(%check),'..%SQLValidateFields(.sqlcode) { set SQLCODE=sqlcode QUIT "" }
                                                                                                	do ..%SQLNormalizeFields()
                                                                                                	if %tstart { TSTART:($zu(115,1)=1)||('$TLEVEL&&($zu(115,1)=2))  } set $zt="ERRORInsert"
                                                                                                	if '$a(%check) {
                                                                                                		do {
                                                                                                			if $g(%vco)'="" { d @%vco quit:sqlcode<0  }
                                                                                                		} while 0
                                                                                                		if sqlcode<0 { set SQLCODE=sqlcode do ..%SQLEExit() QUIT "" }		// A constraint failed
                                                                                                	}
                                                                                                	if '$d(%d(1)) { set %d(1)=$i(^User.Habra.BankDataD) } elseif %d(1)>$g(^User.Habra.BankDataD) { if $i(^User.Habra.BankDataD,$zabs(%d(1)-$g(^User.Habra.BankDataD))) {}} elseif $d(^User.Habra.BankDataD(%d(1))) { set SQLCODE=-119,%msg=$$FormatMessage^%occMessages(,"%SQL.Filer","SQLFiler33",,"ID",%d(1),"User_Habra"_"."_"BankData"_"."_"ID") do ..%SQLEExit() QUIT "" }
                                                                                                	set:'($d(%d(4))#2) %d(4)=""
                                                                                                	if '$a(%check,2) {
                                                                                                		new %ls lock +^User.Habra.BankDataD(%d(1))#"E":$zu(115,4) set %ls=$s('$t:0,1:$case($SYSTEM.Lock.ReturnCode(),0:1,4:2,2:2,1:3,:""))
                                                                                                		set:%ls=2 $e(%check,2)=$c(1) set:%ls=2&&($tlevel) %0CacheLock("User.Habra.BankData","E")=1 set:$case(%ls,1:1,2:1,:0) $e(%l)=$c(1)
                                                                                                		if '%ls||(%ls=3) { set SQLCODE=-110,%msg=$$FormatMessage^%occMessages(,"%SQL.Filer",$s('%ls:"SQLFiler40",1:"SQLFiler55"),,%oper,"User_Habra"_"."_"BankData",$g(%d(1))) do ..%SQLEExit() QUIT ""  }
                                                                                                	}
                                                                                                	set ^User.Habra.BankDataD(%d(1))=$lb($g(%d(4)),$g(%d(3)),$g(%d(2)))
                                                                                                	lock:$a(%l) -^User.Habra.BankDataD(%d(1))#"E"
                                                                                                	TCOMMIT:%tstart&&($zu(115,1)=1)
                                                                                                	set SQLCODE=0
                                                                                                	QUIT %d(1) 			// %SQLInsert
                                                                                                
                                                                                                


                                                                                                Я не представляю себе COS программиста, который будет в своем коде так заботиться об уровнях транзакции, блокировках, откатах и обработке ошибок. COS/M код, созданный человеком, будет значительно короче и проще, и не будет заботиться (скорее всего) о всех граничных условиях. Хотя и не факт, что будет более читабельным (вон смотрите, там даже комментарии иногда генерируются :) )

                                                                                                В любом случае, прошу запомнить — у нас нет интерпретации при работе с SQL, транслятор SQL генерирует COS код, и, при разборе запросов, применяет все полагающиеся оптимизирующие эвристики. SQL это не просто и борьба за улучшения в генерируемом коде происходит постоянная.

                                                                                                P.S.

                                                                                                Большая просьба к участникам дискуссии — давайте попытаемся не выходить за пределы компетенции, и, в любом случае, попытаемся вести себя уважительно.
                                                                                                Мне нравится тенденция, наблюдаемая у некоторых участников дискуссии, пойти и почитать документы про Cache' (что я всячески приветствую), и давайте все остальные вопросы, которые возникнут по результатам тестирования задавать нам по другим каналам (письмом или через социальные сети). С удовольствием ответим!
                                                                                      –2
                                                                                      Да, в случае голой заливки данных (я не говорю про запросы), INSERT в CacheSQL всегда будет медленнее чем прямая манипуляция глобалом, т.к. добавляется некоторый объем дополнительной работы необходимый для эмуляции ACID. Программист, работающий с глобалом напрямую обычно игнорирует некоторую транзакционную строгость, выигрывая по скорости. Иногда, при первичной загрузке данных, или в монопольном режиме на это можно закрыть глаза.

                                                                                      То есть все же полная функциональность не сохраняется? Так зачем вы об этом пишете?
                                    +3
                                    >> Вот только в них легко можно встретить вложенность больше 31 уровня. И что делать?
                                    На самом деле не легко, если только искусственно создать или спецом не оптимально, даже в самых монстрах не будет и 20. А 31 уровня более чем достаточно чтобы систематизировать все знания человечества.
                                      0
                                      Вы просто не встречались с, прости г-ди, энтерпрайзным xml, где внутри подряд три вложенных конверта, а только потом данные начинаются.

                                      Это же не структура классификатора, это программные данные, а там всякое случается.
                                        0
                                        А вот здесь подробнее, пожалуйста! Ну 3 вложенных конверта, и данные, как тут больше 31 уровня подындексов получается?

                                        Можно примерчик (чисто для развития)
                                          +1
                                          Вложенность html тегов в произвольной странице сети может зашкаливать и за 50 уровень, при использовании кучи div'ов и table для верстки.
                                            +1
                                            Это я понимаю, но я спрашивал про другой тип данных — xml структуру данных, представляющее собой сериализацию (в xml, json, yaml, toml, что-нибудь еще более модное) какой-то определенной иерархии объектов с полями и с вложенностью. Что предполагает немного другой сценарий использования, и больший, скажем так, «порядок».

                                            Хотелось бы посмотреть именно на «enterprise xml» с адской вложенностью, чтобы понять где наши ограничения будут упираться в пределы движка.
                                              0
                                              Ну вы же понимаете, что вам никто реальный «enterprise xml» реальной компании никогда не покажет. Ну примерно, в одной фирме где я работал xml выглядели как-то так (сознательно извратил насколько мог, но принцип понятен, причем в нем может быть бесконечная вложенность устройств друг в друга):

                                              Внимание большой xml
                                              <soap:Envelope
                                                      xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
                                                      soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
                                              
                                                  <soap:Body xmlns:m="http://www.example.org/stock">
                                                      <m:GetInventoryResponse>
                                                          <m:Countries>
                                                              <m:Country>
                                                                  <m:Name>Russia</m:Name>
                                                                  <m:Regions>
                                                                      <m:Region>
                                                                          <m:Name>Omsk</m:Name>
                                                                          <m:Cities>
                                                                              <m:City>
                                                                                  <m:Name>Omsk</m:Name>
                                                                                  <m:Offices>
                                                                                      <m:Office>
                                                                                          ...
                                                                                          <m:Networks>
                                                                                              <m:Network>
                                                                                                  <m:Servers>
                                                                                                      <m:Server>
                                                                                                          <m:cards>
                                                                                                              <m:card>
                                                                                                                  <m:ports>
                                                                                                                      <m:port>
                                                                                                                          <m:links>
                                                                                                                              <m:link>
                                                                                                                                  <m:devices>
                                                                                                                                      <m:device>
                                                                                                                                          <m:cards>
                                                                                                                                              <m:card>
                                                                                                                                                  <m:ports>
                                                                                                                                                      <m:port>
                                                                                                                                                          // Вложенная структура других device
                                                                                                                                                      </m:port>
                                                                                                                                                  </m:ports>
                                                                                                                                              </m:card>
                                                                                                                                          </m:cards>
                                                                                                                                      </m:device>
                                                                                                                                  </m:devices>
                                                                                                                              </m:link>
                                                                                                                          </m:links>
                                                                                                                      </m:port>
                                                                                                                  </m:ports>
                                                                                                              </m:card>
                                                                                                          </m:cards>
                                                                                                      </m:Server>
                                                                                                  </m:Servers>
                                                                                              </m:Network>
                                                                                          </m:Networks>
                                                                                      </m:Office>
                                                                                  </m:Offices>
                                                                              </m:City>
                                                                          </m:Cities>
                                                                      </m:Region>
                                                                  </m:Regions>
                                                              </m:Country>
                                                          </m:Countries>
                                                      </m:GetInventoryResponse>
                                                  </soap:Body>
                                              </soap:Envelope>
                                              

                                                0
                                                Собственно говоря, это тоже SOAP. Не вижу смысла такое декомпозировать на глобалы. Подобного рода сообщения удобнее хранить целиком.
                                                  0
                                                  … а потом вам надо в этом «хранить целиком» найти ответ, в котором содержится узел, удовлетворяющим конкретным критериям. Желательно все-таки не полным перебором и десериализацией.

                                                  (и да, это реальный сценарий использования, я такую систему разработал и внедрил)
                                                    0
                                                    И как же вы реализовали быстрый доступ к отдельным узлам XML-документа? Разве не парсингом с использованием библиотек?
                                                      –2
                                                      Конечно, нет. Взял MS SQL, у того встроенная поддержка XML, дальше просто строил запросы на смеси T-SQL и XPath.
                                                        –2
                                                        XPath — это и есть библиотека, которая парсит XML.
                                                          +2
                                                          Конечно, нет. XPath — это язык запросов к XML.
                                                    0
                                                    Это обычная inventory.xml, описывающая обычную сеть обычной распределенной компании. Иногда уровни вложенности могут быть бесконечными, особенно если там прибавить связи разными сервисами в облаках, один из которых указывает на другой и т.д… Это рядовая задача энтерпрайза.
                                                      0
                                                      Разве есть смысл такое декомпозировать на глобалы для быстрого доступа к отдельным узлам?
                                                      Глобалы и XML — это специфичные ситуации, а не волшебная пилюля от всего.
                                                        0
                                                        Если вы хотите конкурировать с mongoDB, то естественно нужно. Смысл mongoDB в том что она очень быстро записывает и отдает как целый документ, так и его отдельные части. Предлагать клиентам вон ту структуру данных пишите нам, а с вон с той идите лесом в другую базу — верный способ их потерять. Проблема в том что ваши глобалы предлагают либо писать все целиком, либо отдельными узлами, но при этом с ограничениями вложенности, размера и не факт что сборка целого json'a документа будет быстрой.
                                                          0
                                                          Отдельные части Mongo не сможет отдать так же быстро как глобалы, из-за того, что придётся парсить документ и вынимать из него отдельную часть. Я не проводил тестов. Мне это ясно из устройства баз. Моё мнение.

                                                          У Mongo для JSON свои сильные преимущества. К ней не нужно реализовывать свой язык запросов.
                                                          Глобалы тоже прекрасно хранят JSON, но для этого нужно писать свой код или использовать EWD, например.

                                                          Этот пост вообще не для клиентов. Просто о возможностях глобалов для разработчиков.
                                                            0
                                                            Мне это ясно из устройства баз. Моё мнение.

                                                            Боюсь ваше устройство баз весьма слабое, в монге храниться не обычный JSON, специально проиндексированный и заранее распарсенный BSON (как я понимаю с индексом с помощью того же бинарного дерева). Так что чье кун-фу сильнее должны решать тесты, а не чье-то мнение о неправильном устройстве баз.
                                                              0
                                                              Я же писал о том, что это BSON. Будьте внимательнее.

                                                              И читал устройство формата BSON, который нужно парсить программно. В BSON не используются внутри B*-tree, и это не словарь со сложностью доступа O(1). Парсинг нужен.

                                                              Больше вложенности — больше парсинга.
                                              +2
                                              Возьмем простой SOAP (неймспейсы я повырезаю):

                                              <s:Envelope>
                                              <s:Body>
                                              <MessagePart/>
                                              </s:Body>
                                              </s:Envelope>
                                              


                                              Посмотрим, сколько уровней нам надо для его представления:
                                              "156" //идентификатор сообщения
                                              "156","s:Envelope" //корневой элемент
                                              "156","s:Envelope", "0", "s:Body" //тело SOAP
                                              "156","s:Envelope", "0", "s:Body", "0", "MessagePart" //добрались до собственно данных
                                              


                                              (Откуда нули? Из того банального соображения, что элементов с одним наименованием может быть много, а глобалы, насколько я понял, не умеют несколько одинаково названных подиндексов. Ну и порядок тегов тоже имеет значение. Плюс надо атрибуты отдельно выносить, для них тоже поможет, благо, вот они не имеют порядка.)

                                              Итого один уровень вложенности в XML дает нам два индекса, соответственно, чтобы получить 31 уровень, нужно, округляя, 16 уровней вложенности.

                                              Вот вам пример сообщения в СМЭВ при обмене с одним малоизвестным ведомством:

                                              <S:Envelope>
                                                <S:Body>
                                                  <ws:DXSmev>
                                                    <smev:MessageData>
                                                      <smev:AppData>
                                                        <dx:DXBox>
                                                          <dx:DXPack>
                                                            <dx:Documents>
                                                              <ns2:exitRestrictionActRequest>
                                                                <data>
                                                                  <AddressData>
                                                                    <Address>
                                                                      <OKATO>46248501000</OKATO>
                                                                    </Address>
                                                                  </AddressData>
                                                                </data>
                                                                <data>
                                                                  <personDatum>
                                                                    <FIO>
                                                                      <Surname>Коняв</Surname>
                                                                    </FIO>
                                                                  </personDatum>
                                                                </data>
                                                              </ns2:exitRestrictionActRequest>
                                                            </dx:Documents>
                                                          </dx:DXPack>
                                                        </dx:DXBox>
                                                      </smev:AppData>
                                                    </smev:MessageData>
                                                  </ws:DXSmev>
                                                </S:Body>
                                              </S:Envelope>
                                              


                                              Я насчитал 13 уровней. И это не самый заковыристый вариант (в нем только три конверта — SOAP, СМЭВ, ведомство, в реальности я видел ситуации, когда внутри конверта ведомства лежал следующий конверт с семантикой «пакет», а еще дальше — отдельный документ со структурой). И да, в СМЭВ 3 будет веселее, потому что там другие требования к структуре объектов.

                                              Для пущего развлечения хочу заметить, что в этой же структуре легко могут быть бинарные элементы объемом до 5 (а иногда и больше) Мб.
                                                +4
                                                Хороший пример, имеет смысл, спасибо!
                                                (Есть некоторые «стилистичекие» замечания к авторам _такого_ формата, но раз уж выросло такое...)

                                                При десериализации такого в объекты я бы в нескольких местах пропускал лишние промежуточные конверты (например, smev:MessageData/smev:AppData, или AddressData/Address), но наверное я чего-то не понимаю, и они там действительно нужны.

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

                                                P.S.
                                                И да, для больших BLOB-ов всегда и везде придется разбивать блоки на подузлы. В этом нет ничего специального — везде есть верхний предел блока базы, или страницы в хранилище, или ограничения по длине примитива, которые приходится обходить разбивая на куски, например, по 32КБ.

                                                Трюк, при работе с базами данных, минимизировать количество обращений к диску, при заданных параметрах файловой системы и дискового хранилища. Понятно, что ни в какой ОС, нет возможности прочитать 32МБ BLOB за одно обращение к IO. И, понятно, что на практике придется читать блоками определенного размера (приближенными, например, к размеру блока файловой-системы). Так вот, при большом количестве возможных вариантов оказывается что хранение BLOB-ов кусками по 32КБ вполне хороший вариант. :)
                                                  +2
                                                  (Есть некоторые «стилистичекие» замечания к авторам _такого_ формата, но раз уж выросло такое...)

                                                  Это бесполезно. Спорить с госуровнем — гиблое дело. Я пробовал, не помогло.

                                                  При десериализации такого в объекты я бы в нескольких местах пропускал лишние промежуточные конверты (например, smev:MessageData/smev:AppData, или AddressData/Address), но наверное я чего-то не понимаю, и они там действительно нужны.

                                                  Иногда нужны, если вам нужен полный аудит.

                                                  В-любом случае, я пока не вижу как, даже при таком адовом дизайне схемы, мы упрёмся в 32 вложенных подындекса.

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

                                                  И да, для больших BLOB-ов всегда и везде придется разбивать блоки на подузлы.

                                                  Ключевой вопрос — кому придется. Лично я предпочитаю, чтобы это за меня делала БД (включая оптимизацию этого доступа и прочее счастье). То есть я понимаю, что у БД есть страницы, а у хранилища ниже уровнем — блоки, но можно я об этом не буду думать?
                                                    +1
                                                    То есть я понимаю, что у БД есть страницы, а у хранилища ниже уровнем — блоки, но можно я об этом не буду думать?

                                                    Так в Cache' и не надо об этом думать: поточные поля (символьные или бинарные) автоматически при сохранении бьются на куски.

                                                    Но в этом случае мы уже оперируем не чистым иерархическим хранилищем/глобалами, описанными автором, а пользуемся надстройкой над ними в виде объектов/классов, предоставленными Cache', но в основе своей использующими те же самые глобалы.
                                                      0
                                                      Автор-то про глобалы и их применимость рассказывает, понятно, что поверх можно фигачить разные абстракции.
                                                    0
                                                    В-любом случае, я пока не вижу как, даже при таком адовом дизайне схемы, мы упрёмся в 32 вложенных подындекса. Все же хотелось бы примера из жизни, который имел бы смысл.

                                                    На самом деле, в xml мы заодно влетаем в соседнее ограничение: максимальная длина ключа в 511 байт. Предположим, что мы добрые, и у нас однобайтовая кодировка (на самом деле, конечно, нет; ненавижу тебя, госуха) — это все равно 511 символов, а по-хорошему, имена надо хранить квалифицированными (я подозреваю, что если пример выше квалифицировать — то он уже вылетит за длину).
                                                      +3
                                                      Со СМЭВом этим вы заинтриговали, коллега, который этим занимается сейчас в отпуске, но у меня было впечатление что эта адовая XML схема вообще без проблем использовалась ими в их инсталляциях HealthShare/Ensemble в России. Вернется из отпуска — посмотрю на то как это проецируется в Cache'. Интересно.
                                                        0
                                                        А у некоторых БД на глобалах — 1019 байт. У GT.M.

                                                        Чтобы упереться даже в 511, нужно чтобы каждый индекс глобала был в среднем больше 16.5 байт.

                                                        Глобалы — это специфический инструмент, весьма универсальный, но со своими ограничениями.
                                                          +1
                                                          Угу. Вот и я про то, что специфический.
                                                  0
                                                  Вообще я не говорил, что глобалы самый лучший способ хранения XML. Это один из способов, когда приемлемая вложенность и нужен быстрый доступ к узлам.
                                                    0
                                                    Вы говорили, что xml легко хранится в глобалах. Легко-то легко, только есть ограничения. Специализированные хранилища справляются с этим сущееественно лучше.
                                                      0
                                                      Что же это вы исключений не обозначили? Вы утверждаете что специализированные хранилища справляются с этим существенно лучше в абсолютно всех случаях?
                                                        0
                                                        Нет, зачем же во всех? В общих.
                                                          0
                                                          Вообще расплывчатое определение. Общий случай.
                                                          Что это такое общий случай?

                                                          Если нужно хранить документы целиком, то я не удивлюсь, если кто-нибудь выберет глобалы из-за их скорости.

                                                          А если нужно менять/добавлять/удалять отдельные узлы в документах, то глобалы прямо созданы для этого.

                                                          Есть бесконечное множество сценариев использования иерархических документов. И для приличной части этого бесконечного количества могут подойти глобалы. Всё зависит от задачи.
                                                            0
                                                            Если нужно хранить документы целиком, то я не удивлюсь, если кто-нибудь выберет глобалы из-за их скорости.

                                                            … ограничение в 3.6Мб.

                                                            А если нужно менять/добавлять/удалять отдельные узлы в документах, то глобалы прямо созданы для этого.

                                                            … ограничение по глубине вложенности.

                                                            Нет, несомненно, глобалы могут подойти. Просто каждый раз есть вопрос — а нет ли чего-то, что подойдет лучше?
                                                              0
                                                              Ограничения есть у всех баз. Если хранить документы целиком, то 16МБ, на весь документ, очень жёсткое ограничение.
                                                              А если нужно декомпозировать на иерархическую структуру с быстрым доступом к свойствам, то 3.6Мб на отдельное свойство не такое и большое ограничение.

                                                              Технологию подбирают подзадачу. Я постарался систематизировать сильные стороны глобалов.
                                                                +1
                                                                Если хранить документы целиком, то 16МБ, на весь документ, очень жёсткое ограничение.

                                                                Ну так возьмите БД, в которой нет такого ограничения. Это не обязательно глобалы.

                                                                А если нужно декомпозировать на иерархическую структуру с быстрым доступом к свойствам, то 3.6Мб на отдельное свойство не такое и большое ограничение.

                                                                … пока мы не пытаемся хранить в свойствах файлы.

                                                                Я постарался систематизировать сильные стороны глобалов.

                                                                Ну вот XML, файлы и индексы — как-то не сильная сторона оказалась.
                                                                  0
                                                                  Сильная, только для специфических случаев. Каких — уже говорил.

                                                                  Специализированная БД для файлов — это файловая система.
                                                                    0
                                                                    Если хранить документы целиком, то 16МБ, на весь документ, очень жёсткое ограничение.

                                                                    Ну так возьмите БД, в которой нет такого ограничения. Это не обязательно глобалы.


                                                                    16МБ — это у Mongo. У подобных Mongo баз есть ограничения на объём документа, весьма жёсткие. И на число уровней (у Mongo — 100). Так как иначе парсер (при доступе к конкретным свойствам) будет вешать сервер.
                                                                      0
                                                                      У подобных Mongo баз есть ограничения на объём документа, весьма жёсткие.

                                                                      Ага. Вот например у RavenDB нет (точнее, есть — около пары гигабайт).

                                                                      Конечно, можно сказать, что равен не подобен монге (и в чем-то это правда даже), но равен — честная ДОДБ.
                                                                        0
                                                                        Сказки, у монго доступ к конкретным свойствам очень быстрый. И между 16Мб и 3,6 Мб и 100 и 31 уровней вложенности — четыре большие разницы.
                                                                          0
                                                                          Сказки, у монго доступ к конкретным свойствам очень быстрый.


                                                                          По сравнению с чем? Сколько выборок в секунду ты можешь сделать из разных документов объёмом 3МБ с 15-го уровня вложенности?

                                                                          Тут есть чёткие дихотомии.

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

                                                                          0) Документы больше 16МБ
                                                                          — но свойства меньше 3.6 — глобалы
                                                                          — свойства не укладываются в 16МБ — не то, ни другое (если не использовать разрезание документов на части)
                                                                          2) Документы целиком больше 3.6 и меньше 16 — Mongo
                                                                          3) Меньше 3.6МБ и больше 100 уровней вложенности — Mongo
                                                                          4) Меньше 3.6МБ и менее 31 уровня вложенности — глобалы
                                                                          5) Документы около 1К — на вкус и цвет товарищей нет
                                                                            0
                                                                            Вы так говорите, как будто кроме монги и глобалов жизни не существует.

                                                                            Не говоря уже о том, что задача («выбор иерархической документно-ориентированной базы с возможностью быстрого доступа к свойствам») изначально неверна, плясать надо от сценариев использования. Ну и да, polyglot persistence рулит.
                                                                              –1
                                                                              vedenin1980 — специалист по Mongo, я пишу про глобалы, и с ним мы обсуждали именно Mongo vs глобалы.

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

                                                                                (а вообще, конечно, диалоги — это в личной переписке, а здесь публичное обсуждение, все-таки)
                                                                  0
                                                                  Скажите, а для документа выше как надо поступить?
                                                                  Какая структура для его компонентного хранения?

                                                                  Мне как не специалисту кажется, что требуется аналог Parent_ID и/или Child_ID а не пытаться уровни иерархии документа вписать в уровни иерархии индексов.
                                                                    0
                                                                    Вообще, как я понял, приведённый документ, это некое сообщение. То есть вещь, которую нужно доставить из А в Б.

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

                                                        Максимальная вложенность исходного кода этой страницы как раз 20. Она монстр?
                                                          0
                                                          А есть хоть какой-то смысл хранить HTML этой страницы в БД с обеспечением быстрого доступа к каждому узлу?

                                                          Подобные документы достаются из БД целиком.

                                                          А вот например в XML медкарты добавить новый узел со сканом МРТ будет удобно.
                                                            0
                                                            А есть хоть какой-то смысл хранить HTML этой страницы в БД с обеспечением быстрого доступа к каждому узлу?


                                                            Самое очевидное — если её надо сформировать.
                                                              0
                                                              Формировать страницу через то, что записать всё по тэгам в иерархическую БД?
                                                              Это будет медленнее, чем формировать напрямую в памяти.

                                                              Использовать глобалы как кеш? В этом случае имеет смысл хранить куски документа целиком без разбиения по свойствам.
                                                                0
                                                                Прямо на М и формировать. Данные в глобалах, мета-данные тоже, пусть М программы и формируют страничку.
                                                                Можно использовать глобалы во время отладки.
                                                                А в продакшене — локальные переменные.
                                                                  0
                                                                  Как я понимаю, на Caché Server Pages так и делают.

                                                                  Вообще я удивлён, что M в своё время не захватил нишу веб-разработки как PHP. Языки-то сильно похожие.
                                                              0
                                                              Ага, скан у вас помещается в 3.6Мб? Мне когда последний раз выдавали на руки сканы (не МРТ правда, а рентгена) — они как раз заняли половину CD-болванки (какой-то чертов пропьетарный формат для медицинского ПО). Да что там, у меня в 3.6 мегабайта фотографии с моего фотоаппарата не помещаются.
                                                                0
                                                                Болванка МРТ состоит из множества файлов в формате DICOM. Как кино из отдельных кадров.
                                                                Я открыл свою МРТ — каждый кадр это 586КБ.

                                                                Тем более, я не уверен, что есть смысл в медкарте хранить все эти файлы. Достаточно выбрать самый характерный.
                                                                Но таких отдельных кадров, сканов анализов может быть очень много.
                                                                  +1
                                                                  К сожалению, то, что отдельный скан может быть больше 3.6Мб — это такая реальность, данная нам в ощущениях. Не МРТ — так рентген, не рентген — так еще что-то.

                                                                  Нет, я понимаю, что объяснить заказчику можно любое ограничение (чего уж скрывать, все мы так рано или поздно делали), но на этом фоне придирки к аналогичным ограничениям у монги выглядят мелко.
                                                                    0
                                                                    Поэтому эти придирки и в спойлере. И там жёстко обозначена область, в которой глобалы подходят лучше.
                                                                    Структуры хранения в Mongo и глобалы логически идеентичны. Различия только в реализациях и во встроенных языках.

                                                                    Если интересно, то Mongo хочет влезть на этот рынок и готовит презентации, где говорит, что Mongo — это почти глобалы)
                                                                      0
                                                                      И там жёстко обозначена область, в которой глобалы подходят лучше.

                                                                      Угу. Та, где вам, на самом деле, не нужно хранилище документов, а нужно хранилище свойств. Дальше вопрос к сценариям использования и архитектуре приложения.
                                                                        0
                                                                        Документы состоят из свойств. Глобалы дают возможность красиво и быстро декомпозировать документы по свойствам.
                                                                        Это и есть ключевое преимущество перед БД, которые хранят целиком.

                                                                        Глобалы — это одна из самых первых документно-ориентированных баз, если не первая вообще (66г!).
                                                                          0
                                                                          Документы состоят из свойств.

                                                                          Это неоднозначный вопрос, сводящийся к тому, состоит ли целое из частей. Смысл ДОДБ, обычно, все-таки в том, чтобы работать с документом, как с целым (я приводил сценарий использования, он не единственный).
                                                                            0
                                                                            В любом случае: глобалы позволяют как хранить документ целиком, так и разбивая на части. С известными ограничениями.
                                                                            По логической структуре глобалы — это надмножество над документно-ориентированными базами, которые хранят документы целиком.
                                                                              0
                                                                              По логической структуре глобалы — это надмножество над документно-ориентированными базами, которые хранят документы целиком.

                                                                              Не-а. Глобалы — надмножество над key-value, потому что глобалы не умеют анализ содержимого документа (а именно оно специфично для документо-ориентированных хранилищ).
                                                                                +1
                                                                                А может Глобалы — key-value, где key может быть массивом?
                                                                                  0
                                                                                  Да. Почему нет. Следующая статья будет об этом.
                                                                                    0
                                                                                    Эта идея приходила мне в голову, но она, мне кажется, все-таки слишком упрощает ситуацию: групповые удаления, обходы — это все не ложится в семантику композитного ключа.
                                                                                      0
                                                                                      Согласен. Глобалы — иерархическое хранилище, с интерфейсом доступа к конкретным узлам как у многомерного массива. Я бы так определил.
                                                                                    0
                                                                                    Расскажите мне как анализирует, например, MongoDB документы.
                                                                                    Желательно как оно это делает само. Без запросов со стороны.

                                                                                    Так как к любой БД на глобалах также можно написать интерфейс для запросов. И код для анализа.
                                                                                      0
                                                                                      Ну как-как, вы скармливаете СУБД документ (кому что, в Mongo — JSON/BSON, RavenDB — .net-объект), та его сохраняет в понятном для себя виде, и в случае необходимости (например, при индексации или запросе) парсит содержимое.
                                                                    +1
                                                                    Это лишь простейший пример, на самом деле зачастую в энтерпрайзе вообще требуется бесконечное кол-во уровней иерархии, например вы хотите сделать сервис который строит дерево всех страниц любого сайта инета, доступных с главной страницы сайта, естественно вы должны считать что вложенность тут может быть бесконечной (ну или заведомо очень большой). И таких случаев в реальном энтерпрайзе довольно много.
                                                                      +2
                                                                      В данном случае Вы строите не дерево а _граф_
                                                                0
                                                                Как и первая статья — много маркетологической шелухи, никакой конкретики.
                                                                Тут что-то про скорость говорили… Так а где повторяемые и открытые тесты?
                                                                Где техническое описание, как же оно работает «под капотом»?
                                                                Напоминаю, что это хабр, а вовсе не гиктаймс или мегамозг.
                                                                  0
                                                                  Цель этой статьи — обозначить области применения глобалов, поскольку многие вообще не знают об этой технологии и о её сильных сторонах.
                                                                  На техническое описание формата хранения я дал ссылку в первых же комментах.

                                                                  По реализации — уже говорилось, что известные БД на глобалах работают на B*-tree, но в принципе могут работать и на других алгоритмах.

                                                                  Не все читатели тут являются разработчиками БД. Моя цель в этих статьях — показать для чего нужны глобалы, а не как конкретно они работают и какие оптимизации были использованы.
                                                                  +5
                                                                  Давайте, все же я попытаюсь вернуть дискуссию в правильное с методической точки зрения русло?

                                                                  Моя позиция такова — с логической точки зрения разницы многомерного хранилища (глобалов), документной базы (JSON/BSON) и XML иерархии нет. Они все оперируют эквивалентным множеством моделей и данных.
                                                                  (Что я попытался донести в своей презентации на RIT 2015 Cache'Conf —
                                                                  www.slideshare.net/TimurSafin/multimodel-database-cach )

                                                                  Интересна позиция Mongo в этом отношении osehra.org/sites/default/files/NoSQLtheModernMUMPS.pdf
                                                                  При разговоре с группой пользователей VistA они пытались донести до аудитории мысль, что Mongo/документные базы, это тоже самое, что и иерархические базы (MUMPS/Cache'). С чем я не могу не согласиться :)

                                                                  Еще один важный аспект, в большинстве случае движки СУБД оперируют вариациями b-tree формата (когда они не SSTable/LSM, конечно). И Cache' с его b*-tree тут не сильно отличается, хотя и отличается в деталях, облегчающих множество операций с глобалами, упомянутых Сергеем inetstar в этих двух статьях. Детали формата b*-tree мы попытались раскрыть в двух презентациях на недавнем RIT (моей ретроспективной, упомянутой выше, и Сергея Шутова про внутренности собственно Cache' b*-tree. Как оказалось его презентации в онлайне нет, как только исправим ситуацию сообщу в эту ветку)
                                                                    0
                                                                    — не тратя при этом лишнее дисковое пространство на пустые колонки, как было бы в реляционном случае.

                                                                    вы когда делаете такие заявления хотя бы подкрепляте фактами. Например: MSSQL 2014

                                                                    SELECT t.NAME AS TableName, p.rows AS RowCounts, SUM(a.used_pages) * 8 AS UsedSpaceKB, (SUM(a.total_pages) — SUM(a.used_pages)) * 8 AS UnusedSpaceKB
                                                                    FROM sys.tables t INNER JOIN sys.indexes i ON t.OBJECT_ID = i.object_id INNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
                                                                    INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id LEFT OUTER JOIN sys.schemas s ON t.schema_id = s.schema_id
                                                                    WHERE t.NAME LIKE 'slnx_job' AND t.is_ms_shipped = 0 AND i.OBJECT_ID > 255 GROUP BY t.Name, s.Name, p.Rows ORDER BY UsedSpaceKB DESC

                                                                    table | rows | used space | unused space
                                                                    slnx_job | 1626829 | 1019848 | 392

                                                                    Вставляю 10 колонок — размер не изменился.

                                                                    alter table slnx_job add c1 int
                                                                    alter table slnx_job add c2 int
                                                                    alter table slnx_job add c3 int
                                                                    alter table slnx_job add c4 int
                                                                    alter table slnx_job add c5 int
                                                                    alter table slnx_job add c6 int
                                                                    alter table slnx_job add c7 int
                                                                    alter table slnx_job add c8 int
                                                                    alter table slnx_job add c9 int

                                                                    Уверяю вас даже после добавления 10,000 колонок размер не поменяется. Реляционные базы не хранят пустые пространства.

                                                                    Насчет ХМЛ. Например DB2 хранит XML как внутри набор настояших реляционых таблиц со всей соотвествуюшей скоростью доступа.
                                                                      0
                                                                      Это потому что вы вставили колонки без значений по-умолчанию.
                                                                      Попробуйте задать такие значения.

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

                                                                      P.S. Я тестировал на MySQL 5.5.

                                                                      Ждёт результатов тестирований. Надеюсь, таблица slnx_job у вас большая.
                                                                        +1
                                                                        Это потому что вы вставили колонки без значений по-умолчанию.

                                                                        А зачем использовать значения по умолчанию в БД? Точнее, не так: значение по умолчанию — это не пустая колонка, это именно имеющее смысл значение (например, текущая дата). И неудивительно, что на него тратится место. А на несуществующие значения — не тратится (точнее, зависит от реализации).
                                                                          0
                                                                          А зачем использовать значения по умолчанию в БД?


                                                                          Во многих БД таблицы, где NULL не используются работают существенно быстрее.
                                                                            0
                                                                            Во многих БД таблицы, где NULL не используются работают существенно быстрее.

                                                                            У вас, конечно же, есть тесты, подтверждающие это заявление. Скажем, в MS SQL я такого никогда не наблюдал (и, собственно, не понимаю, откуда там этому логически взяться).
                                                                              –2
                                                                              For best performance, and to reduce potential code bugs, columns should ideally be set to NOT NULL. For example, use of the IS NULL keywords in the WHERE clause makes that portion of the query non-sargeble, which means that portion of the query cannot make good use an index.

                                                                              И подобных статей много. Ещё одна:

                                                                              Using NULL values has the following drawbacks:

                                                                              1) Using NULL values in the Database is always going to be overhead in terms of querying the tables’ columns containing the NULL values especially the larger tables. This is because, to query the column which has the NULL values from the table, index won’t be used as we need to query using IS NULL clause. As a result full table scan is performed.

                                                                              2) When NULL value is used as one of the value for the column, it takes up the whole length of the column size if the column is of fixed length. However, using variable length column does not have this impact but otherwise with fixed length columns, always to store NULL value it occupies the entire column length resulting in growing the Database size which is totally unnecessary.

                                                                              3) Using NULL values in the Columns of the tables lead to convoluted Transact SQL code resulting in code not running efficiently or error prone.

                                                                              Hence, considering above, always try to avoid using NULL values and instead one can use some constants to represent NULL values like N/A.
                                                                                +2
                                                                                Еще раз спрашиваю, тесты есть?

                                                                                По мелочам:

                                                                                1. В статье, на которую вы ссылаетесь первой ссылкой, нет приведенного вами текста («For best performance....»; собственно, в ней вообще нет слова «performance»)
                                                                                2. Что касается второй «статьи», то она говорит, что при поиске по NULL (обратите внимание, не просто по колонке, которая может содержать такие значения, а именно поиск строк содержащих такие значения) индексы не используются, т.е. стоимость такого поиска (и того такого, а не любого другого) эквивалентна полному перебору. Во-первых, она ничего не говорит о любых других операциях по такой таблице. Во-вторых, просветите меня, пожалуйста, какова алгоритмическая сложность поиска узла, не содержащего заданный подиндекс, в глобале? Т.е., как найти всех пользователей, у которых не задан город?
                                                                                  0
                                                                                  А в глобалах я легко могу сделать подобный индекс. В индексном глобале (и только в нём), например, в качестве NULL буду использовать "\0"

                                                                                  Set ^index_global("\0", id1) = 1
                                                                                  Set ^index_global("Moscow", id2) = 1
                                                                                    0
                                                                                    В RDBMS тоже можно сделать подобный индекс, технически ничего не мешает.

                                                                                    (кстати, а что вы будете делать, когда \0 — валидное значение?)
                                                                                      0
                                                                                      Собственно, постгре и индексирует: «Also, an IS NULL or IS NOT NULL condition on an index column can be used with a B-tree index.»
                                                                                        –1
                                                                                        Даже больше, есть мнение, что NULL не только не замедляют, но и ускоряют работу постгре.
                                                                                          +2
                                                                                          NOT NULL, в некоторых случаях, действительно ускоряет работу постгреса. Если в таблице в начале идут NOT NULL атрибуты фиксированной длины, то доступ к этим атрибутам осуществляется по предвычисленным значениям. Однако, почувствовать это преимущество на паре десятков атрибутов, вряд ли получится.

                                                                                          P.S. Умоляю вас, «постгрес» :)
                                                                                            0
                                                                                            Разговор был о MS SQL.
                                                                                            Условный индекс — это не то. Придётся для каждого столбца делать 2 индекса — по колонкам и условия. Это убьёт производительность. А в моём случае это будет один индексный глобал на одну колонку (set колонок).

                                                                                            Напоминаю исходную точку разговора: не рекомендуется вручную программировать таблицы на глобалах. Не к тому цепляетесь.
                                                                                              +2
                                                                                              Разговор был о MS SQL.

                                                                                              Разговор был о «во многих БД». Впрочем, вы и в MS SQL падение производительности не подтвердили.

                                                                                              Условный индекс — это не то.

                                                                                              А я и не про условные индексы.

                                                                                              Придётся для каждого столбца делать 2 индекса — по колонкам и условия. Это убьёт производительность.

                                                                                              С чего вы это взяли?

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

                                                                                              Я вообще не понимаю, зачем программировать таблицы на глобалах, когда есть нормальные табличные инструменты.
                                                                                                +1
                                                                                                Разговор был о MS SQL.

                                                                                                … который тоже поддерживает индексы с NULL.

                                                                                                SELECT [Id], [Text]
                                                                                                FROM [dbo].[NV]
                                                                                                WHERE Text IS NULL
                                                                                                
                                                                                                // Execution plan
                                                                                                Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV]), SEEK:([Speed].[dbo].[NV].[Text]=NULL) ORDERED FORWARD)
                                                                                                


                                                                                                Передаю пламенный привет (устаревшим) статьям, которые вы цитировали.
                                                                                                  0
                                                                                                  1) Вы не показали определение таблицы, в котором это поле указано как индексируемое

                                                                                                  2) Приведите ссылку на документацию

                                                                                                  3) И что это действительно не только для текстовых полей.

                                                                                                  4) Сделайте ещё поиск по NOT NULL по текстовому и не по текстовому полю

                                                                                                  5) Вы не сможете сделать строку из 10 000 столбцов, размером более 8КБ. Так что глобалы на разреженных данных MS SQL в общем случае заменить не сможет.
                                                                                                    +1
                                                                                                    Вы не показали определение таблицы, в котором это поле указано как индексируемое

                                                                                                    Вы серьезно считаете, что MS SQL способен использовать индекс, который не задан? Но мне не сложно.

                                                                                                    CREATE TABLE [dbo].[NV](
                                                                                                    	[Id] [uniqueidentifier] NOT NULL,
                                                                                                    	[Text] [nvarchar](50) NULL,
                                                                                                        CONSTRAINT [PK_NV] PRIMARY KEY CLUSTERED ([Id] ASC)
                                                                                                    ) ON [PRIMARY]
                                                                                                    GO
                                                                                                    
                                                                                                    CREATE NONCLUSTERED INDEX [IX_NV] ON [dbo].[NV] ([Text] ASC) ON [PRIMARY]
                                                                                                    GO
                                                                                                    


                                                                                                    Приведите ссылку на документацию

                                                                                                    msdn.microsoft.com/en-us/library/ms188783%28v=sql.110%29.aspx

                                                                                                    И что это действительно не только для текстовых полей.

                                                                                                    StmtText
                                                                                                    ---------------------------------------------------
                                                                                                    SELECT Id, Int
                                                                                                    FROM [dbo].[NV]
                                                                                                    WHERE Int IS NULL
                                                                                                    
                                                                                                    StmtText
                                                                                                    -------------------------------------------------------------------------------------------------------------
                                                                                                      |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_1]), SEEK:([Speed].[dbo].[NV].[Int]=NULL) ORDERED FORWARD)
                                                                                                    


                                                                                                    Вы не сможете сделать строку из 10 000 столбцов, размером более 8КБ. Так что глобалы на разреженных данных MS SQL в общем случае заменить не сможет.

                                                                                                    Чтобы сохранить разреженные данные, не обязательно пихать их в одну строчку одной таблицы. Поэтому как раз в общем случае — сможет.
                                                                                                      –1
                                                                                                      1) Вообще-то в документации ничего не говорится об ускорении выборок строк с NULL-значениями.

                                                                                                      2) И я просил привести примеры с NOT NULL ещё. Я-то легко на глобалах сделаю индексированную выборку по NOT NULL. О чём вы как-то забыли. Приводите.

                                                                                                      3) Таблица в общем случае никогда не была деревом. Сэмулировать дерево на таблицах можно, но это нерационально. В этом тоже в статье написано.
                                                                                                        0
                                                                                                        Вообще-то в документации ничего не говорится об ускорении выборок строк с NULL-значениями.

                                                                                                        А должно? Но вот вам: msdn.microsoft.com/en-us/library/ms190457%28v=sql.110%29.aspx

                                                                                                        An index is an on-disk structure associated with a table or view that speeds retrieval of rows from the table or view. An index contains keys built from one or more columns in the table or view. These keys are stored in a structure (B-tree) that enables SQL Server to find the row or rows associated with the key values quickly and efficiently.


                                                                                                        Никаких специальных оговорок про NULL нет.

                                                                                                        И я просил привести примеры с NOT NULL ещё. Я-то легко на глобалах сделаю индексированную выборку по NOT NULL. О чём вы как-то забыли. Приводите.

                                                                                                        А вы меньше редактируйте комментарий после отправки, меньше будет «забываться».

                                                                                                        CREATE TABLE [dbo].[NV](
                                                                                                        	[Id] [uniqueidentifier] NOT NULL,
                                                                                                        	[TextNull] [nvarchar](50) NULL,
                                                                                                        	[IntNull] [int] NULL,
                                                                                                        	[TextNotNull] [nvarchar](50) NOT NULL,
                                                                                                        	[IntNotNull] [int] NOT NULL
                                                                                                        )
                                                                                                        
                                                                                                        SELECT Id FROM NV WHERE TextNull IS NULL
                                                                                                          |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_4]), SEEK:([Speed].[dbo].[NV].[TextNull]=NULL) ORDERED FORWARD)
                                                                                                        
                                                                                                        SELECT Id FROM NV WHERE TextNull IS NOT NULL
                                                                                                          |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_4]), SEEK:([Speed].[dbo].[NV].[TextNull] IsNotNull) ORDERED FORWARD)
                                                                                                        
                                                                                                        SELECT Id FROM NV WHERE TextNull = '123'
                                                                                                          |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_4]), SEEK:([Speed].[dbo].[NV].[TextNull]=CONVERT_IMPLICIT(nvarchar(4000),[@1],0)) ORDERED FORWARD)
                                                                                                        
                                                                                                        SELECT Id FROM NV WHERE IntNull IS NULL
                                                                                                          |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_1]), SEEK:([Speed].[dbo].[NV].[IntNull]=NULL) ORDERED FORWARD)
                                                                                                        
                                                                                                        SELECT Id FROM NV WHERE IntNull IS NOT NULL
                                                                                                          |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_1]), SEEK:([Speed].[dbo].[NV].[IntNull] IsNotNull) ORDERED FORWARD)
                                                                                                        
                                                                                                        SELECT Id FROM NV WHERE IntNull = 1
                                                                                                          |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_1]), SEEK:([Speed].[dbo].[NV].[IntNull]=CONVERT_IMPLICIT(int,[@1],0)) ORDERED FORWARD)
                                                                                                        
                                                                                                        SELECT Id FROM NV WHERE TextNotNull = '123'
                                                                                                          |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_3]), SEEK:([Speed].[dbo].[NV].[TextNotNull]=CONVERT_IMPLICIT(nvarchar(4000),[@1],0)) ORDERED FORWARD)
                                                                                                        
                                                                                                        SELECT Id FROM NV WHERE IntNotNull = 1
                                                                                                          |--Index Seek(OBJECT:([Speed].[dbo].[NV].[IX_NV_2]), SEEK:([Speed].[dbo].[NV].[IntNotNull]=CONVERT_IMPLICIT(int,[@1],0)) ORDERED FORWARD)
                                                                                                        


                                                                                                        Таблица в общем случае никогда не была деревом. Сэмулировать дерево на таблицах можно, но это нерационально.

                                                                                                        Не более нерационально, чем имитировать таблицы на дереве. Но речь-то не об этом, речь о том, чтобы сохранить конкретные данные в реляционной структуре, а это совсем не то же самое, что «сэмулировать дерево».
                                                                                                      –1
                                                                                                      1. Для это есть специальный индексный сервер.

                                                                                                      2.https://msdn.microsoft.com/en-us/library/ms142571.aspx

                                                                                                      3.
                                                                                                      create table AAA
                                                                                                      (
                                                                                                      id int not null primary key,
                                                                                                      c1 int null index c1_index,
                                                                                                      c2 float null index c2_index
                                                                                                      )

                                                                                                      5)идея хранить дерево всего в одной таблице — неверная с архитектурной точки зрения вреляционых базах и мне видится неверной точка зрения что нельзя сохранить медицинскую историю человека в реляционной структуре.

                                                                                                      Число людей, болезней, симптомов, процедур, снимков, мнений, да чего угодно настолько конечно, что даже SAP с 36000 таблиц кажется монстром, по сравнению базой хранения всего типичного госпиталя, которую вы считаете нельзя положить в реляционную таблицу.

                                                                                                      Watson использует DB2 и им в голову не пришло использовать нереляционную базу. Вы же не будете отрицать что ничего близко похожего на ваших глобалах не сделано.

                                                                                                      Если вам нужно более чем 10000 или миллион колонок — это не проблема сделать нужное количество таблиц с отношением 1:1
                                                                                                        0
                                                                                                        1. Для это есть специальный индексный сервер.


                                                                                                        Я просто попросил показать определение таблицы

                                                                                                        2) Приведите ссылку на документацию
                                                                                                        2.https://msdn.microsoft.com/en-us/library/ms142571.aspx


                                                                                                        Требовалась ссылка на документацию, в которой было бы явно написано, что поля с NULL-значениями индексируются.

                                                                                                        5)идея хранить дерево всего в одной таблице — неверная с архитектурной точки зрения в реляционых базах и мне видится неверной точка зрения что нельзя сохранить медицинскую историю человека в реляционной структуре.


                                                                                                        Можно использовать несколько таблиц. И это всё равно будет ошибка с точки зрения архитектуры.

                                                                                                        Watson использует DB2
                                                                                                        достоверной информации о том, что Watson использует DB2 как единственное хранилище не нашёл. Однако у IBM есть технология аналогичная глобалам.

                                                                                                        Самая большая медицинская система с самым большим числом пользователей VistA, что говорит в пользу глобалов и об их подходящести для данной сферы. Собственно говоря, глобалы именно для этой сферы и придумали.
                                                                                            0
                                                                                            Статья откуда цитата про «best perfomance»
                                                                                            Нам лучше вернуться к теме. Так как больше 1024 колонок вы не сделаете в MS SQL. А в спец. типе таблиц wide строка не более 8КБ.
                                                                                              +1
                                                                                              Статья откуда цитата про «best perfomance»

                                                                                              Это ровно тот же аргумент, что уже обсуждался — про индексы и поиск с IS NULL. Не интересно по второму разу обсуждать.

                                                                                              Так как больше 1024 колонок вы не сделаете в MS SQL.

                                                                                              А надо?

                                                                                              А в спец. типе таблиц wide строка не более 8КБ.

                                                                                              Я вам честно скажу, это ограничение есть во всех таблицах MS SQL, не только в wide. Правда, начиная с 2008-го, есть row overflow для строковых и бинарних типов, но он подтормаживает.
                                                                                                0
                                                                                                Так как больше 1024 колонок вы не сделаете в MS SQL.

                                                                                                А надо?


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

                                                                                                Человек мне сказал, что разреженные столбцы не занимают место и похвастался, что мог бы вставить 10 000 колонок в MS SQL. Так вот. Не смог бы. А если и смог бы, то там свои ограничения.

                                                                                                8КБ на строку с пациентом — это абсолютно нереально. И там ещё нельзя по одной новые колонки добавлять в этом типе таблиц.
                                                                                                  +1
                                                                                                  Во-первых, для медкарт лучше ДОДБ. Но, во-вторых, если бы заказчик настаивал на RDBMS, то немного разумной декомпозиции — и все будет хорошо.

                                                                                                  Я ни разу в жизни не видел ни одного объекта, у которого бы реально было хотя бы несколько сотен индивидуальных, недекомпонуемых на группы свойств. Человеческий мозг так не работает, ему некомфортно в таких моделях.

                                                                                                    0
                                                                                                    Во-первых, для медкарт лучше ДОДБ


                                                                                                    Базы на глобалах — это и есть ДОБД.

                                                                                                    Я ни разу в жизни не видел ни одного объекта


                                                                                                    А вот я видел. Даже 40 свойств с десятками подсвойств заставляют разработчиков уже сходить с ума.
                                                                                                    Я встречал это в базе, где хранилась персональная информация о людям.

                                                                                                    Пришлось использовать EAV — я тогда ещё не знал о глобалах.
                                                                                                      0
                                                                                                      Базы на глобалах — это и есть ДОБД.

                                                                                                      Это не единственные существующие ДОДБ. Более того, я уже писал, почему я не считаю глобалы ДОДБ.

                                                                                                      А вот я видел. Даже 40 свойств с десятками подсвойств заставляют разработчиков уже сходить с ума.

                                                                                                      Извините, но это не «сотни свойств», это именно «40 свойств с десятками подсвойств». У вас уже есть декомпозиция, но вы ее почему-то не видите.
                                                                                                        0
                                                                                                        почему я не считаю глобалы ДОБД


                                                                                                        Вы просто не разбираетесь в ДОБД.
                                                                                                        Почитайте википедию.
                                                                                                        Там есть MUMPS.

                                                                                                        У вас уже есть декомпозиция, но вы ее почему-то не видите.

                                                                                                        Я вижу многоуровневое дерево. Ясно и отчётчиво.

                                                                                                        На таблицах это либо EOV или 160 таблиц (с декомпозицией). А на глобалах — одно красивое дерево.
                                                                                                          +3
                                                                                                          Вы просто не разбираетесь в ДОБД. Почитайте википедию.

                                                                                                          Давайте не будем доставать и меряться, кто в чем разбирается, хорошо?

                                                                                                          Я вижу многоуровневое дерево. Ясно и отчётчиво.

                                                                                                          ...eye of the beholder. Вы привыкли так декомпоновать системы, потому что вы привыкли так работать с глобалами. Это не значит, что это единственная возможная декомпозиция этой системы.

                                                                                                          На таблицах это либо EOV или 160 таблиц (с декомпозицией).

                                                                                                          Откуда взялась цифра «160»? Вы посчитали сущности в E-R?
                                                                                                            –1
                                                                                                            Вы просто не разбираетесь в ДОБД. Почитайте википедию.

                                                                                                            Давайте не будем доставать и меряться, кто в чем разбирается, хорошо?


                                                                                                            Более скромно было сказать: Извиняюсь, вы правы.

                                                                                                            Википедия — очень хороший аргумент. В мою пользу. Там за одну фразу может многомесячная борьба идти.
                                                                                                            Вы сами себе выдумали определение ДОБД и решили, что оно самое лучшее. Так что ваше определение ДОБД — субъективное, а не то, которое реально используется в мире. А MUMPS — это ДОБД.

                                                                                                            Откуда взялась цифра «160»?


                                                                                                            Это примерная цифра. Слава богу, хватило ума, не делать полную нормализацию, а использовать EAV.

                                                                                                            40 необязательных свойств, у которых у каждого может быть начиная от 4-х вложенных свойств. У которых также могут быть вложенные свойства. Все эти свойства разнотипны, а не просто текстовые поля определённой длины.

                                                                                                            Так что при полной нормализации без EAV будет, как минимум, 1 таблица 40 колонками для свойств + 160 таблиц связей с подсвойствами + 4*40 таблиц подсвойствами = 321 даже. И это только при двухкратном уровне вложенности.

                                                                                                            А на EAV 4 таблицы (из-за неограниченных вложенностей, а так вообще 3).

                                                                                                            Но меня всё время не оставляла мысль, что это решение (EAV) не идеальное. Когда я узнал о глобалах, то понял то, что они то что нужно.