2015-12-27 2 views
1

Мне нужно работать с базой данных, которая содержит столбец change, указывающий, каким образом были изменены три других столбца в сравнении с предыдущей соответствующей записью. Тип изменения может быть new, removed или changed.Преобразование «подстроенных флагов» назад к исходному значению

Эти типы присвоены следующие номера:

column | a  b  c 
----------+---------------------- 
new  | 3  12  48 
removed | 2  8  32 
changed | 1  4  16 

содержание В change столбца является суммой всех ходатайствующих типов изменений, то есть, если столбец a был changed и bremoved, то change колонка будет 1 + 8 = 9. (Всегда есть изменение, т. Е. Может быть 1, 2 или 3 слагаемых.)

Моя проблема: я не могу придумать разумный способ преобразования этого «суммированного флага» обратно в исходное значение (часть проблемы, не зная, что делать в Google).

Я могу сказать, что если change неровный, a был либо new, либо changed; и что если change>=48, c было new плюс, возможно, другие изменения, иначе change>=32 =>c был removed плюс, возможно, другие изменения и так далее. Возможно, я мог бы объединить это в огромный логический запрос, но я уверен, что для этого нужно разработать сложное решение.

Я использую PostgreSQL в случае, если это релевантно. В таблице содержится около 50 миллионов строк.

ответ

1

Это можно сделать, используя побитовый И оператор (&) в сочетании с побитовыми сдвигами (>>).

Следующий запрос возвращает все записи таблицы, с в трех дополнительных колонок изменения в , б и гр соответственно:

select *, 
     case change & 3 
      when 1 then 'changed' 
      when 2 then 'removed' 
      when 3 then 'new' 
     end as change_to_a, 
     case (change >> 2) & 3 
      when 1 then 'changed' 
      when 2 then 'removed' 
      when 3 then 'new' 
     end as change_to_b, 
     case (change >> 4) & 3 
      when 1 then 'changed' 
      when 2 then 'removed' 
      when 3 then 'new' 
     end as change_to_c 
from mytable; 

Вот fiddle.

Пример вывода:

id change change_to_a change_to_b change_to_c 
----------------------------------------------- 
1  9  changed  removed  (null) 
2 50  removed  (null)  new 
3 83   new  (null)  changed 
4 20  (null)  changed  changed 
5 25  changed  removed  changed 

Вот другой подход. Это также возвращает 3 дополнительных столбцов, но один для каждого типа изменений, а значения представляют собой конкатенацию «а», «б», «с»:

select *, 
     concat(
      case when change  & 3 = 1 then 'a' end, 
      case when (change >> 2) & 3 = 1 then 'b' end, 
      case when (change >> 4) & 3 = 1 then 'c' end) changed, 
     concat(
      case when change  & 3 = 2 then 'a' end, 
      case when (change >> 2) & 3 = 2 then 'b' end, 
      case when (change >> 4) & 3 = 2 then 'c' end) removed, 
     concat(
      case when change  & 3 = 3 then 'a' end, 
      case when (change >> 2) & 3 = 3 then 'b' end, 
      case when (change >> 4) & 3 = 3 then 'c' end) new 
from mytable; 

Вот fiddle.

Пример вывода:

id change changed  removed new 
----------------------------------------- 
1  9  a   b  (null) 
2 50  (null)   a  c 
3 83  c   (null)  a 
4 20  bc   (null) (null) 
5 25  ac   b  (null) 
+0

Большое спасибо, он работает красиво. Просто КАК вы знали использовать '&' с 3 на каждом и '>>' некоторые с 2 или 4? Попытка и ошибка? Логика? Это имеет смысл, как только вы его получили, но получить там тяжелую вещь ... – Christallkeks

+0

Это логика. Как вы знаете, цифры умножаются на четыре для * b * по сравнению с * a *, и снова на четыре, чтобы получить случай * c *. Теперь разделение на четыре - это то же самое, что смещение 2 двоичных цифр [это то, что знают все программисты моего (старого) возраста). Оператор AND - способ извлечь определенные биты. Число 3 соответствует младшим 2 битам числа, которое может иметь значение 0-3. Это, конечно, логика, и это очень помогает, когда вы получаете доступ к двоичной системе, маскировке и смещению. – trincot

+0

Ну ладно, я понял. Молодой программист здесь, еще многое узнать, особенно на этом абстрактном уровне. Раньше я слышал о бит-мудших операциях, но до сих пор не использовал их и не использовал. Еще раз спасибо, отличный ответ! – Christallkeks

1

Вы можете использовать битовые операции. Если я правильно понимаю:

select (case when col::bit(8) & B'00000011' then 'new' 
      when col::bit(8) & B'00000001' then 'changed' 
      when col::bit(8) & B'00000010' then 'removed' 
     end) as a_status, 
     (case when col::bit(8) & B'00001100' then 'new' 
      when col::bit(8) & B'00000100' then 'changed' 
      when col::bit(8) & B'00001000' then 'removed' 
     end) as b_status, 
     (case when col::bit(8) & B'00110000' then 'new' 
      when col::bit(8) & B'00010000' then 'changed' 
      when col::bit(8) & B'00100000' then 'removed' 
     end) as c_status 
+0

Я не совсем понял ваш работать ...:/Postgres не хочет использовать '&' на целое число, и я не мог правильно бросить 'changed' в bin или найти функцию 'dectobin' ... Жаль, что мне очень любопытно, как вы решили это без пошагового переключения, как это делали trincot. – Christallkeks

+0

@ пользователь3746543., , Если 'col' - целое число, то вы можете преобразовать его в тип' bit() 'для операции. –

Смежные вопросы