Is there a sane way of storing int, float and boolean values in the same column in Postgres?
If have something like that:
rid | time | value |
---|---|---|
2d9c5bdc-dfc5-4ce5-888f-59d06b5065d0 | 2021-01-01 00:00:10.000000 +00:00 | true |
039264ad-af42-43a0-806b-294c878827fe | 2020-01-03 10:00:00.000000 +00:00 | 2 |
b3b1f808-d3c3-4b6a-8fe6-c9f5af61d517 | 2021-01-01 00:00:10.000000 +00:00 | 43.2 |
Currently I’m using jsonb to store it, the problem however now is, that I can’t filter in the table with for instance the greater operator.
The query
SELECT *
FROM points
WHERE value > 0;
gives back the error:
ERROR: operator does not exist: jsonb > integer: No operator matches the given name and argument types. You might need to add explicit type casts.
For me it’s okay to handle boolean as 1 or 0 in case of true
or false
. Is there any possibility to achieve that with jsonb or is there maybe another super type which lets me use a column that is able to use all three types?
Performance is not so much of a concern here, as I’m going to have very few records inside of that table, max 5k I guess.
3
Answers
I actually found out, regardless of the jsonb fields value, that you can compare it to other jsonb in postgres. That means, I can for instance do the following:
This correctly gives me back only the third row. It just ignores the
bool
value. To filter for a certain bool I can achieve that with the following query:This is good enough for me. I even could hold timestamps in the json column and compare them using this methodology.
Another way of solving the problem after all your comments seem to be to make the column a numeric. This would work as well, but requires more client side conversion, as I would have to have a second type column, remembering what the actual type is. This type should than be used on the client side to convert the value back into its og value. For integers its trivial, for booleans like @schwern suggested, one can use
1
and0
, for dates, one could use the unix timestamp representation.When I now want to search for a certain value, the
type
has to be contained in thewhere
clause as well.You should skip the rows where
value
is notnumber
and cast the value tonumeric
, e.g.:If you were just storing integers and floats, normally you’d use a
float
ornumeric
column.But there’s that pesky
true
.You could cast the JSON…
…but there’s that pesky
true
.You have to convert the boolean to a number to make it work.
Or ignore it.
This having to work around the type system suggests that
value
is actually two or even three different fields crammed into one. Consider separating them into multiple columns.