Оптимизация КЛАСТЕРНОГО ИНДЕКСА для использования с JOIN - PullRequest
1 голос
/ 12 августа 2011

таблица optin_channel_1 (для каждого «канала» есть выделенная таблица)

CREATE TABLE [dbo].[optin_channel_1](
    [key_id] [bigint] NOT NULL,
    [valid_to] [datetime] NOT NULL,
    [valid_from] [datetime] NOT NULL,
    [key_type_id] [int] NOT NULL,
    [optin_flag] [tinyint] NOT NULL,
    [source_proc_id] [int] NOT NULL,
    [date_inserted] [datetime] NOT NULL
) ON [PRIMARY]

CREATE CLUSTERED INDEX [ix_id] ON [dbo].[optin_channel_1] 
(
    [key_type_id] ASC,
    [key_id] ASC,
    [valid_to] ASC,
    [valid_from] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

стол profile_conns

CREATE TABLE [dbo].[profile_conns](
    [profile_key_id] [bigint] NOT NULL,
    [valid_to] [datetime] NOT NULL,
    [valid_from] [datetime] NOT NULL,
    [conn_key_id] [bigint] NOT NULL,
    [conn_key_type_id] [int] NOT NULL,
    [conn_type_id] [int] NOT NULL,
    [source_proc_id] [int] NOT NULL,
    [date_inserted] [datetime] NOT NULL
) ON [PRIMARY]

CREATE CLUSTERED INDEX [ix_id] ON [dbo].[profile_conns] 
(
    [profile_key_id] ASC,
    [conn_key_type_id] ASC,
    [conn_key_id] ASC,
    [valid_to] ASC,
    [valid_from] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

стол lu_channel_conns

CREATE TABLE [dbo].[lu_channel_conns](
    [channel_id] [int] NOT NULL,
    [conn_type_id] [int] NOT NULL,
 CONSTRAINT [PK_lu_channel_conns] PRIMARY KEY CLUSTERED 
(
    [channel_id] ASC,
    [conn_type_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]

таблица lu_conn_type

CREATE TABLE [dbo].[lu_conn_type](
    [conn_type_id] [int] NOT NULL,
    [default_key_type_id] [int] NOT NULL,
    [master_key_type_id] [int] NOT NULL,
    [date_inserted] [datetime] NOT NULL,
 CONSTRAINT [PK_lu_conns] PRIMARY KEY CLUSTERED 
(
    [conn_type_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]

просмотр v_source_proc_id_by_group_id

SELECT DISTINCT x.source_proc_id, x.source_proc_group_id
FROM lu_source_proc x INNER JOIN lu_source_proc_group y ON x.source_proc_group_id = y.group_id

Будет выполнено динамическое выражение SQL:

SET @sql_str='SELECT @ret=MAX(o.optin_flag)
    FROM optin_channel_'+CAST(@channel_id AS NVARCHAR(100))+' o
    INNER HASH JOIN dbo.v_source_proc_id_by_group_id y ON o.source_proc_id=y.source_proc_id AND y.source_proc_group_id=@source_proc_group_id
    INNER HASH JOIN profile_conns z ON z.profile_key_id=cast(@profile_key_id AS NVARCHAR(100)) AND z.conn_key_type_id=o.key_type_id AND z.conn_key_id=o.[key_id] AND z.valid_to=''01.01.3000''
    INNER HASH JOIN lu_channel_conns x ON x.channel_id=@channel_id AND z.conn_type_id=x.conn_type_id
    INNER HASH JOIN lu_conn_type ct ON ct.conn_type_id=x.conn_type_id AND ct.default_key_type_id=o.key_type_id'
SET @param='@channel_id INT, @profile_key_id INT, @source_proc_group_id INT, @ret NVARCHAR(400) OUTPUT'
EXEC sp_executesql @sql_str,@param,@channel_id,@profile_key_id,@source_proc_group_id,@ret OUTPUT

т.е. это дает:

SELECT @ret=MAX(o.optin_flag) AS optin_flag
FROM optin_channel_1 o
INNER HASH JOIN dbo.v_source_proc_id_by_group_id y 
    ON o.source_proc_id=y.source_proc_id 
    AND y.source_proc_group_id=5
INNER HASH JOIN profile_conns z 
    ON z.profile_key_id=1 
    AND z.conn_key_type_id=o.key_type_id 
    AND z.conn_key_id=o.[key_id] 
    AND z.valid_to='01.01.3000'
INNER HASH JOIN lu_channel_conns x 
    ON x.channel_id=1 
    AND z.conn_type_id=x.conn_type_id
INNER HASH JOIN lu_conn_type ct 
    ON ct.conn_type_id=x.conn_type_id 
    AND ct.default_key_type_id=o.key_type_id

Эти таблицы используются для базы данных optin. optin_flag может быть 0 или 1. С последним оператором я хочу получить 1 как optin_flag из optin_channel_1 для данного channel_id=1 для пользователя с profile_key_id=1, когда optin был вставлен в базу данных процессом, принадлежащим source_proc_group_id=5. Надеюсь, этого достаточно, чтобы понять, что происходит.

Это лучший способ использовать CLUSTERED INDEX? Или было бы лучше удалить profile_key_id из индекса на profile_conns и поместить z.profile_key_id=1 в предложение WHERE?

Может быть, есть гораздо лучший способ оптимизировать этот выбор (изменения в схеме базы данных невозможны, только изменения в индексах и операторе модификации).

1 Ответ

3 голосов
/ 12 августа 2011

Не зная размера таблиц и вида хранимых в них данных, их трудно измерить.

Предполагая, что в optin_channel_1 много данных, а в profile_cons много данных, я бы попробовал следующее:

  • Кластерный индекс для optin_channel_1 (key_id) или key_type_id в зависимости от того, какое поле имеет наиболее различимые значения. (поскольку у вас нет индекса покрытия)
  • Кластерный индекс для profile_conns (cons_key_id) или cons_key_type_id в зависимости от того, что вы выбрали в optin_channel_1
  • и т.д ...

По сути, если ваша таблица profile_conns содержит мало данных, я бы поместил кластерный индекс в самое фрагментированное поле «фильтра» (я подозреваю, profile_key_id). Если в таблице много данных, я бы стремился к объединению хеша / слияния и сопоставлял кластеризованный индекс с кластеризованным индексом таблицы optin_channel_1.

Я бы также переписал запрос так:

SELECT @ret = MAX(o.optin_flag) AS optin_flag
  FROM optin_channel_1 o
  JOIN dbo.v_source_proc_id_by_group_id y 
    ON o.source_proc_id = y.source_proc_id  
  JOIN profile_conns z 
    ON z.conn_key_type_id = o.key_type_id 
   AND z.conn_key_id = o.[key_id] 
  JOIN lu_channel_conns x 
    ON z.conn_type_id = x.conn_type_id
  JOIN lu_conn_type ct 
    ON ct.conn_type_id = x.conn_type_id 
   AND ct.default_key_type_id=o.key_type_id 
 WHERE y.source_proc_group_id = 5
   AND z.profile_key_id = 1 
   AND x.channel_id = 1 
   AND z.valid_to = '01.01.3000'

Запрос изменился следующим образом:

  • Помещение условий фильтра в предложение where показывает, какие релевантные поля предназначены для объединения хешем / слиянием
  • Вводить подсказки о соединении редко бывает хорошей идеей. Очень сложно превзойти регулятор запросов, чтобы определить лучший план запроса. Плохой план обычно означает, что у вас есть проблема с вашими индексами / статистикой.

Итак, как резюме:

  • маленькая таблица соединяется с большой таблицей ==> используйте вложенные циклы и сфокусируйте свой кластеризованный индекс на поле «фильтр» в маленькой таблице и поле соединения в большой таблице.
  • большая таблица соединена с большой таблицей => перейти к объединению хеша / слияния и поместить кластерный индекс в совпадающее поле с обеих сторон
  • многополевые индексы обычно являются хорошей идеей, когда они «покрывают», это означает, что все поля, которые вы запрашиваете, включены в индекс. (или включены в предложение include ())
...