拆分给定的字符串并准备 case 语句
Split given string and prepare case statement
Table: table_name
create table table_name
(
given_dates timestamp,
set_name varchar
);
插入记录:
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');
现在我想更新 set_name 一些日期。
例如:
我想像这样更新 table:
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
注意: given_dates
和 set_name
是传递参数,因为它们是动态的。我可能会通过2套
如上图s1,s2
也可按要求传4组
所以我需要动态案例语句来更新 set_name
。
给定两个参数:
declare p_dates varchar := '2001-01-01to2001-01-05,2001-01-10to2001-01-15';
declare p_sets varchar := 's1,s2';
好吧,我可以使用以下静态脚本来做到这一点:
静态更新语句:
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;
上面的更新语句完成了静态的工作。
与更新方式相同 table 我只想准备 case 语句,它应该是动态的,可以根据参数 (p_dates,p_sets)
的变化而变化。
问题:
- 如何拆分给定的
p_dates
日期? (我在两个日期之间有 to
关键字。)
- 如何拆分给定的集合
p_sets
? (我在两个 set_name 之间有 ',' 逗号。)
- 拆分
p_dates
和p_sets
后如何准备动态case语句?
此问题与 Dynamic case statement using SQL Server 2008 R2 有关,这与 Microsoft SQL 服务器是同一回事。
String_to_array
declare p_dates varchar[] := string_to_array('2001-01-01,2001-01-05,
2001-01-10,2001-01-15*2001-01-01,2001-01-05,2001-01-10,2001-01-15','*');
declare p_sets varchar[] := string_to_array('s1,s2',',');
declare p_length integer=0;
declare p_str varchar[];
declare i integer;
select array_length(p_dates ,1) into p_count;
for i in 1..p_count loop
p_str := string_to_array( p_dates[i],',')
execute 'update table_name
SET set_name =
CASE
when given_dates between'''|| p_str [1] ||''' and '''|| p_str [2]
||''' then ''' || p_sets[1] ||'''
when given_dates between '''|| p_str [3] ||''' and '''
|| p_str [4] ||''' then ''' || p_sets[2] ||'''
else ''''
end';
end loop;
清理设置:
CREATE TABLE tbl (
given_date date
, set_name varchar
);
使用单数术语作为 单个 值的列名称。
数据类型显然是date
and not a timestamp
.
将您的文本参数转换成有用的 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" 很方便,但有其注意事项。 Postgres 9.4 添加了一个干净的解决方案,Postgres 10 最终净化了这个行为。见下文。
动态执行
准备好的语句
准备好的语句只对创建会话可见,并随之消亡。 Per documentation:
Prepared statements only last for the duration of the current database session.
PREPARE
每个会话一次:
PREPARE upd_tbl AS
UPDATE tbl t
SET set_name = s.set_name
FROM (
SELECT unnest(string_to_array(, ',')) AS date_range
, unnest(string_to_array(, ',')) AS set_name
) s
WHERE t.given_date BETWEEN split_part(date_range, 'to', 1)::date
AND split_part(date_range, 'to', 2)::date;
或使用您的客户提供的工具来准备声明。
使用任意参数执行n次:
EXECUTE upd_tbl('2001-01-01to2001-01-05,2001-01-10to2001-01-15', 's1,s4');
服务器端函数
函数被保留并且对所有 会话可见。
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(, ',')) AS date_range
, unnest(string_to_array(, ',')) 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;
调用n次:
SELECT f_upd_tbl('2001-01-01to2001-01-05,2001-01-20to2001-01-25', 's2,s5');
卓越的设计
使用数组参数(仍然可以作为字符串文字提供),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(, ) s(date_range, set_name)
WHERE t.given_date <@ s.date_range
$func$ LANGUAGE sql;
<@
being the "element is contained by" operator.
致电:
SELECT f_upd_tbl('{"[2001-01-01,2001-01-05]"
,"[2001-01-20,2001-01-25]"}', '{s2,s5}');
详情:
现在我们可以使用 datemultirange
.
create or replace function f_upd_tbl_multirange(_dr datemultirange , _n text[])
returns void as
$func$
UPDATE tbl t
SET set_name = s.set_name
FROM unnest(,) s(date_range,set_name)
WHERE t.given_date <@ s.date_range
$func$ language sql;
运行吧。
SELECT f_upd_tbl_multirange(
'{[''2022-01-01'',''2022-01-05''],[''2022-02-06'',''2022-02-25'']}', '{s2,s5}');
Table: table_name
create table table_name
(
given_dates timestamp,
set_name varchar
);
插入记录:
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');
现在我想更新 set_name 一些日期。
例如:
我想像这样更新 table:
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
注意: given_dates
和 set_name
是传递参数,因为它们是动态的。我可能会通过2套
如上图s1,s2
也可按要求传4组
所以我需要动态案例语句来更新 set_name
。
给定两个参数:
declare p_dates varchar := '2001-01-01to2001-01-05,2001-01-10to2001-01-15';
declare p_sets varchar := 's1,s2';
好吧,我可以使用以下静态脚本来做到这一点:
静态更新语句:
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;
上面的更新语句完成了静态的工作。
与更新方式相同 table 我只想准备 case 语句,它应该是动态的,可以根据参数 (p_dates,p_sets)
的变化而变化。
问题:
- 如何拆分给定的
p_dates
日期? (我在两个日期之间有to
关键字。) - 如何拆分给定的集合
p_sets
? (我在两个 set_name 之间有 ',' 逗号。) - 拆分
p_dates
和p_sets
后如何准备动态case语句?
此问题与 Dynamic case statement using SQL Server 2008 R2 有关,这与 Microsoft SQL 服务器是同一回事。
String_to_array
declare p_dates varchar[] := string_to_array('2001-01-01,2001-01-05,
2001-01-10,2001-01-15*2001-01-01,2001-01-05,2001-01-10,2001-01-15','*');
declare p_sets varchar[] := string_to_array('s1,s2',',');
declare p_length integer=0;
declare p_str varchar[];
declare i integer;
select array_length(p_dates ,1) into p_count;
for i in 1..p_count loop
p_str := string_to_array( p_dates[i],',')
execute 'update table_name
SET set_name =
CASE
when given_dates between'''|| p_str [1] ||''' and '''|| p_str [2]
||''' then ''' || p_sets[1] ||'''
when given_dates between '''|| p_str [3] ||''' and '''
|| p_str [4] ||''' then ''' || p_sets[2] ||'''
else ''''
end';
end loop;
清理设置:
CREATE TABLE tbl (
given_date date
, set_name varchar
);
使用单数术语作为 单个 值的列名称。
数据类型显然是date
and not a timestamp
.
将您的文本参数转换成有用的 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" 很方便,但有其注意事项。 Postgres 9.4 添加了一个干净的解决方案,Postgres 10 最终净化了这个行为。见下文。
动态执行
准备好的语句
准备好的语句只对创建会话可见,并随之消亡。 Per documentation:
Prepared statements only last for the duration of the current database session.
PREPARE
每个会话一次:
PREPARE upd_tbl AS
UPDATE tbl t
SET set_name = s.set_name
FROM (
SELECT unnest(string_to_array(, ',')) AS date_range
, unnest(string_to_array(, ',')) AS set_name
) s
WHERE t.given_date BETWEEN split_part(date_range, 'to', 1)::date
AND split_part(date_range, 'to', 2)::date;
或使用您的客户提供的工具来准备声明。
使用任意参数执行n次:
EXECUTE upd_tbl('2001-01-01to2001-01-05,2001-01-10to2001-01-15', 's1,s4');
服务器端函数
函数被保留并且对所有 会话可见。
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(, ',')) AS date_range
, unnest(string_to_array(, ',')) 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;
调用n次:
SELECT f_upd_tbl('2001-01-01to2001-01-05,2001-01-20to2001-01-25', 's2,s5');
卓越的设计
使用数组参数(仍然可以作为字符串文字提供),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(, ) s(date_range, set_name)
WHERE t.given_date <@ s.date_range
$func$ LANGUAGE sql;
<@
being the "element is contained by" operator.
致电:
SELECT f_upd_tbl('{"[2001-01-01,2001-01-05]"
,"[2001-01-20,2001-01-25]"}', '{s2,s5}');
详情:
现在我们可以使用 datemultirange
.
create or replace function f_upd_tbl_multirange(_dr datemultirange , _n text[])
returns void as
$func$
UPDATE tbl t
SET set_name = s.set_name
FROM unnest(,) s(date_range,set_name)
WHERE t.given_date <@ s.date_range
$func$ language sql;
运行吧。
SELECT f_upd_tbl_multirange(
'{[''2022-01-01'',''2022-01-05''],[''2022-02-06'',''2022-02-25'']}', '{s2,s5}');