После применения данных предложений, а также после разговора с нашим администратором базы данных, победившей идеей было отказаться от слияния и использовать логические условия над циклом.
Добавление start / commit, по-видимому, сокращает время выполнения на 1,5–3 секунды.
Добавление первичного ключа к целевой таблице дало наилучшее сокращение, сократив время выполнения примерно до 13 секунд.
Преобразование слияния в условную логику в этом случае было наилучшим вариантом, достигая результата примерно за 8 секунд.
При использовании условных выражений первичный ключ в целевой таблице наносит небольшой ущерб (около 1 секунды), но впоследствии его существенное сокращение значительно сокращает время, поскольку эта таблица является лишь предыдущим шагом для большого соединения. (То есть результат этого слияния записей последний раз используется в объединении с 11+ таблицами.) Поэтому я сохранил P.K.
Поскольку кажется, что не существует решения без цикла курсора, я использовал условные выражения для объединения значений с использованием переменных и выдачи только вставок в целевую таблицу, что устраняет необходимость поиска записи для обновления или проверки ее существования. .
Вот упрощенный пример.
create table #source_t(account varchar(10), event varchar(10));
Insert into #source_t(account, event) values ('account1','event 1');
Insert into #source_t(account, event) values ('account1','event 2');
Insert into #source_t(account, event) values ('account1','event 3');
Insert into #source_t(account, event) values ('account2','came');
Insert into #source_t(account, event) values ('account2','saw');
Insert into #source_t(account, event) values ('account2','conquered');
create table #target(
account varchar(10), -- make primary key if the result is to be joined afterwards.
event_list varchar(2048)
);
declare ciclo cursor for
select account, event
from #source_t c
order by account --,...
for read only;
declare @account varchar(10), @event varchar(40), @last_account varchar(10), @event_list varchar(1000)
open ciclo
fetch ciclo into @account, @event
set @last_account = @account, @event_list = null
begin tran
while @@sqlstatus = 0 BEGIN
if @last_account <> @account begin -- if current record's account is different from previous, insert into table the concatenated event string
insert into #target(account, event_list) values (@last_account, @event_list)
set @event_list = null -- Empty the string for the next account
end
set @last_account = @account -- Copy current account to the variable that holds the previous one
set @event_list = case @event_list when null then @event else @event_list + ' | ' + @event end -- Concatenate events with separator
fetch ciclo into @account, @event
END
-- after the last fetch, @@sqlstatus changes to <> 0, the values remain in the variables but the loop ends, leaving the last record unprocessed.
insert into #target(account, event_list) values (@last_account, @event_list)
commit tran
close ciclo
deallocate cursor ciclo;
select * from #target;
drop table #target;
drop table #source_t;
Результат:
account |event_list |
--------|---------------------------|
account1|event 1 | event 2 | event 3|
account2|saw | came | conquered |
Этот код работал достаточно быстро в моем реальном случае использования. Однако его можно было бы дополнительно оптимизировать, отфильтровав исходную таблицу, чтобы она содержала только те значения que, которые необходимы для последующего объединения. В связи с этим я сохранил окончательный объединенный набор результатов (за исключением объединения с #target) в другой временной таблице, оставив некоторые столбцы пустыми. Затем #source_t был заполнен с использованием только учетных записей, присутствующих в наборе результатов, обработал его до #target и, наконец, использовал #target для обновления конечного результата. При этом время выполнения для производственной среды сократилось примерно до 8 секунд (включая все этапы).
Решения UDF уже решали эту проблему для меня раньше, но в ASE 15 они должны быть привязаны к таблице и должны написать одну функцию на столбец. Кроме того, это возможно только в среде разработки, не авторизованной для производства из-за привилегий только для чтения.
В заключение, цикл курсора в сочетании с оператором слияния представляет собой простое решение для объединения записей с использованием конкатенации определенных значений. Для повышения производительности требуется первичный ключ или индекс, включая столбцы, используемые для сопоставления.
Условная логика приводит к еще большей производительности, но идет на более сложный код (чем больше кода, тем больше вероятность ошибок).