powered by simpleCommunicator - 2.0.59     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Издеваясь над птицей - странный эффект с индексом по computed-by полю
15 сообщений из 15, страница 1 из 1
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38817318
Arioch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Досталась в наследство несколько выподвывернутая таблица.

Ради удобства в некоторых запросах добавил пару computed-by поля.
Рекурсивных запросов нет, так что максимум рекурсии должен быть 1 уровень вложенности.
По структуре ОКВЭДов, даже если сделать полную рекурсию, то максимум должен быть 9-я вложенность. Но такого нет.

ID - просто PK: 1,2,3,4,...
Пример: CODE = '01.13.24' // PARENT_CODE = '01.13.2'
Остальные поля не важны.
ODS 11.1 dialect 3; 1842 строки в таблице

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
CREATE TABLE OKVED (
    ID           T_IDLONG NOT NULL /* T_IDLONG = INTEGER */,
    ID_SECTION   T_IDLONG /* T_IDLONG = INTEGER */,
    CODE         T_TEXT_C008 /* T_TEXT_C008 = CHAR(8) */,
    TITLE        T_TEXT_V256 /* T_TEXT_V256 = VARCHAR(255) */,
    ID_OKONX     T_IDLONG /* T_IDLONG = INTEGER */,
    ID_CALCTYPE  T_IDLONG /* T_IDLONG = INTEGER */,
    PARENT_CODE  COMPUTED BY (
       Left( code, decode( CHAR_LENGTH(TRIM( TRAILING FROM code)), 8,7, 7,5, 5,4, 4,2))
    ),
    PARENT_ID    COMPUTED BY (
        (select u.id from okved u where u.code = okved.parent_code)
    )
);


/******************************************************************************/
/***                                Indices                                 ***/
/******************************************************************************/

CREATE UNIQUE INDEX OKVED_CODE_IDX ON OKVED (CODE);
CREATE INDEX XIF848OKVED ON OKVED (ID_SECTION);
CREATE INDEX XIF852OKVED ON OKVED (ID_OKONX);
CREATE INDEX XIF946OKVED ON OKVED (ID_CALCTYPE);
CREATE UNIQUE INDEX XPKOKVED ON OKVED (ID);



Всё считается и показывается правильно.

По мотивам http://www.sql.ru/forum/1128761/kodirovka-podklucheniya-raznyy-plan провел эксперимент с индексом по вычислимому столбцу.

Код: sql
1.
 create index test_okved on okved computed by ( parent_id ) 



По идее данный индекс мало на что влияет (если не указывать parent_id в сортировке или условиях).
IBExpert ему почему-то показал статистику = 1.

Далее, делаем запросы (все они без этого индекса прoходят без каких-либо проблем)

Код: sql
1.
 select first 10 id from okved 



Отрабатывает.

Код: sql
1.
 select first 10 id from okved order by id  


Код: sql
1.
 select first 10 id, parent_id from okved 


Код: sql
1.
 select id, parent_id from okved 



FB 2.5.3 Win64 - запрос не проходит, IBExpert дважды показывает диагностику о рекурсии по системным таблицам
FB 2.1.6 Win32 - запрос не проходит, IBExpert после долгого ожидания дважды показывает deadlock

Удаляем этот индекс - все запросы проходят в лёт.

При это я не вижу ни одной причины серверу вообще трогать новосозданный индекс по выражению-полю parent_id ни в одном из этих трёх запросов
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38817366
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Arioch,

не надо такие индексы создавать. Такое придумать можно только нарочно, чтобы сделать птице побольнее.
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38817374
Arioch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Симонов Денис,

я могy только повторить насчёт трусов и крестика:

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


И в любом случае, имеем какое-то совершенно иррациональное поведение оптимизатора. ХЗ где оно еще может вылезти. Два дедлока на prepare запроса...

PS: легендарная "база Болтика" тоже была примером "чтобы сделать птице побольнее."
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38817390
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Arioch,

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

И уж если о рациональности говорить, то надо вообще такие кривые индексы запрещать создавать. Вот только проверку всей возможной кривизны не так уж легко сделать.
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38817401
Arioch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Симонов Денис,

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

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

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

