Если имелось ввиду про хранение, то все намного проще, int остается полноценным. Признак что в поле нет данных (Null) хранится в битовой маске перед самой записью. Таким образом если в записи есть Null, то само значение не записывается хранилище, но взводится флаг. Строка с Null занимает меньше памяти чем запись в которой записан 0 (битовая маска есть всегда). А еще извлечение подобных записей происходит быстрее, так как они более плотно упакованы на страницу, так что совет не использовать Null весьма спорный.
Один из примеров что были у меня - ведомости на начисление/удержание, все по заветам денормализации dPeriod (период расчета) и idCharge (Код оплаты) вынесены в Blt (Шапку ведомости)
Bltlist - содержит список сотрудников с суммами. Следующий запрос собирает информацию сколько сотрудник получил по указанному коду оплат в текущем расчетном периоде
```sql
select *
from
Blt
innerjoin Bltlist on
Blt.Id = BltList.IdBlt
where
Blt.dPeriod = '01.01.2024' and
Blt.idCharge = 10 and
Bltlist.idCard = 1000
```
PG читает из Bltlist по индексу idCard все записи с самих лохматых времен. Затем собирает все ведомости по индексу dPeriod, idCharge опять же все, в том числе где нашего сотрудника нет. затем делает хешджоин так как что слева что справа записей более 1000.
А теперь если делаем денормализацию и дублируем поля dPeriod, idCharge в Bltlist
```sql
select *
from
Blt
innerjoin Bltlist on
Blt.Id = BltList.IdBlt
where
Bltlist.dPeriod = '01.01.2024' and
Bltlist.idCharge = 10 and
Bltlist.idCard = 1000
```
Теперь PG по индексу dPeriod, idCharge, idCard читает только те записи, которые нам нужны ни каких лишних чтений
Мое понимание такое - В первом варианте сначала читается индекс и затем по каждой записи идет в основную таблицу. так как порядок записей совпадает, то страница не вытесняется из кеша и просто читается повторно из оперативной памяти. Во втором случае все начинается так же, но так как сортировка индекса не совпадает с сортировкой в таблице, то происходит постоянный промах, а старые страницы вытесняются из кеша. В худшем случае приходится прочитать страницу данных для каждой записи + полный индекс.
В своем проекте сделал так, преобразование делается в набор | и пробелов, потом в отчетной системе или веб странички у строки делается межсимвольный интервал таким что бы две || соприкасались. Как итог не нужно было ни каких внешних библиотек все возвращала СУБД. Единственное ограничение в том что ограничен в высоте штрих кода.
Работают корректно только если номера пинов и портов — константы. GCC всегда транслирует в sbi и cbi если это возможно. При этом Изменений в программе минимум. Всего то нужно использовать digitalWriteC вместо digitalWrite.
Если имелось ввиду про хранение, то все намного проще, int остается полноценным. Признак что в поле нет данных (Null) хранится в битовой маске перед самой записью. Таким образом если в записи есть Null, то само значение не записывается хранилище, но взводится флаг. Строка с Null занимает меньше памяти чем запись в которой записан 0 (битовая маска есть всегда). А еще извлечение подобных записей происходит быстрее, так как они более плотно упакованы на страницу, так что совет не использовать Null весьма спорный.
Один из примеров что были у меня - ведомости на начисление/удержание, все по заветам денормализации dPeriod (период расчета) и idCharge (Код оплаты) вынесены в Blt (Шапку ведомости)
Bltlist - содержит список сотрудников с суммами. Следующий запрос собирает информацию сколько сотрудник получил по указанному коду оплат в текущем расчетном периоде
```sql
select *
from
Blt
inner join Bltlist on
Blt.Id = BltList.IdBlt
where
Blt.dPeriod = '01.01.2024' and
Blt.idCharge = 10 and
Bltlist.idCard = 1000
```
PG читает из Bltlist по индексу idCard все записи с самих лохматых времен. Затем собирает все ведомости по индексу dPeriod, idCharge опять же все, в том числе где нашего сотрудника нет. затем делает хешджоин так как что слева что справа записей более 1000.
А теперь если делаем денормализацию и дублируем поля dPeriod, idCharge в Bltlist
```sql
select *
from
Blt
inner join Bltlist on
Blt.Id = BltList.IdBlt
where
Bltlist.dPeriod = '01.01.2024' and
Bltlist.idCharge = 10 and
Bltlist.idCard = 1000
```
Теперь PG по индексу dPeriod, idCharge, idCard читает только те записи, которые нам нужны ни каких лишних чтений
Мое понимание такое - В первом варианте сначала читается индекс и затем по каждой записи идет в основную таблицу. так как порядок записей совпадает, то страница не вытесняется из кеша и просто читается повторно из оперативной памяти.
Во втором случае все начинается так же, но так как сортировка индекса не совпадает с сортировкой в таблице, то происходит постоянный промах, а старые страницы вытесняются из кеша. В худшем случае приходится прочитать страницу данных для каждой записи + полный индекс.
Можно обратное число записывать, и в этом случае наоборот искать минимальное
Работают корректно только если номера пинов и портов — константы. GCC всегда транслирует в sbi и cbi если это возможно. При этом Изменений в программе минимум. Всего то нужно использовать digitalWriteC вместо digitalWrite.