web-dev-qa-db-ja.com

カーソルが必要で、列ストアクラスター化インデックスがある場合の対処法

ColumnStoreクラスター化インデックスを持つ非常に大きなSQL Serverテーブルがあります。テーブルのクエリは非常に高速であるため、これは適切です。ただし、テーブル内のレコードの大部分を更新する必要があります。従来の更新ステートメントを試したところ、トランザクションログがいっぱいになりました。通常は、カーソルを使用して複合キーの追加部分を使用することでこれを処理します。これにより、バッチを更新し、ループ内で更新ステートメントを実行できます。ただし、クラスター化されたインデックスを持つColumnStoreテーブルでは機能しないことをエラーメッセージで知りました。

誰かがデータを更新するための代替方法を提案できますか?

トランザクションログをオフにするだけですか?それが心配です。

実行できないことの1つは、トランザクションログに領域を追加することです。それはすでにかなり大きいです。

declare @ig as cursor;
declare @pname varchar(200)

set @ig = cursor for 
select distinct injection_group from mytable where 
ProductGroup = 'Widget'
and target_month = '1/1/2018'
open @ig;

fetch next from @ig into @pname;

while @@FETCH_STATUS = 0
begin
    print @pname;
    update 
    mytable
    set injection_group = replace(injection_group, '  ', ' ')
    where productgroup = 'Widget'
    and target_month = '1/1/2018'
    and injection_group = @pname
    fetch next from @ig into @pname;
end

close @ig;
deallocate @ig;

メッセージ35370、レベル16、状態1、行5のカーソルは、クラスター化列ストアインデックスを持つテーブルではサポートされていません。

2
RDScience

この答えはSQL Server 2016の観点から書かれていますが、重要な詳細のほとんどは同じだと思います。

トランザクションログの観点から見ると、更新は非常に高価です。これらは、削除+挿入として舞台裏で実装されます。削除では、行あたりのトランザクションログコストが固定されています。私が正しく覚えていれば、削除された100万行あたり約200 MBです。挿入できるのはデルタストアだけなので、データはヒープとして書き込まれます。これらのヒープは、2016年には圧縮解除され、2014年にはページ圧縮されます。

列ストアインデックスに対する更新ではなく、削除+挿入を行うのは ベストプラクティス です。テーブルのほとんどの行を本当に処理している場合は、すべての新しいデータを新しいコピーに挿入し、完了したらテーブルを切り替えることができます。挿入されたほぼすべての行は、デルタストアをバイパスし、代わりに圧縮された行グループに書き込まれます。圧縮された行グループにデータを書き込む場合は、デルタストアへの書き込みに比べて、必要なトランザクション書き込みが大幅に少なくなります。

SQL Server 2016では、このようなアプローチでパフォーマンスを大幅に向上させることができます。 Nikoの記事によると、SQL Server 2014についても同様です。

2
Joe Obbish
DECLARE
@LargestKeyProcessed varchar(200) = 'A',
@NextBatchMax varchar(200),
@RC INT = 1;

WHILE (@RC > 0)
BEGIN

SELECT distinct TOP (1) @NextBatchMax = injection_group
FROM mytable where 
productgroup = 'Widget'
and target_month = '1/1/2018'  
and injection_group > @LargestKeyProcessed
ORDER BY injection_group ASC;

print @NextBatchMax;

update 
mytable
set injection_group = replace(injection_group, '  ', ' ')
where productgroup = 'Widget'
and target_month = '1/1/2018'
and injection_group = @NextBatchMax

SET @RC = @@ROWCOUNT;
SET @LargestKeyProcessed = @NextBatchMax;

END
0
RDScience