Новое в Caché 2015.1: SQL-индекс по элементам свойства-массива

После заполнения (do ##class (User.eav).RepopulateAll ()) в наших таблицах окажутся следующие данные:

Таблица eav:

Class User.eav Extends %Persistent{Index idx1 On attributes (ELEMENTS) [ Data = entity ];

Index idx2 On (attributes (KEYS),  attributes (ELEMENTS)) [ Data = entity ];

Property entity;

Property attributes As array Of %String (SQLTABLENAME = «attributes») [ SqlFieldName = attr ];

/// do ##class (User.eav).RepopulateAll ()ClassMethod RepopulateAll (){  do …%DeleteExtent ()    set name=$TR («Сосна^ сиби^рская кедро^вая»,»^»,$c (769))    set obj=…%New ()  set obj.entity=«Человек»  do obj.attributes.SetAt (22, «Возраст»)  do obj.attributes.SetAt (186, «Рост»)  do obj.attributes.SetAt («Вася», «Имя»)  do obj.%Save ()

  set obj=…%New ()  set obj.entity=«Дерево»  do obj.attributes.SetAt (186, «Возраст»)  do obj.attributes.SetAt (22, «Высота»)  do obj.attributes.SetAt («Сосновые», «Семейство»)  do obj.attributes.SetAt (name, «Имя»)  do obj.%Save ()    /*    ; или &sql (insert into eav (entity) select 'Человек' union select 'Дерево')  &sql (insert into attributes (eav, element_key, attr)  select 1,'Возраст',22 union  select 1,'Рост',186 union  select 1,'Имя','Вася' union  select 2,'Возраст',186 union  select 2,'Высота',22 union  select 2,'Семейство','Сосновые' union  select 2,'Имя',: name)  */    do …Reindex ()}

/// do ##class (User.eav).Reindex ()ClassMethod Reindex (){  do …%BuildIndices (,1)

  do $system.SQL.TuneTable («SQLUser.eav»,1)  do $system.SQL.TuneTable («SQLUser.attributes»,1)  do $system.OBJ.Compile ($classname (), «cu»)}

}

ID entity 1 Человек 2 Дерево Таблица attributes: eav ID attr element_key 1 1||Возраст 22 Возраст 1 1||Имя Вася Имя 1 1||Рост 186 Рост 2 2||Возраст 186 Возраст 2 2||Высота 22 Высота 2 2||Имя Сосна́ сиби́рская кедро́вая Имя 2 2||Семейство Сосновые Семейство Глобал с данными:

USER>zw ^User.eavI ^User.eavI («idx1»,» 186»,1)=$lb (», «Человек») ^User.eavI («idx1»,» 186»,2)=$lb (», «Дерево») ^User.eavI («idx1»,» 22»,1)=$lb (», «Человек») ^User.eavI («idx1»,» 22»,2)=$lb (», «Дерево») ^User.eavI («idx1»,» ВАСЯ»,1)=$lb (», «Человек») ^User.eavI («idx1»,» СОСНА́ СИБИ́РСКАЯ КЕДРО́ВАЯ»,2)=$lb (», «Дерево») ^User.eavI («idx1»,» СОСНОВЫЕ»,2)=$lb (», «Дерево») ^User.eavI («idx2», «Возраст»,» 186»,2)=$lb (», «Дерево») ^User.eavI («idx2», «Возраст»,» 22»,1)=$lb (», «Человек») ^User.eavI («idx2», «Высота»,» 22»,2)=$lb (», «Дерево») ^User.eavI («idx2», «Имя»,» ВАСЯ»,1)=$lb (», «Человек») ^User.eavI («idx2», «Имя»,» СОСНА́ СИБИ́РСКАЯ КЕДРО́ВАЯ»,2)=$lb (», «Дерево») ^User.eavI («idx2», «Рост»,» 186»,1)=$lb (», «Человек») ^User.eavI («idx2», «Семейство»,» СОСНОВЫЕ»,2)=$lb (», «Дерево») Глобал с индексами:

USER>zw ^User.eavI ^User.eavI («idx1»,» 186»,1)=$lb (», «Человек») ^User.eavI («idx1»,» 186»,2)=$lb (», «Дерево») ^User.eavI («idx1»,» 22»,1)=$lb (», «Человек») ^User.eavI («idx1»,» 22»,2)=$lb (», «Дерево») ^User.eavI («idx1»,» ВАСЯ»,1)=$lb (», «Человек») ^User.eavI («idx1»,» СОСНА́ СИБИ́РСКАЯ КЕДРО́ВАЯ»,2)=$lb (», «Дерево») ^User.eavI («idx1»,» СОСНОВЫЕ»,2)=$lb (», «Дерево») ^User.eavI («idx2», «Возраст»,» 186»,2)=$lb (», «Дерево») ^User.eavI («idx2», «Возраст»,» 22»,1)=$lb (», «Человек») ^User.eavI («idx2», «Высота»,» 22»,2)=$lb (», «Дерево») ^User.eavI («idx2», «Имя»,» ВАСЯ»,1)=$lb (», «Человек») ^User.eavI («idx2», «Имя»,» СОСНА́ СИБИ́РСКАЯ КЕДРО́ВАЯ»,2)=$lb (», «Дерево») ^User.eavI («idx2», «Рост»,» 186»,1)=$lb (», «Человек») ^User.eavI («idx2», «Семейство»,» СОСНОВЫЕ»,2)=$lb (», «Дерево») Теперь выполним следующий запрос:

select entity from eav where attributes→attr = 22

entity Человек Дерево Запрос отрабатывает, но использует полное сканирование, а не наши индексы. Если посмотреть в SMP (Портал управления системой) на наши таблицы, то мы в них не найдём idx1 и idx2, хотя как мы ранее видели, данные в них сгенерированы.

Это происходит потому, что SQL-ядро «видит» только те индексы по коллекциям-массивам, которые базируются исключительно на полях подтаблицы-массива и обязательно содержат ключ, т.е. propArray (KEY). Оба наших индекса содержат поле entity, которое отсутствует в подтаблице attributes.

Также не будет виден индекс Index idx3 On attributes (ELEMENTS); поскольку он не содержит attributes (KEYS), а вот индексы:

Index idx4 On (attributes (KEYS), attributes (ELEMENTS)); Index idx5 On (attributes (ELEMENTS), attributes (KEYS)); будут видны и следовательно будут учитываться при запросах, но не для всех типов запросов они оптимальны.Так как же минимальными усилиями добиться видимости индексов на элементы коллекции-массива SQL-ядром? В версии Caché 2015.1 появилась возможность проецировать коллекцию как поле таблицы, если эта коллекция проецируется в подтаблицу, используя методы SetCollectionProjection/GetCollectionProjection.По умолчанию эта возможность выключена.

В предыдущих версиях данных методов нет, но Вы можете попробовать включить эту фичу вручную:

%SYS>set ^%SYS («sql», «sys», «collection projection»)=1 После изменения обязательно необходимо перекомпилировать классы.Итак, включим этот параметр и посмотрим, что это нам дало.

В SMP теперь стали видны наши индексы, а в таблице eav появилось скрытое поле-коллекция attr. Тем не менее наш запрос по прежнему не видит индексы idx1/idx2.

Для исправления ситуации воспользуемся уже известным предикатом FOR SOME %ELEMENT:

select entity from eav where for some %element (attr) (%value = 22)

entity Человек Дерево Теперь в запросе задействован индекс idx1. Немного его видоизменим:

select entity from eav where for some %element (attr) (%value = 22 and %key= 'Возраст')

entity Человек select entity from eav where for some %element (attr) (%value = 22 and %key= 'Высота')

entity Дерево В последних двух примерах уже будет задействован индекс idx2 вместо idx1.

© Habrahabr.ru