SQL Server не использует индекс в хранимой процедуре

Я использую Mediawiki 1.13.3, и это работает:

#Item1
Somestuff
<ol start="2">
<li>Item2 </li>
</ol>
11
задан Jan Jongboom 8 October 2009 в 08:47
поделиться

10 ответов

[EDIT]

The PERSISTED-not-being-used issue below occurs only with actieGroep/actieId on my system (SQL 2008). But it's possible that the same problem could be happening on your SQL 2005 system with the dagnummer/datum columns as well. If indeed that's happening, it would explain the behavior you're seeing, since a clustered index scan would be required to filter for values of datum. To diagnose whether this is indeed happening, simply add the datum column as an INCLUDE-d column to your index, like this:

CREATE NONCLUSTERED INDEX [IX_STAT_Statistieken_1] ON [dbo].[STAT_Statistieken]  
(  
    [foreignId] DESC,  
    [dagnummer] DESC,  
    [actieId] ASC,   
    [aantal] ASC    
) INCLUDE (datum)  ON [PRIMARY]

If the problem goes away with this index revision, then you know that dagnummer is the issue-- you can probably even remove dagnummer from the index since SQL isn't using it anyways.

Also, revising your index to add actieId is a good idea since it evades the issue noted below. But in the process you also need to leave the aantal column in the index, so that your index will be a covering index for this query. Otherwise SQL will have to read your clustered index to get the value of that column. This will slow down your query since lookups into the clustered index are quite slow.

[КОНЕЦ РЕДАКТИРОВАНИЯ]

Вот несколько идей, которые могут помочь вам исправить это, с наиболее вероятными / простыми вещами в первую очередь:

  • Когда я пытался воспроизвести вашу схему и запросы (с сгенерированными поддельными data), я вижу, что ваш PERSISTED вычисляемый столбец actieGroep повторно копируется во время выполнения, а не используется постоянное значение. Это похоже на ошибку в оптимизаторе SQL Server. Поскольку значение базового столбца actieGroep отсутствует в покрывающем индексе IX_STAT_Statistieken_1 index (присутствует только вычисляемый столбец), если SQL Server решит, что ему необходимо получить этот дополнительный столбец, SQL может рассмотреть кластеризованный индекс для будет дешевле, чем использование некластеризованного индекса и последующий поиск actieId для каждой соответствующей строки в кластерном индексе. Это связано с тем, что поиск по кластеризованному индексу очень дорого по сравнению с последовательным вводом-выводом, поэтому любой план, требующий поиска более нескольких процентов строк, вероятно, дешевле выполнить со сканированием. В любом случае, если это действительно проблема, с которой вы столкнулись, добавление actieGroep в качестве столбца INCLUDE-d вашего индекса IX_STAT_Statistieken_1 должно помочь обойти проблему. Примерно так:

    СОЗДАТЬ НЕКЛАССИВНЫЙ ИНДЕКС [IX_STAT_Statistieken_1] НА [dbo]. [STAT_Statistieken]
    (
    [foreignId] DESC,
    [secondaryId] ASC,
    [actieGroep] ASC,
    [dagnummer] DESC,
    [aantal] ASC
    ) INCLUDE (actieId) ON [PRIMARY]

  • тип данных вычисляемого столбца actieGroep является строкой, но вы сравниваете ее с целыми числами (например, IN (1,2,3)) в предложении WHERE и операторах CASE. Если SQL решит преобразовать столбец вместо константы, это повлияет на производительность запроса и может сделать проблему расширения вычисляемого столбца (описанную выше) более вероятной. Я настоятельно рекомендую изменить определение вычисляемого столбца на целочисленный тип, например

    CASE WHEN actieId МЕЖДУ 0 И 9 THEN actieId
    КОГДА actieId МЕЖДУ 10 И 99 ТОГДА actieId / 10
    КОГДА actieId МЕЖДУ 100 И 999 ТОГДА actieId / 100
    КОГДА actieId МЕЖДУ 1000 И 9999, ТО actieId / 1000
    WHEN actieId BETWEEN 10000 AND 99999 THEN actieId/10000
    WHEN actieId BETWEEN 100000 AND 999999 THEN actieId/100000
    WHEN actieId BETWEEN 1000000 AND 9999999 THEN actieId/1000000
    ELSE actieId/10000000 END

  • you're doing a GROUP BY a column which only has one possible value. Therefore, the GROUP BY is unnecessary. Hopefully the optimizer would be smart enough to know this, but you can never be sure.

  • Try using an OPTIMIZE FOR hint instead of directly forcing indexes, that may work around the error you get with your hint

  • Craig Freedman's post http://blogs.msdn.com/craigfr/archive/2009/04/28/implied-predicates-and-query-hints.aspx which describes common causes of the hint-related error message that you're getting when RECOMPILE is used. You may want to review that post and make sure you're running the latest updates to SQL Server.

  • I'm sure you've already done this, but you may want to build a "clean room" version of your data, by doing what we're doing: creating a new DB, use the DDL in your question to create the tables, and then populating the tables with data. If the results you get are different, look closley at the schema in your real table and indexes, and see if they're different.

