すべての文字タイプが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%を使用している更新手順がタイムアウトしています。
どのような状況で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