Разбить заданную строку и подготовить оператор case
Таблица: имя_таблицы
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_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;
Приведенный выше оператор обновления выполняет работу, но статически.
Как и в случае обновления таблицы, я хочу подготовить только инструкцию case, которая должна быть динамической и может изменяться в соответствии с параметрами. (p_dates,p_sets)
изменения.
Вопросы:
- Как разделить данные даты, которые
p_dates
? (Я имеюto
ключевое слово между двумя датами.) - Как разделить данные наборы,
p_sets
? (У меня есть запятая ',' между двумя set_names.) - Как подготовить динамический регистр после разбиения
p_dates
а такжеp_sets
?
Этот вопрос относится к динамическому описанию случая с использованием SQL Server 2008 R2, что то же самое, но для Microsoft SQL Server.
3 ответа
Чистая настройка:
CREATE TABLE tbl (
given_date date
, set_name varchar
);
Используйте единственный термин в качестве имени столбца для одного значения.
Тип данных явно date
и не 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;
"Параллельное гнездо" удобно, но имеет свои недостатки. Postgres 9.4 добавляет чистое решение. Увидеть ниже.
Динамическое исполнение
Подготовленное заявление
Подготовленные операторы видны только сеансу создания и погибают вместе с ним. По документации:
Подготовленные операторы действуют только в течение текущего сеанса работы с базой данных.
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');
Превосходный дизайн
Используйте параметры массива (все еще могут быть представлены как строковые литералы), daterange
тип (обе страницы 9.3) и новая параллель unnest()
(стр. 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;
<@
будучи оператором "элемент содержится".
Вызов:
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($1,$2) 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}');
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;