If none of this works, comment and I can suggest some more wild ideas. :-)

Also, please add the exact version and update level of SQL Server to your question!

7
ответ дан 3 December 2019 в 05:58
поделиться

это может быть анализ параметров, поэтому попробуйте что-нибудь вроде этого:

ALTER PROCEDURE MyProcedure (@fid BIGINT)
AS BEGIN
    DECLARE @fid_sniff BIGINT
    SET @fid_sniff=@fid
    SELECT foreignId
    FROM STAT_Statistieken
    WHERE foreignId = @fid_sniff
END

подробнее об обнаружении выходных параметров: http://omnibuzz-sql.blogspot.com/2006/11/parameter-sniffing-stored-procedures.html

2
ответ дан 3 December 2019 в 05:58
поделиться

Какой тип данных является foreignId в таблице? Если это int, вы, вероятно, получаете неявное преобразование, которое предотвращает поиск по индексу. Если тип данных в таблице - int, тогда переопределите параметр на int, и вы должны получить поиск по индексу (а не сканирование индекса) для этого запроса.

6
ответ дан 3 December 2019 в 05:58
поделиться

Полученное вами сообщение об ошибке может быть сгенерировано, если в вашем запросе есть конфликтующие подсказки.

Можете ли вы выполнить запрос, включая подсказку, вне хранимой процедуры?

Альтернативный ход мыслей, вы тестировали / запускали свою хранимую процедуру с другим значением параметра? Возможно, значение параметра, использованное для создания исходного плана выполнения, не подходит для всех действий. Вы можете захотеть перекомпилировать свою хранимую процедуру, чтобы увидеть, создается ли другой план выполнения между разными запусками с разными параметрами.

Если вы хотите, чтобы новый план запроса вычислялся для каждого выполнения вашей хранимой процедуры, вы можете используйте предложение WITH RECOMPILE. Это должно быть исключением, а НЕ нормой. Подтвердите поведение вашей процедуры и создание плана с помощью тестирования.

1
ответ дан 3 December 2019 в 05:58
поделиться

Попробуйте это и получите результат:

DBCC FLUSHPROCINDB: Используется для очистки кеша хранимых процедур для конкретной базы данных на SQL Server, а не всего SQL Server. Идентификационный номер базы данных, на которую будет воздействовать, необходимо ввести как часть команды.

Вы можете использовать эту команду перед тестированием, чтобы убедиться, что предыдущие планы хранимых процедур не повлияют отрицательно на результаты тестирования.

Пример:

] DECLARE @intDBID INTEGER SET @intDBID = (ВЫБРАТЬ dbid ИЗ master.dbo.sysdatabases WHERE name = 'database_name') DBCC FLUSHPROCINDB (@intDBID)

1
ответ дан 3 December 2019 в 05:58
поделиться

Когда вы передаете параметр, сколько строк в таблице соответствует JOIN относительно общего количества строк в таблице? SQL Server выбирает индекс, используя, среди прочего, соотношение совпадающих строк, возвращаемых JOIN, к общему количеству строк в таблице. Если имеется большое количество возвращаемых строк относительно общего числа в таблице, индекс может быть проигнорирован как индексы предпочтений SQL Server, где количество совпадающих строк меньше по сравнению с общим числом.

Итак, если ваш SELECT и ваш Stored Вызов процедуры использовал разные значения для @fid, тогда вы можете иногда использовать индекс, а иногда нет.

0
ответ дан 3 December 2019 в 05:58
поделиться

Я видел подобное поведение раньше, когда он фактически принимал подсказку индекса и сделать с ним что-нибудь похуже (сканирование нефильтрованного индекса с поиском по закладкам).

Один из этих четырех должен помочь:

1) Добавить; -T4102; -T4118 к параметрам запуска SQL Server 2005 (может применяться к SQL 2008) . Примечание: это возвращает SQL 2000 неправильную обработку запросов IN и NOT IN в SQL 2005.

2) UPDATE STATISTICS [dbo]. [STAT_Statistieken] WITH FULLSCAN

3) ОПЦИЯ (MAXDOP 1) - иногда параллелизм приводит к генерации действительно глупых запросов

4) Убедитесь, что индекс находится в оперативном режиме.