Как минимум этa грабля готовый пример для статьи "как поставить FB на колени" или любого другого списка gotcha
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38836503
MrCat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вот почитал вас, тут почитал, и назрел вопрос.

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

В случае, если в выражении индекса содержится запрос, даже опосредованно - через использование вычисляемого поля, оптимизатор дальше его не анализирует, формально создаёт индекс, но в потом этот индекс не перестраивает, и в запросах его не использует. Если в выражении индекса содержатся только обращения к текущим полям или скалярные функции (типа substring), оптимизатор расслабляется и нормально отстраивает и использует индекс.

Я прав, или нет?
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38836507
dimitr
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MrCat,

индекс всегда поддерживается ("перестраивается"). А будет ли он использоваться в запросах - зависит от кучи факторов, начиная с тех же запросов.
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38836508
Фотография kdv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Arioch,

Код: sql
1.
2.
    PARENT_ID    COMPUTED BY (
        (select u.id from okved u where u.code = okved.parent_code)


ох, ёжик...
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38836837
Ivan_Pisarevsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
AriochРади удобства в некоторых запросах добавил пару computed-by поля.Я тут выстрелил себе в ногу, теперь хромаю.
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38836876
Arioch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ух ты, празднуем возрождение темы?

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

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

kdv,
а сформулируй лучше в рамках DDL
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38837044
Ivan_Pisarevsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
некроманты, блин.
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38837417
MrCat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
dimitrMrCat,

индекс всегда поддерживается ("перестраивается"). А будет ли он использоваться в запросах - зависит от кучи факторов, начиная с тех же запросов.
Тогда приведу конкретику.

Создадим таблицу, в которой два поля - текстовое значение и некоторый ключ к нему. В моём случае понадобился первый непрерывный кусок состоящий из цифр. Разумеется, нет никаких проблем с тем, чтобы хранить ключ обычным физическим проиндексированным полем, и даже можно сэкономить - сделать float-ключ, а не строку. Мне стало интересно, как заработает другой вариант, с вычисляемым полем и индексом по нему:
фигура первая - неудачная
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
 
-- попробуем создать индекс по выражению, который включает в 
-- своё выражение вычисляемое поле. Не случайно индекс по вычисляемому
-- полю запрещён, но проверим лишний раз

-- таблица, которую мы будем мучать
create table t1(
  fld_Value varchar(50),
  fld_Key computed by((
    -- PNum - номер символа
    -- PChr, PNextChr - текущий и следующий за ним символ
    with recursive tmp(PNum, PChr, PNextChr) as (
        select
            1,
            substring(fld_Value from 1 for 1),
            substring(fld_Value from 2 for 1)
        from rdb$database

        union all

        select
            PNum + 1,
            substring(fld_Value from PNum + 1 for 1),
            substring(fld_Value from PNum + 2 for 1)
        from tmp
        where
            -- рекурсия не прерывается:
            -- * пока не достигли конца строки
            PNum < char_length(fld_Value) and
            -- * пока не попали на первый фронт цифра -> не-цифра
            (
                not (PChr between '0' and '9') or
                (PNextChr between '0' and '9')
            )
    ) -- /with recursive
    select cast(list(PChr, '') as varchar(30))
    from tmp
    where
        -- в итоговый лист попадают только цифры
        PChr between '0' and '9'
  )) -- /computed by
) -- /create table

-- собственно, индекс, который пробует обойти запрет
-- на индексацию вычисляемых полей "в лоб"
create index idx1 on t1
computed by ((fld_Key));

-- пушечное "свежее мя-я-яско!"
insert into t1(fld_Value)
values('ABCDE123344556667ILIKEIT');
insert into t1(fld_Value)
values('KARKNULVORONNEVERMORE123');
insert into t1(fld_Value)
values('OBEAFINEGIRLKISSME!');
commit; -- должна перестроить индекс

-- проверяем, что вычисляемое поле считается правильно:
select *
from t1;

/*
вернулось:
FLD_VALUE                               FLD_KEY
----------------------------------------------
ABCDE123344556667ILIKEIT        123344556667
KARKNULVORONNEVERMORE123    123
OBEAFINEGIRLKISSME!                null

ок, ключевое поле нормальное
*/

-- смотрим статистику индекса
select rdb$statistics
from rdb$indices
where rdb$index_name = 'IDX1';
-- вернула 1,00

-- попробуем явно пересчитать статистику
SET STATISTICS INDEX idx1;

-- снова смотрим статистику
select rdb$statistics
from rdb$indices
where rdb$index_name = 'IDX1';
-- чудес не бывает, она снова вернула 1,00

-- естественно, индекс при такой статистике
-- никуда не подцепится:
select fld_Value
from t1
where (fld_key) starting with '1233';
/*
получившийся план запроса:

PLAN (RDB$DATABASE NATURAL)
PLAN JOIN ()
PLAN (T1 NATURAL)

как мы видим из плана, значения вычисляемого поля,
существовавшие в момент построения индекса,
не используются. Вместо них используются текущие 
(мгновенные) значения вычисляемого поля.
*/



фигура вторая - тоже неудачная
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
-- давайте попробуем построить вычисляемый индекс
-- по вычисляемому полю, такой, чтобы в запросах
-- он всё ж подцепился.

alter table t1
  add fld_AltKey computed by(
    -- это, конечно, не первый блок цифр. Просто уберём,
    -- для иллюстрации, подзапросы из выражения:
    cast(
        substring(fld_Value from 1 for 1) ||
        substring(fld_Value from 3 for 1) ||
        substring(fld_Value from 5 for 3)
        as varchar(5)
    )
  )

select *
from t1;
/*
FLD_VALUE                               FLD_KEY           FLD_ALTKEY
--------------------------------------------------------
ABCDE123344556667ILIKEIT        123344556667   ACE12
KARKNULVORONNEVERMORE123    123                 KRNUL
OBEAFINEGIRLKISSME!                                      OEFIN

ок, всё прилично пока...
*/

-- альтернативный индекс
create index idx2 on t1
computed by ((fld_AltKey));

-- коммитим и смотрим статистику индекса
select rdb$statistics
from rdb$indices
where rdb$index_name = 'IDX2';
-- вернула 1,00. Статистика не пересчиталась... странно!

-- укажем явный пересчёт статистики
SET STATISTICS INDEX idx2;

-- снова смотрим статистику
select rdb$statistics
from rdb$indices
where rdb$index_name = 'IDX2';
-- селективность с какого-то перепоя становится отрицательной: -1.00!

-- ладно, пробуем подцепить индекс...
select fld_Value
from t1
where (fld_AltKey) starting with 'K';
/*
не, нифига. План запроса:
PLAN (T1 NATURAL)
То же, что и раньше, только поскольку  вычисляемое 
поле теперь без запросов (скалярные функции), то и 
join'а больше нет.
*/



фигура третья - удачная, конечно, но толку-то...
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
-- хорошо, теперь создадим индекс по выражению так,
-- чтобы вычисляемые поля в нём вообще не светились.
-- Попытаемся подцепить его в запросе

-- копипастим выражение для второго вычисляемого поля:
create index idx3 on t1
computed by (
    cast(
        substring(fld_Value from 1 for 1) ||
        substring(fld_Value from 3 for 1) ||
        substring(fld_Value from 5 for 3)
        as varchar(5)
    )
);

select rdb$statistics
from rdb$indices
where rdb$index_name = 'IDX3';
-- получаем ожидаемую статистику - 0,33

-- подцепится?
select fld_Value
from t1
where (fld_AltKey) starting with 'K';
/* PLAN (T1 INDEX (IDX3))
индекс подцепился, логично - выражение для fld_AltKey 
попало в where clause (поле вычисляется же в момент 
вычитки), и совпало с для fld_AltKey.
*/



Можно сделать предположение: любые пляски с source вычисляемого поля/индекса не помогают подцепить индекс, который вычисляется как выражение, включающее в себя это вычисляемое поле. Нельзя упростить выражение для вычисляемого индекса через использование в нём вычисляемого поля. Попробуем ещё раз обмануть птичку.
фигура четвёртая - неудачная
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
-- повторяем третий вариант, но теперь включаем в индекс 
-- целиком выражение для первого вычисляемого поля:
create index idx4 on t1
computed by((
    -- PNum - номер символа
    -- PChr, PNextChr - текущий и следующий за ним символ
    with recursive tmp(PNum, PChr, PNextChr) as (
        select
            1,
            substring(fld_Value from 1 for 1),
            substring(fld_Value from 2 for 1)
        from rdb$database

        union all

        select
            PNum + 1,
            substring(fld_Value from PNum + 1 for 1),
            substring(fld_Value from PNum + 2 for 1)
        from tmp
        where
            -- рекурсия не прерывается:
            -- * пока не достигли конца строки
            PNum < char_length(fld_Value) and
            -- * пока не попали на первый фронт цифра -> не-цифра
            (
                not (PChr between '0' and '9') or
                (PNextChr between '0' and '9')
            )
    ) -- /with recursive
    select cast(list(PChr, '') as varchar(30))
    from tmp
    where
        -- в итоговый лист попадают только цифры
        PChr between '0' and '9'
)) -- /computed by

-- что статистика, изменилась?
select rdb$statistics
from rdb$indices
where rdb$index_name = 'IDX4';
-- о, да! она 0,33.

-- ну, подцепит или нет?
select fld_Value
from t1
where fld_Key starting with '1233';
/* нет, не подцепит

PLAN (RDB$DATABASE NATURAL)
PLAN JOIN ()
PLAN (T1 NATURAL)

непонятно почему, возможно оптимизатор опустил руки на join'е.
По плану есть вопрос - в конце сообщения
*/



Обмануть не вышло.
Но оно и к лучшему. Потому что чем городить такие огороды - копипастить некислый sql в два места - лучше уж физическое ключевое поле создать.

В рамках раскапывания старых могил спрошу ещё:
* что означает отрицательная селективность индекса (фиг. 2)?
* я плохо понимаю, как это работает, но движку можно подсказать, какой индекс он должен подцепить при выполнении запроса - select ... PLAN () . Можно ли в верхних "НЕТ-НЕТ-ДА-НЕТ" как-то помочь оптимизатору найти вычисляемый индекс?
* не совсем понимаю план запроса в чевёртой фигуре - почему t1 join'ится к rdb$database? Надо же для каждой записи таблицы ( T1 NATURAL ) посчитать выч. поле с подзапросом ( RDB$DATABASE NATURAL ), а не наоборот.
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38837426
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MrCat,

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

Какие чудо результаты найдёт твой индекс, если модифицируешь данные в таблицах, которые используются в подзапросе вычисляемого поля?
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38837463
MrCat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Симонов Денис,

Ну я так и предположил, когда первое сообщение писал - "оптимизатор должен явно видеть, что значение индекса по вычисляемым полям изменится только тогда, когда изменятся значения использованных в выражении полей", там же не только подзапрос может вернуть изменившееся значение, там RAND() достаточно пихнуть в выражение.

Просто тут я присоединяюсь и к Arioch, и к kdv ( CORE-1212 ) - индекс разрешён, создаётся, но не подцеплятся. Смущает только пользователя.

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

С индексом теперь понятно, спасибо, но что, всё же, означает отрицательная селективность? Это глюк или говорит о чём-то?
...
Рейтинг: 0 / 0
Издеваясь над птицей - странный эффект с индексом по computed-by полю
    #38837518
Arioch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MrCat,

Ты везучий... Или может быть у тебя ODS новая... Или может быть твои бешенные индексы - единственные на этой таблице и нормальных индексов по ней нет (PK например)

Птому что в моем примере, после создание "бешенного" индекса переставали работать ЛЮБЫЕ запросы, которые потенциально могли использовать ХОТЯ БЫ КАКОЙ-НИБУДЬ индекс.
...
Рейтинг: 0 / 0
15 сообщений из 15, страница 1 из 1
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Издеваясь над птицей - странный эффект с индексом по computed-by полю
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]