2016-06-30 6 views
0

У меня есть следующая схема:Обновление нескольких строк в таблице с разными значениями

DROP SCHEMA IF EXISTS s CASCADE; 
CREATE SCHEMA s; 

CREATE TABLE "s"."t1" 
(
    "c1" BigSerial PRIMARY KEY, 
    "c2" BigInt NOT NULL, 
    "c3" BigInt 
) 
WITH (OIDS=FALSE); 

INSERT INTO s.t1 (c2, c3) VALUES (10, 100); 
INSERT INTO s.t1 (c2, c3) VALUES (20, 200); 
INSERT INTO s.t1 (c2, c3) VALUES (30, 300); 
INSERT INTO s.t1 (c2, c3) VALUES (40, 400); 

PREPARE updateplan (BigInt, BigInt) AS 
    update s.t1 
    SET c3 = $2 
    WHERE c2 = $1; 

EXECUTE updateplan (20, 250); 

PREPARE updatearrayplan(BigInt[], BigInt[]) AS 
    for i in size($1) 
    DO 
     update s.t1 
     SET c3 = $2[$i] 
     WHERE c2 = $1[$i] 
    END FOR  

EXECUTE updatearrayplan({20, 30}, {275, 375}) 

/* 20, 200 -> 20, 275 */ 
/* 30, 300 -> 30, 375 */ 

После выполнения updatearrayplan я ожидающей строки в имеют эти значения 20 -> 275, 30 -> 375

Есть ли способ обновить несколько строк с разными значениями столбцов, переданными в виде массива. Также есть гарантия, что порядок массивов будет сохранен.

ответ

1

Try:

WITH arrays AS( 
    SELECT * from 
    unnest(
     ARRAY[20, 30], 
     ARRAY[275, 375] 
    ) as xy(x,y) 
) 
UPDATE t1 
SET c3 = a.y 
FROM arrays a 
WHERE c2 = a.x; 

Смотрите описание unnest функции здесь: click


EDIT


@kordiroko К сожалению. Я пробовал целый день, изменяя ваше решение. Не удалось заставить его работать.

Возможно, у вас установлена ​​более старая версия PostgreSQL. Я тестировал на версии 9.5, он взял меня всего пару минут, чтобы получить его работу, просто копировать/вставить и изменить два параметра в запросе:

create table t1(
    c2 BIGINT, 
    c3 bigint 
); 


insert into t1(c2, c3) 
select x, x * 100 
from generate_series(1,1000000) x; 

CREATE OR REPLACE FUNCTION updatefunc1(BigInt[], BigInt[]) 
RETURNS void as $$ 
BEGIN 
    FOR i IN array_lower($1, 1) .. array_upper($1, 1) 
    LOOP 
     update t1 
     SET c3 = $2[i] 
     WHERE c2 = $1[i]; 
    END LOOP; 
END;  
$$ 
LANGUAGE plpgsql; 


CREATE OR REPLACE FUNCTION updatefunc2(BigInt[], BigInt[]) 
RETURNS void as $$ 
BEGIN 
    WITH arrays AS( 
     SELECT * from 
     unnest($1, $2 ) as xy(x,y) 
    ) 
    UPDATE t1 
    SET c3 = a.y 
    FROM arrays a 
    WHERE c2 = a.x; 
END;  
$$ 
LANGUAGE plpgsql; 


select updatefunc1(ARRAY[20], ARRAY[275]); 

select updatefunc2(ARRAY[30], ARRAY[555]); 

select * from t1 where c2 in (20,30); 

Позвольте мне знать, если это правильно или есть лучшее решение.

Это очень правильно, но ... это немного медленно.

Я проверил вашу функцию только 100 записей:

select updatefunc1(
    array(select * from generate_series(1,100)), 
    array(select 22222 from generate_series(1,100)) 
); 

потребовалось более 12 секунд:

Результат (стоимость = 20.00..20.31 строк = 1 ширина = 0) (фактическая время = 12259.095..12259.096 строки = 1 петли = 1) Выход: updatefunc1 (($ 0) :: BIGINT [], ($ 1) :: BIGINT []) InitPlan 1 (возвращает $ 0)

Теперь сравните его к моей функции, но для 100.000 записей:

select updatefunc2(
    array(select * from generate_series(1,100000)), 
    array(select 22222 from generate_series(1,100000)) 
); 

результат является 1 секунда 150 мс:

Результат (стоимость = 20.00..20.31 строк = 1 ширина = 0) (фактический время = 1150.018 ..1150.123 строки = 1 петли = 1) Выход: updatefunc2 (($ 0) :: BIGINT [], ($ 1) :: BIGINT []) InitPlan 1 (возвращает $ 0)

Приведенные выше результаты означают, что ваша функция:

(12/100)/(1,150/100000) = 10434,78

