I'd like to convert a query such as:
SELECT BoolA, BoolB, BoolC, BoolD FROM MyTable;
Into a bitmask, where the bits are defined by the values above.
For example, if BoolA
and BoolD
were true, I'd want 1001
or 9
.
I have something in mind to the effect of:
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;
But I'm not sure if this is the best approach and seems rather verbose. Is there an easy way to do this?
PostgreSQL Boolean is a simple data type that we have used to represent only the structure of true or false data or values. PostgreSQL will support the SQL99 defined Boolean data type of SQL standard; Boolean is also known as “bool”, bool is an alias of Boolean data type in PostgreSQL.
PostgreSQL supports a single Boolean data type: BOOLEAN that can have three values: true , false and NULL . PostgreSQL uses one byte for storing a boolean value in the database. The BOOLEAN can be abbreviated as BOOL . In standard SQL, a Boolean value can be TRUE , FALSE , or NULL .
For a bitmask, the type bitstring
would be the better choice. Could look like this then:
SELECT BoolD::int::bit
|| BoolC::int::bit
|| BoolB::int::bit
|| BoolA::int::bit
FROM tbl;
true
converts to 1
, false
to 0
. You can simply concatenate bits to a bitstring.
It seems you need an integer
as result - there is a simple & fast way:
SELECT (BoolD::int::bit
|| BoolC::int::bit
|| BoolB::int::bit
|| BoolA::int::bit)::bit(4)::int
FROM tbl;
Be sure to read the fine print in the chapter "Bit String Functions and Operators" of the manual.
I came up with two more ideas and put together a quick test / reference with 10k rows to sum it all up.
Test setup:
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);
Demo:
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;
You could also use |
(bitwise OR) instead of the +
operator.
Individual test runs show basically the same performance for all five methods.
Maybe like this:
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;
where <<
is the bitwise shift left
operator.
I came up with this approach as well. It's the most concise I could find short of writing a custom function. I'll accept this answer unless anyone has anything more clever.
SELECT
(BoolD::int << 0) +
(BoolC::int << 1) +
(BoolB::int << 2) +
(BoolA::int << 3)
from MyTable;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With