2016-05-31 7 views
0

У меня был запрос в SQL-сервер, который выглядит примерно так:Postgresql эквивалент с (XLock, уключин)

DECLARE @someIncrementalField int; 
set @someIncrementalField = select max(SomeField) from SomeTable with (xlock, rowlock) where field1 = 1 and field2 = 2 and field3 = 3; 

таблица SQL Server был уникальный индекс ключа на поле1, field2, field3.

Идея заключалась в том, чтобы заблокировать часть таблицы в соответствии с индексом и выбрать максимальное значение и использовать max + 1 для вставки в ту же таблицу.

Теперь я пытаюсь перейти на PostgreSql, как это сделать, это postgres? Я не могу сделать

FOR UPDATE 

с агрегированным запросом на выбор. Если я сделаю

LOCK table IN ROW EXCLUSIVE MODE 

будет блокировать всю таблицу или часть таблицы, которая индексируется с использованием полей?

EDIT: НЕ ДЕЛАЕТ ЭТО ДЛЯ ПОСЛЕДОВАТЕЛЬНОСТИ. У меня есть отдельное поле идентификации на столе.

EDIT 2:

id a b(date)  c trace 
1 1 6/1/2016 1 100001 
2 1 6/1/2016 1 100002 
3 1 6/1/2016 1 100003 
4 1 6/1/2016 1 100004 
5 1 6/1/2016 2 100001 
6 1 6/1/2016 2 100002 
7 1 6/1/2016 3 100001 
8 1 6/1/2016 3 100002 

Позволяет сказать, что это таблица, столбец трассировки вращается от 100000 - 999999. Следующая запись была (1, 6/1/2016, 1),

  1. Я хочу, чтобы заблокировать часть таблицы, где а = 1, Ь = 6/1/2016, с = 1.

  2. Получить максимум колонки трассировки из эта часть.

  3. Добавить (1, 6/1/2016, 1, (макс с шага 2) + 1)

  4. Освободить замок.

Когда это происходит, я хочу, чтобы другие вставки и обновления проходили очень хорошо в других частях таблицы.

Могу ли я создать циклическую последовательность для столбца трассировки на основе a, b и c?

EDIT 3: Я хотел бы поблагодарить a_horse_with_no_name за объяснение подхода к использованию триггера. Я многое узнал из этого. Сначала я применил ваше решение, после чего установил отладчик и пробовал некоторые другие вещи, и для меня работало следующее решение.

Я сделал что-то подобное, чтобы достичь того, что искал. Я использую расширение pldbgapi (debugger), чтобы убедиться, что он не блокирует всю таблицу, и не было никаких повторяющихся ошибок ввода.

create or replace function insert_new(IN a_in integer, IN, b_in integer, IN c_in integer) 
    returns integer 
as 
$func$ 
declare new_trace integer; 
begin 
    --this locks the max value until the function ends and other concurrent inserts would wait here until the function holding the lock is done 
    perform trace from my_table where a=a_in and b=b_in and c=c_in order by trace desc limit 1 for update; 
    select trace from my_table where a=a_in and b=b_in and c=c_in into new_trace order by trace desc limit 1; 

    SELECT CASE WHEN new_trace IS 999999 THEN 100001 ELSE new_trace + 1 END INTO new_trace; 
    insert into my_table values(a, b, c, new_trace); 

    return query select new_trace; 
end; 
$func$ 
language plpgsql; 
+0

«* Идея заключалась в том, чтобы зафиксировать часть таблицы в соответствии с индексом и выберите максимальное значение и используйте max + 1 для вставки в ту же таблицу * «сделайте *** НЕ *** сделайте это! Он не будет масштабироваться вообще и будет работать только корректно, если вы действительно заблокируете всю таблицу. Почему, по-вашему, вам это нужно в первую очередь? Почему бы просто не использовать последовательность для генерации идентификаторов? –

+0

Вы слышали о свойстве ['IDENTITY'] (https://msdn.microsoft.com/en-us/library/ms186775.aspx) в SQL Server. –

+0

У меня есть поле идентификации в таблице, это инкрементное поле, о котором я говорю, должно основываться на field1, field2 (это поле даты) и поле3, и оно идет от 1 ... 999999 и снова вращается. Также не пытайтесь полностью заблокировать всю таблицу. Я пытаюсь заблокировать часть таблицы, где field1 = что-то, field2 = something и field3 = что-то, и получить максимальное значение этого инкрементного поля. – dtksmsl

ответ

1

Я не думаю, что есть способ, вы можете добиться того, что без блокировки всей таблицы, как это единственный способ предотвратить параллельные вставки, которые будут портить ваш max() + 1 поиск.

Единственный способ, о котором я могу думать, это использовать какую-то пользовательскую функцию генератора, которая использует отдельную таблицу для генерации значения трассировки.

create table trace_generator 
(
    a_value integer not null, 
    b_value date not null, 
    c_value integer not null, 
    trace_value integer not null default 100000, 
    primary key (a_value, b_value, c_value) 
); 

insert into generator (a_value, b_value, c_value) 
select distinct a,b,c 
from the_table; 

Если вы хотите, вы можете заполнить trace_generator в триггер вставки для базовой таблицы, если новые комбинации (а, б, в) вставлены.

Теперь создадим функцию, которая получает следующее значение трассировки:

create function next_value(p_aval integer, p_bval date, p_cval integer) 
    returns integer 
as 
$$ 
    update trace_generator 
    set trace_value = case 
         when trace_value = 999999 then 100000 
         else trace_value + 1 
         end 
    where a_value = p_aval 
    and b_value = p_bval 
    and c_value = p_cval 
    returning trace_value; 
$$ 
language sql; 

Если вы хотите, чтобы генерировать строки в trace_generator таблице автоматически для новых комбинаций (a,b,c) вы могли бы использовать insert on conflict update внутри этой функции вместо простое обновление.

Это заблокирует строку в таблице generator, но ничего не будет в базовой таблице. Чтобы вставить новую строку со значением трассировки, вы просто вызываете эту функцию. Это будет блокировать все другие операции, которые пытаются генерировать новое значение трассировки:

insert into the_table (a,b,c,trace) 
values (1, date '2016-06-01', 1, next_value(1, date '2016-06-01', 1)); 

Пока вставка не совершала никакой другая транзакция не может вставить строку для этой комбинации, Ь, с. Если вставка зафиксирована, любая ожидающая (следующая) транзакция увидит увеличенное значение. Если вставка откатется, следующая транзакция увидит старое значение.

К сожалению, вы не можете сделать звонок next_value() значением по умолчанию для столбца, поскольку вам не разрешено ссылаться на другие столбцы в функции для значения по умолчанию. Но вы могли бы положить, что в триггер, чтобы автоматизировать этот процесс:

create or replace function assign_trace() 
    returns trigger 
as 
$$ 
begin 
    new.trace_value := next_value(new.a_value, new.b_value, new.c_value); 
    return new; 
end; 
$$ 
language plpgsql; 

create trigger trace_trg 
    before insert on the_table 
    for each row execute procedure assign_trace(); 

Затем вставка может быть упрощена:

insert into the_table (a,b,c) 
values (1, date '2016-06-01', 1); 
+0

Благодарим вас за подробный пример. Я реализовал ваше решение, и оно сработало. Я попробовал что-то еще с блокировкой для обновления и протестировал его, похоже, тоже работает. Я добавил свое решение в EDIT. – dtksmsl

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