【问题标题】:Split given string and prepare case statement拆分给定的字符串并准备 case 语句
【发布时间】:2015-03-17 03:01:02
【问题描述】:

表格:表格名称

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。

例如

我想像这样更新表格:

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_datesset_name 是传递参数,因为它们是动态的。我可以通过2套 如上图s1,s2 或者根据需要可以通过4套。

所以我需要动态 case 语句来更新 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;

上面的更新语句完成的工作是静态的。

与更新表的方法相同,我只想准备 case 语句,它应该是动态的,可以根据参数 (p_dates,p_sets) 更改。

问题

  1. 如何拆分p_dates 的给定日期? (我在两个日期之间有 to 关键字。)
  2. 如何拆分p_sets 的给定集合? (我在两个 set_name 之间有 ',' 逗号。)
  3. p_datesp_sets拆分后如何准备动态case语句?

这个问题与Dynamic case statement using SQL Server 2008 R2 相关,这与 Microsoft SQL Server 相同。

【问题讨论】:

标签: sql postgresql postgresql-9.3 set-returning-functions unnest


【解决方案1】:

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;

【讨论】:

  • 如果有5套怎么办?您需要根据您的脚本再次编写when和condition 5 time。
  • 出现错误:invalid type name "array_length(p_dates ,1) into p_count"。由于性能问题,甚至我也不想使用循环。可能有 100 组要更新,因此循环不是一个好主意。
【解决方案2】:

干净的设置:

CREATE TABLE tbl (
  given_date date
, set_name varchar
);

使用单数术语作为单数值的列名。
数据类型明显是date and not a timestamp

将您的文本参数转换为有用的表格:

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:

准备好的语句仅在当前数据库会话期间持续。

PREPARE每个会话一次

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;

或使用客户提供的工具来准备声明。
使用任意参数执行n次:

EXECUTE upd_tbl('2001-01-01to2001-01-05,2001-01-10to2001-01-15', 's1,s4');

服务器端函数

函数对所有会话保持和可见。

CREATE FUNCTION一次

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;

调用n次:

SELECT f_upd_tbl('2001-01-01to2001-01-05,2001-01-20to2001-01-25', 's2,s5');

SQL Fiddle

卓越的设计

使用数组参数(仍然可以作为字符串字面量提供)、daterange 类型(均为 pg 9.3)和 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;

&lt;@ 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}');

详情:

【讨论】:

  • 太棒了!太感谢了。在function f_upd_tbl() 的高级设计部分出现错误ERROR: function unnest(daterange[], text[]) does not exist LINE 7: FROM unnest($1, $2) s(date_range, set_name)。我使用的是 PostgreSQL 9.3 版本。
  • 但无论如何我对这个概念很清楚。太感谢了。我希望如果有 1000 个投票按钮,我可以点击它。
  • @MAK: unnest() 具有多个参数是 Postgres 9.4 中的新功能,如前所述。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-08
  • 1970-01-01
  • 2015-12-09
  • 2011-04-18
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
相关资源
最近更新 更多