web-dev-qa-db-ja.com

nvarchar(max)のvarcharへの変換とテーブルの最適化

すべての文字タイプがnvarcharに設定されているテーブルを使用しています。それらの一部はnvarchar(max)です。これらすべてをvarcharに変換し、本番環境での実際の使用に基づいて文字幅を指定しています。本番データでは、任意の列に実際に使用されている幅の2文字から最大900文字の範囲が使用されます。該当する場合は、10%のパディングを追加します。

_-- Insert statements for procedure here
UPDATE Listings WITH (ROWLOCK) 
SET [SubType] = 'S' 
WHERE @idSettings = idSettings AND
    (@idRetsClass = 0 OR idRetsClass = @idRetsClass)
    AND (@idRetsSetting = 0 OR idRetsSetting = @idRetsSetting)
    AND IsNew = 1 AND ([SubType] LIKE '%Single Family Home%' OR [SubType] LIKE '%Modular%' OR [SubType] LIKE '%Mobile Home%' 
    OR [SubType] LIKE '% Story%' OR [SubType] = '' OR [SubType] = 'residential - S' OR [SubType] = '1 House on Lot' OR [SubType] = '2 Houses on Lot' 
    OR [SubType] = 'Detached' OR [SubType] LIKE '%single family%' OR [SubType] = 'ranch' OR [SubType] = 'Semi-Detached' OR [SubType] = 'single' OR [SubType] = 'one family' OR [SubType] = 'Residential' 
    OR [SubType] = 'Ranch Type' OR [SubType] = '2 or More Stories' OR [SubType] = 'Cape Cod' OR [SubType] = 'Split Level' OR [SubType] = 'Bi-Level' OR [SubType] = 'Detached Single' 
    OR [SubType] = 'Single-Family Homes' OR [SubType] = 'house' OR [SubType] = 'detached housing'  OR [SubType] = 'det')
_

文字通り140(nvarchar)列で構成されるこのテーブルの大規模なオーバーホール。11はMAXです。 30個のインデックスを削除して、後で再作成しています。

私の質問は、どのような場合にvarchar(max)が推奨されますか?

4k以上の文字があると予想される場合のみ?

これを行う場合、何を学び、準備する必要がありますか?

これにより、クラスター化キーに影響するクラスター化インデックスの更新ですべての非クラスター化インデックスを更新する必要がある場合に、パフォーマンスが向上しますか?

クラスター化インデックスの更新に クエリ実行プラン表示されるプラン の75%から95%を使用している更新手順がタイムアウトしています。

実際の実行プランへのリンク

7
Keith Beard

どのような状況でvarchar(max)が望ましいですか

コメンターはこの点についてすでに詳細に述べています。 VARCHAR(MAX)は、列が非ASCII文字を必要とせず、列の最大長が不明または8,000文字を超えることが100%確実である場合に、一般的に意味があると思います。同様の質問については https://stackoverflow.com/questions/7141402/why-not-use-varcharmax を読むことができます。

アップデート手順がタイムアウトしています

実行計画に基づいて、更新のパフォーマンスに影響を与える主な要因は、更新される列にフルテキストインデックスがあり、そのフルテキストインデックスにCHANGE_TRACKING = AUTOを使用していることだと思います。

この回答の下部にあるスクリプトは、適度な数の行に対する単純な更新ステートメントを示しています。このようなフルテキストインデックスを追加するだけで、パフォーマンスは19ミリ秒から500ミリ秒を超えます。

全文検索のビジネスニーズに応じて、CHANGE_TRACKING = OFFを使用して全文索引を作成し(このオーバーヘッドは発生しません)、定期的にALTER FULLTEXT INDEX...START FULL POPULATIONまたはSTART INCREMENTAL POPULATIONを実行して、テーブルのデータを全文に同期できます。 -テキストインデックス。こちらのBOLの記事を参照してください。 https://msdn.Microsoft.com/en-us/library/ms187317.aspx

-- Create test data on a new database
CREATE DATABASE TestFullTextUpdate
GO
USE TestFullTextUpdate
GO
CREATE TABLE dbo.fulltextUpdateTest (
    id INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_fulltextUpdateTest PRIMARY KEY,
    object_counter_name NVARCHAR(512) NOT NULL
)
GO

--13660 row(s) affected
INSERT INTO dbo.fulltextUpdateTest (object_counter_name)
SELECT object_name + ': ' + counter_name
FROM sys.dm_os_performance_counters
CROSS JOIN (SELECT TOP 10 * FROM master..spt_values) x10
GO

-- ~19ms per update
DECLARE @startDate DATETIME2 = GETDATE()
SET NOCOUNT ON
UPDATE dbo.fulltextUpdateTest SET object_counter_name = object_counter_name
SET NOCOUNT OFF
DECLARE @endDate DATETIME2 = GETDATE()
PRINT(DATEDIFF(ms, @startDate, @endDate))
GO 10

-- Add a fulltext index with the default change-tracking behavior
CREATE FULLTEXT CATALOG DefaultFulltextCatalog AS DEFAULT AUTHORIZATION dbo
GO
CREATE FULLTEXT INDEX ON dbo.fulltextUpdateTest (object_counter_name)
KEY INDEX PK_fulltextUpdateTest
WITH CHANGE_TRACKING = AUTO
GO

-- ~522ms per update
-- Execution plan, like the plan in your question, shows that we must
-- touch the fulltext_index_docidstatus for each row that is updated
DECLARE @startDate DATETIME2 = GETDATE()
SET NOCOUNT ON
UPDATE dbo.fulltextUpdateTest SET object_counter_name = object_counter_name
SET NOCOUNT OFF
DECLARE @endDate DATETIME2 = GETDATE()
PRINT(DATEDIFF(ms, @startDate, @endDate))
GO 10

-- Cleanup
USE master
GO
DROP DATABASE TestFullTextUpdate
GO
4
Geoff Patterson