Parsing data with PostgreSQL
A lot of my posts seem to be about PostgreSQL, which is mainly because I work with it a lot of my professional time lately. This week I needed to convert the mysterious way in which Evault stores the backup schedule. Evault uses SQL server for storing it's schedules, but I've dumped that into a PostgreSQL database. One of the types of entry looks like binary series for on which days the backup run. So a serie of 1110110 states that the backup ought to run on Monday, Tuesday, Wednesday, Friday and Saturday. Let's create a database:
create table schedule ( description character varying(12) unique not null , daysofweek bit varying(7) check(length(daysofweek)=7) );
Note that I am using a bit string type for defining the daysofweek string. This way it directly validates added entries. The check constraint ensures that exactly 7 characters are added. So I feed this table with some sample data:
insert into schedule(description, daysofweek) values ('first', '1110101'), ('second', '0001000'), ('third', '0101010');
No rocket science so far. Now to find out which backups are scheduled for today. I'll combine a few queries:
dbname=> select extract(dow from now()); date_part ----------- 2 (1 row) dbname=> select string_to_array(daysofweek::text,null) from schedule; string_to_array ----------------- {1,1,1,0,1,0,1} {0,0,0,1,0,0,0} {0,1,0,1,0,1,0} (3 rows)
The first query asks for the day of week (dow) from the current moment. The second query splits the bit string by the null value, resulting in a array element for every bit. The bit string obviously needs to be casted to text before the string_to_array function works. Combining these two queries gives:
=> select description, (string_to_array(daysofweek::text,null))[extract(dow from now())] from schedule; description | string_to_array -------------+----------------- first | 1 second | 0 third | 1 (3 rows)
There are still a few things to note here. A PostgreSQL array starts counting it's fields with one (where most other languages I know start counting at zero). I also assumed in the example that the Evault string starts with Monday. What if it would start with Sunday? We can use the modulo for that. That leaves the remainder of a division.
somedb=> select ( ( extract(dow from now()) + 1 )::integer % 7 ) + 1; ?column? ---------- 1 (1 row)
As we have seven days, we need the 7 after the modulo sign (%). The first +1 states that we want to offset the day of the week by one. When we start counting on Monday, the Tuesday is no longer the second, but the third (second plus one) day. Hence the +1 after the dow extraction. The second +1 is there to offset for the fact that a PostgreSQL array starts counting at one instead of zero. So, the total select is:
=> select description, (string_to_array(daysofweek::text,null))[( ( extract(dow from now()) + 1 )::integer % 7 ) + 1] from schedule; description | string_to_array -------------+----------------- first | 0 second | 1 third | 1 (3 rows)