раз slooooooooooooooooooooooooooweeeeeeeeeeeeeeeeeeeeeeer,
и в% это только 1043400% медленнее


EDIT 2


Моя версия 9.2.15. Он подбрасывает синтаксические ошибки

Ниже приводится версия, которая должна работать на ранних версиях с PostgreSQL:

CREATE OR REPLACE FUNCTION updatefunc3(BigInt[], BigInt[]) 
RETURNS void as $$ 
BEGIN 
    WITH arrays AS( 
     SELECT arr1[ rn ] as x, arr2[ rn ] as y 
     FROM (
      SELECT $1 as arr1, $2 as arr2, generate_subscripts($1, 1) As rn 
     ) x 
    ) 
    UPDATE t1 
    SET c3 = a.y 
    FROM arrays a 
    WHERE c2 = a.x; 
END;  
$$ 
LANGUAGE plpgsql; 

select updatefunc3(ARRAY[40,82,77], ARRAY[333,654]); 

select * from t1 where c2 in (40,82,77); 

и тест скорости uptadint 100000 строк:

select updatefunc3(
    array(select * from generate_series(1,100000)), 
    array(select 22222 from generate_series(1,100000)) 
); 

Результат (стоимость = 20,00..20.31 строк = 1 ширина = 0) (фактическое время = 1361.358 ..1361.460 rows = 1 loops = 1) Выход: updatefunc3 (($ 0) :: BIGINT [], ($ 1) :: BIGINT []) InitPlan 1 (возвращает $ 0)

время обновления 100k строк ниже 1,5 секунды


EDIT 3


@kordiko: Не могли бы вы сказать мне, почему ваш запрос намного лучше. Моя функция проходит через каждую строку и обновляет элементы по одному. Ваша функция также выглядит аналогичной. Разве что все эквивалентные строки обновляются одновременно в вашем запросе.

Это потому, что моя функция работает только одну команду обновления независимо от количества элементов в массивах, в то время как ваши обновления функциональных элементов один на один - для 100 элементов он работает 100 команд обновления. Для 1000 элементов он запускает 1000 команд обновления.
Я провел тесты на столе со 1000000 строками, но без индекса. В моей функции обновление читает содержимое таблицы только один раз (выполняется полное сканирование таблицы) и обновляет соответствующую строку. Ваша функция выполняет 100 обновлений, и каждый из них выполняет полное сканирование таблицы.
Если вы создаете и индекс col2, то скорость вашей функции резко inceases см ниже тест (обратите внимание, что ряд элементов в этом тесте увеличивается от 100 до 100000:

create INDEX t1_c2_ix on t1(c2); 

select updatefunc1(
    array(select * from generate_series(1,100000)), 
    array(select 22222 from generate_series(1,100000)) 
); 

Result (cost=20.00..20.31 rows=1 width=0) (actual time=**3430.536**..3430.636 rows=1 loops=1) 
    Output: updatefunc1(($0)::bigint[], ($1)::bigint[]) 
    InitPlan 1 (returns $0) 

Теперь время составляет лишь около 3,5 второго

И испытание моей функции после создания индекса:.

select updatefunc3(
    array(select * from generate_series(1,100000)), 
    array(select 22222 from generate_series(1,100000)) 
); 

Результат (стоимость = 20.00..20.31 строк = 1 ширина = 0) (фактическое время = 1270,619. .1270.724 строки = 1 петли = 1) Выход: updatefunc3 (($ 0) :: BIGINT [], ($ 1) :: BIGINT []) InitPlan 1 (возвращает $ 0)

время остается тем же самым, но на 100% быстрее, чем ваша функция.

+0

@kordiroko Извините. Я пробовал целый день, изменяя ваше решение. Не удалось заставить его работать. – gudge

+0

Я обновил свой андер, пожалуйста, взгляните на него. – krokodilko

+0

Моя версия - 9.2.15. Он вызывает синтаксические ошибки. – gudge

0

Мой ответ:

CREATE OR REPLACE FUNCTION s.updatefunc1(BigInt[], BigInt[]) 
RETURNS void as $$ 
BEGIN 
    FOR i IN array_lower($1, 1) .. array_upper($1, 1) 
    LOOP 
     update s.t1 
     SET c3 = $2[i] 
     WHERE c2 = $1[i]; 
    END LOOP; 
END;  
$$ 
LANGUAGE plpgsql; 

select s.updatefunc1(ARRAY[20], ARRAY[275]); 

Это делает работу. Я получаю свой ответ:

SELECT c2, c3 FROM s.t1; 
    c2 | c3 
    ----+----- 
    10 | 100 
    30 | 300 
    40 | 400 
    20 | 275 --> Updated 

Сообщите мне, если это правильно или есть лучшее решение

Смежные вопросы