#sql #sql-server
Вопрос:
У меня есть такая таблица, в которой более 7 миллионов записей:
CREATE TABLE [dbo].[Test]
(
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[UUID] [nvarchar](100) NOT NULL,
[FirstName] [nvarchar](50) NULL,
[LastName] [nvarchar](50) NULL,
[AddrLine1] [nvarchar](100) NULL,
[AddrLine2] [nvarchar](100) NULL,
[City] [nvarchar](50) NULL,
[Prov] [nvarchar](10) NULL,
[Postal] [nvarchar](10) NULL,
[DateAdded] [datetime] NULL,
CONSTRAINT [PK_Test]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
Теперь система ежедневно во второй половине дня выполняет следующий запрос select. Самое забавное, что иногда один и тот же запрос выполняется так медленно, что занимает около 4 минут. Во второй или в другие раз тот же запрос выполняется довольно быстро, что занимает меньше секунды.
Запрос:
WITH testquery AS
(
SELECT TOP 1
'Matched' as location,Firstname, LastName,
AddrLine1, AddrLine2, City, Prov, Postal
FROM
[Test]
WHERE
UUID = 'BLABLABLABLABLABLABLABLABLA'
ORDER BY
DateAdded DESC
),
defaults AS
(
SELECT
'Rejected' AS location, NULL AS Firstname, NULL AS LastName,
NULL AS AddrLine1, NULL AS AddrLine2, NULL AS City, NULL AS Prov,
NULL AS Postal
)
SELECT *
FROM testquery
UNION ALL
SELECT *
FROM defaults
WHERE NOT EXISTS (SELECT * FROM testquery);
Кто-нибудь может помочь, пожалуйста?
Примечания:
- У меня есть сервис, который ежедневно по утрам добавляет в таблицу около 1000 новых записей.
- [avg_fragmentation_in_percent] равно 0,01
- UUID может быть продублирован, если у меня один и тот же человек с разными адресами.
- Таблица не используется одновременно где-то еще.
- База данных не занята другими запросами одновременно. Я проверил с помощью «sys.dm_exec_requests»
Комментарии:
1. Посмотрите на фактический план запроса. Указывает ли это на какие-либо отсутствующие индексы? Возможно, включено (UUID,дата указана), поэтому таблицу не нужно сканировать каждый раз. Также подумайте о загрузке временной таблицы с результатами
testquery
, чтобы не запускать ее дважды.2. У вас нет индекса, который охватывает любой из ваших поисковых запросов;
UUID, DateAdded
… Это означает , что запрос должен просканировать все 7 миллионов записей на предмет соответствияUUID
, а затем отсортировать результаты поDateAdded
, чтобы он мог вернуть единственную запись, которую вы ищете. Это все равно, что я прошу вас сказать мне, какой номер строки словоeither
появляется на всех страницах книги, а затем выбрать страницу, на которой этот номер строки самый высокий. Добавление индексаtest(UUID, DateAdded DESC)
сделает вставки немного медленнее и сделает этот запрос намного быстрее.3. Спасибо. Однако это не помогло, потому что я устал от этого теста(UUID, с указанием даты). Он прекратил его, потому что был найден дубликат ключа. Есть еще какие-нибудь предложения, пожалуйста?
Ответ №1:
Вам нужен хороший индекс, чтобы эффективно обслуживать этот запрос.
Вы говорите, что не можете создать его из-за повторяющихся ключевых ошибок: нет необходимости в уникальности индекса.
Таким образом, тот, который вы ищете, будет зависеть от того, какие другие запросы вы выполняете, но для этого запроса будет достаточно следующего:
CREATE NONCLUSTERED INDEX IX_Test_UuidDate ON
Test (UUID ASC, DateAdded DESC)
INCLUDE (Firstname, LastName, AddrLine1, AddrLine2, City, Prov, Postal)
GO
Кроме того, нет необходимости запрашивать таблицу дважды.
Начните с VALUES
конструктора фиктивной таблицы, чтобы у вас всегда была строка, затем LEFT JOIN
таблица и используйте CASE
ее для решения проблемы отсутствия строки.
WITH testquery AS
(
SELECT TOP 1
*
FROM
[Test]
WHERE
UUID = 'BLABLABLABLABLABLABLABLABLA'
ORDER BY
DateAdded DESC
)
SELECT
CASE WHEN UUID IS NULL 'Rejected' ELSE 'Matched' END as location,
t.Firstname,
t.LastName,
t.AddrLine1,
t.AddrLine2,
t.City,
t.Prov,
t.Postal
FROM (VALUES(0)) AS v(dummy)
LEFT JOIN testquery AS t ON 1=1;
Ответ №2:
Обычным объяснением этого является холодный кэш. В вашем случае, я думаю, проблема была бы ORDER BY
в первом CTE.
Чтобы устранить эту проблему, вам нужен индекс test(UUID, DateAdded desc)
.
Я не уверен, почему это ускорится после первого выполнения. Возможно, кэш сервера работает особенно хорошо.
Комментарии:
1. Спасибо. Однако это не помогло, потому что я устал от этого теста(UUID, с указанием даты). Он прекратил его, потому что был найден дубликат ключа. Есть еще какие-нибудь предложения, пожалуйста?
2. @Ribaz . . . Индекс допускает дубликаты. Вы либо определяете уникальный индекс/ограничение, либо ограничение внешнего ключа.