Table: table_name
create table table_name ( given_dates timestamp, set_name varchar );
Insertion of records:
insert into table_name values('2001-01-01'),('2001-01-05'),('2001-01-10'), ('2001-01-15'),('2001-01-20'),('2001-01-25'), ('2001-02-01'),('2001-02-05'),('2001-02-10'), ('2001-02-15');
Now I want to update set_name for some dates.
For example:
I want to update table like this:
given_dates set_name ---------------------- 2001-01-01 s1 2001-01-05 s1 2001-01-10 s2 2001-01-15 s2 2001-01-20 2001-01-25 2001-02-01 2001-02-05 2001-02-10 2001-02-15
Note: The given_dates
and set_name
are pass a parameter because of they are dynamic. I may pass 2 sets
as shown above s1,s2
or may pass 4 sets according to the requirement.
So I need the dynamic case statement for update the set_name
.
Given two parameters:
declare p_dates varchar := '2001-01-01to2001-01-05,2001-01-10to2001-01-15'; declare p_sets varchar := 's1,s2';
Well I can do this by using following static script:
Static Update statement:
update table_name SET set_name = CASE when given_dates between '2001-01-01' and '2001-01-05' then 's1' when given_dates between '2001-01-10' and '2001-01-15' then 's2' else '' end;
The above update statement does the job done but statically.
Like the same way to update table I want to prepare only case statement which should be dynamic which can change as per the parameters (p_dates,p_sets)
changes.
Questions:
- How to split the given dates that is
p_dates
? (I haveto
keyword in between two dates.) - How to split the given sets that is
p_sets
? (I have ‘,’ comma in between two set_names.) - How to prepare dynamic case statement after splitting the
p_dates
andp_sets
?
This question relates to Dynamic case statement using SQL Server 2008 R2, which is the same thing but for Microsoft SQL Server.
Advertisement
Answer
Clean setup:
CREATE TABLE tbl ( given_date date , set_name varchar );
Use a singular term as column name for a single value.
The data type is obviously date
and not a timestamp
.
To transform your text parameters into a useful table:
SELECT unnest(string_to_array('2001-01-01to2001-01-05,2001-01-10to2001-01-15', ',')) AS date_range , unnest(string_to_array('s1,s2', ',')) AS set_name;
“Parallel unnest” is handy but has its caveats. Postgres 9.4 adds a clean solution, Postgres 10 eventually sanitized the behavior of this. See below.
Dynamic execution
Prepared statement
Prepared statements are only visible to the creating session and die with it. Per documentation:
Prepared statements only last for the duration of the current database session.
PREPARE
once per session:
PREPARE upd_tbl AS UPDATE tbl t SET set_name = s.set_name FROM ( SELECT unnest(string_to_array($1, ',')) AS date_range , unnest(string_to_array($2, ',')) AS set_name ) s WHERE t.given_date BETWEEN split_part(date_range, 'to', 1)::date AND split_part(date_range, 'to', 2)::date;
Or use tools provided by your client to prepare the statement.
Execute n times with arbitrary parameters:
EXECUTE upd_tbl('2001-01-01to2001-01-05,2001-01-10to2001-01-15', 's1,s4');
Server-side function
Functions are persisted and visible to all sessions.
CREATE FUNCTION
once:
CREATE OR REPLACE FUNCTION f_upd_tbl(_date_ranges text, _names text) RETURNS void AS $func$ UPDATE tbl t SET set_name = s.set_name FROM ( SELECT unnest(string_to_array($1, ',')) AS date_range , unnest(string_to_array($2, ',')) AS set_name ) s WHERE t.given_date BETWEEN split_part(date_range, 'to', 1)::date AND split_part(date_range, 'to', 2)::date $func$ LANGUAGE sql;
Call n times:
SELECT f_upd_tbl('2001-01-01to2001-01-05,2001-01-20to2001-01-25', 's2,s5');
Superior design
Use array parameters (can still be provided as string literals), a daterange
type (both pg 9.3) and the new parallel unnest()
(pg 9.4).
CREATE OR REPLACE FUNCTION f_upd_tbl(_dr daterange[], _n text[]) RETURNS void AS $func$ UPDATE tbl t SET set_name = s.set_name FROM unnest($1, $2) s(date_range, set_name) WHERE t.given_date <@ s.date_range $func$ LANGUAGE sql;
<@
being the “element is contained by” operator.
Call:
SELECT f_upd_tbl('{"[2001-01-01,2001-01-05]" ,"[2001-01-20,2001-01-25]"}', '{s2,s5}');
Details: