Могу ли я преобразовать кучу логических столбцов в одно растровое изображение в PostgreSQL? - PullRequest
7 голосов
/ 29 января 2012

Я бы хотел преобразовать запрос, такой как:

SELECT BoolA, BoolB, BoolC, BoolD FROM MyTable;

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

Например, если BoolAи BoolD были правдой, я бы хотел 1001 или 9.

Я имею в виду что-то такое:

SELECT
   CASE WHEN BoolD THEN 2^0 ELSE 0 END +
   CASE WHEN BoolC THEN 2^1 ELSE 0 END +
   CASE WHEN BoolB THEN 2^2 ELSE 0 END +
   CASE WHEN BoolA THEN 2^3 ELSE 0 END
FROM MyTable;

Но я не уверенесли это лучший подход и кажется довольно многословным.Есть ли простой способ сделать это?

Ответы [ 3 ]

13 голосов
/ 29 января 2012

Для битовой маски тип bitstring будет лучшим выбором.Тогда это может выглядеть так:

SELECT BoolD::int::bit
    || BoolC::int::bit
    || BoolB::int::bit
    || BoolA::int::bit
FROM tbl;

TRUE преобразуется в 1, FALSE в 0.Вы можете просто объединить биты в цепочку битов.


Привести бит (n) к целому числу

Кажется, вам нужен integer как результат - есть простой и быстрый способ:

SELECT (BoolD::int::bit
     || BoolC::int::bit
     || BoolB::int::bit
     || BoolA::int::bit)::bit(4)::int
FROM tbl;

Обязательно прочитайте мелкий шрифт в главе " Функции битовых строк и операторы " руководства.


Я придумал еще две идеи и собрал краткий тест / справку с 10 тыс. Строк, чтобы подвести итоги.

Настройка теста:

CREATE TEMP TABLE t (boola bool, boolb bool, boolc bool, boold bool);
INSERT INTO t
SELECT random()::int::bool
     , random()::int::bool
     , random()::int::bool
     , random()::int::bool
FROM   generate_series(1,10000);

Демо:

SELECT  CASE WHEN boold THEN 1 ELSE 0 END
     + (CASE WHEN boolc THEN 1 ELSE 0 END << 1)
     + (CASE WHEN boolb THEN 1 ELSE 0 END << 2)
     + (CASE WHEN boola THEN 1 ELSE 0 END << 3) AS andriy

     ,  boold::int
     + (boolc::int << 1)
     + (boolb::int << 2)
     + (boola::int << 3) AS mike

     , (boola::int::bit
     || boolb::int::bit
     || boolc::int::bit
     || boold::int::bit)::bit(4)::int AS erwin1

     ,  boold::int
     | (boolc::int << 1)
     | (boolb::int << 2)
     | (boola::int << 3) AS erwin2

     , (((
       boola::int << 1)
     | boolb::int << 1)
     | boolc::int << 1)
     | boold::int        AS erwin3
FROM   t
LIMIT  15

Вы также можете использовать побитовое ИЛИ | вместо оператора +.
Отдельные тестовые прогоны показывают в основном такую ​​же производительность для всех пяти методов.

2 голосов
/ 29 января 2012

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

SELECT
  (BoolD::int << 0) +
  (BoolC::int << 1) +
  (BoolB::int << 2) +
  (BoolA::int << 3)
from MyTable;
2 голосов
/ 29 января 2012

Может быть так:

SELECT
  (CASE WHEN BoolA THEN 1 ELSE 0 END << 0) +
  (CASE WHEN BoolB THEN 1 ELSE 0 END << 1) +
  (CASE WHEN BoolC THEN 1 ELSE 0 END << 2) +
  (CASE WHEN BoolD THEN 1 ELSE 0 END << 3) AS BitMask
FROM MyTable;

, где << - оператор bitwise shift left.

...