В настоящее время я задаюсь вопросом о некоторых различиях в производительности VARCHAR/NVARCHAR, особенно при использовании сложных LIKE-запросов (начинающихся с _ или%).
У меня есть набор тестов на Microsoft SQL Server 2014. У меня 2 таблицы. Оба имеют поле идентификатора (идентификатор (1, 1) и поле значения (либо VARCHAR (450), либо NVARCHAR (450)). Оба имеют идентичные 1'000'000 случайно сгенерированных записей.
Таблицы называются tblVarCharNoIndex и tblNVarCharNoIndex (поэтому индексов нет. Поведение почти одинаково, если я использую индексы).
Теперь я выполняю следующие запросы, проверяя продолжительность (один раз на VARCHAR, один раз на NVARCHAR)
SELECT * FROM tblVarcharNoIndex WHERE Value LIKE '%ab%'
SELECT * FROM tblNVarcharNoIndex WHERE Value LIKE '%ab%'
Время выполнения сильно отличается. Он занимает 1540 мс в таблице VARCHAR и 8630 мс в таблице NVARCHAR, поэтому он занимает более 5 раз дольше с NVARCHAR.
Я понимаю, что NVARCHAR имеет последствия для производительности, поскольку для хранения требуется 2 байта, это имеет смысл. Но я не могу объяснить ухудшение производительности на 500%, это не имеет для меня никакого смысла.
В соответствии с запросом здесь еще несколько Данных.
Запрос для создания таблицы
CREATE TABLE [dbo].[tblVarcharNoIndex](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Value] [varchar](450) NOT NULL,
CONSTRAINT [PK_tblVarcharNoIndex] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[tblNVarcharNoIndex](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Value] [nvarchar](450) NOT NULL,
CONSTRAINT [PK_tblNVarcharNoIndex] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Запрос для создания значений
DECLARE @cnt INT = 0;
DECLARE @entries INT = 1000000 --1'000'000;
DECLARE @maxLength INT = 450;
DECLARE @minLength INT = 50;
DECLARE @value VARCHAR(450)
DECLARE @length INT
WHILE @cnt < @entries
BEGIN
SELECT @value = ''
SET @length = @minLength + CAST(RAND() * (@maxLength - @minLength) as INT)
WHILE @length <> 0
BEGIN
SELECT @value = @value + CHAR(CAST(RAND() * 96 + 32 as INT))
SET @length = @length - 1
END
INSERT INTO tblBase(Value, NValue) VALUES (@value, @value)
SET @cnt = @cnt + 1;
END;
(значения копируются позже из tblBase)
КАК ПРОБЛЕМА
DECLARE @start DATETIME
DECLARE @end DATETIME
DECLARE @testname NVARCHAR(100) = 'INSERT FROM other table'
--VARCHAR No Index
PRINT 'starting ''' + @testname + ''' on VARCHAR (No Index)'
SET @start = GETDATE()
SELECT * FROM tblVarcharNoIndex WHERE Value LIKE '%ab%' --This takes 1540ms
SET @end = GETDATE()
PRINT '-- finished ''' + @testname + ''' on VARCHAR (No Index)'
PRINT '-- Duration ' + CAST(DATEDIFF(mcs, @start, @end) AS VARCHAR(100)) + ' microseconds'
--NVARCHAR No Index
PRINT 'starting ''' + @testname + ''' on NVARCHAR (No Index)'
SET @start = GETDATE()
SELECT * FROM tblNVarcharNoIndex WHERE Value LIKE '%ab%' --This takes 8630ms
SET @end = GETDATE()
PRINT '-- finished ''' + @testname + ''' on NVARCHAR (No Index)'
PRINT '-- Duration ' + CAST(DATEDIFF(mcs, @start, @end) AS VARCHAR(100)) + ' microseconds'
План выполнения План выполнения обоих запросов выглядит абсолютно одинаково (мне некуда загружать изображение прямо сейчас, но это очень просто):
SELECT (0%) < --- Parallelism (Собрать потоки) (3%) < --- Кластеризованный индексный сканер ON Основной ключ (97%)