Также обратите внимание, что если вы создаете индекс в таблице, созданной в хранимой процедуре, этот индекс не существует при компиляции запросов хранимой процедуры, поэтому он не будет использоваться. Поскольку ваша таблица создается глобально в dbo, я предполагаю, что здесь это НЕ.

РЕДАКТИРОВАТЬ: иногда мне хотелось бы, чтобы был настоящий форс-план, где вы могли бы ввести план напрямую, и любой возможный план будет выполнен: своего рода сборка -подобный язык для БД.

1
ответ дан 3 December 2019 в 05:58
поделиться

Во-первых, я должен сказать, что созданные вами индексы не оптимальны, поскольку их можно использовать только для фильтрации по foreignId .

SQL Server не может выполнить SKIP SCAN , и у вас есть secondaryId в вашем индексе, который не фильтруется с условием диапазона.

Следовательно, ваше условие на foreignId, actieGroep , dagNummer не дает ограниченного числа диапазонов и не может быть полностью выделен. Он может фильтровать только по foreignID , а не по всему набору.

Теперь, к вашему текущему индексу.

Я только что создал ваши таблицы и заполнил их случайными данными с помощью этого скрипта:

DROP TABLE STAT_Statistieken

CREATE TABLE [dbo].[STAT_Statistieken](
    [statistiekId] [bigint] IDENTITY(1,1) NOT NULL,
    [foreignId] [bigint] NOT NULL,
    [datum] [datetime] NOT NULL, --date
    [websiteId] [int] NOT NULL,
    [actieId] [int] NOT NULL, --actionId
    [objectSoortId] [int] NOT NULL, --kindOfObjectId
    [aantal] [bigint] NOT NULL, --count
    [secondaryId] [int] NOT NULL DEFAULT ((0)),
    [dagnummer]  AS (datediff(day,CONVERT([datetime],'2009-01-01 00:00:00.000',(121)),[datum])) PERSISTED, --daynumber
    [actieGroep]  AS (substring(CONVERT([varchar](4),[actieId],0),(1),(1))) PERSISTED,
    CONSTRAINT [STAT_Statistieken_PK] PRIMARY KEY CLUSTERED --actionGroup
    (
        [statistiekId] ASC
    )WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
    ) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_STAT_Statistieken_1] ON [dbo].[STAT_Statistieken] 
(
    [foreignId] DESC,
    [secondaryId] ASC,
    [actieGroep] ASC,
    [dagnummer] DESC, 
    [aantal] ASC --count
)WITH (PAD_INDEX  = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF,  ONLINE = OFF) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_STAT_Statistieken_2] ON [dbo].[STAT_Statistieken] 
(
    [foreignId] DESC,
    [secondaryId] ASC,
    [actieId] ASC,
    [dagnummer] DESC,
    [aantal] ASC -- count
)WITH (PAD_INDEX  = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]

;WITH    nums AS
        (
        SELECT  1 AS num
        UNION ALL
        SELECT  num + 1
        FROM    nums
        )
INSERT
INTO    STAT_Statistieken (
        [foreignId], [datum], [websiteId], [actieId],
        [objectSoortId], [aantal])
SELECT  TOP 100000
        500, GETDATE(), num, num, num, num % 5
FROM    nums
UNION ALL
SELECT  TOP 100000
        num % 1000, GETDATE(), num, num, num, num % 5
FROM    nums
OPTION (MAXRECURSION 0)

UPDATE STATISTICS STAT_Statistieken

], и он использует INDEX SEEK независимо от того, что, скорее всего, означает, что проблема связана с вашим распределением данных.

I '

2
ответ дан 3 December 2019 в 05:58
поделиться

Попробуйте создать свой индекс следующим образом:

CREATE NONCLUSTERED INDEX [IX_STAT_Statistieken_2] ON [dbo].[STAT_Statistieken] 
(
    [foreignId] DESC,
    [secondaryId] ASC,
    [actieId] ASC,
    [dagnummer] DESC,
    [aantal] ASC -- count
)
INCLUDE (actieGroep);       
WITH (PAD_INDEX  = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]

А потом воссоздайте процедуру

0
ответ дан 3 December 2019 в 05:58
поделиться
select AU.*
FROM SYS.Allocation_units AS AU
INNER JOIN SYS.Partitions AS P
ON AU.Container_id = P.Partition_id
WHERE Object_ID = object_id('STAT_Statistieken')

Попробуйте это и проверьте, содержит ли НЕКЛАСТЕРНЫЙ ИНДЕКС больше страниц, чем КЛАСТЕРНЫЙ ИНДЕКС (ЭТО ОЗНАЧАЕТ, ЧТО ДЕШЕВЛЕ ДЛЯ ЧТЕНИЯ КЛАСТЕРИРОВАННОГО ИНДЕКСА)

0
ответ дан 3 December 2019 в 05:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: