Массивное перекрестное соединение в SQL Server 2005

Я портирую процесс, который создает МАССИВНУЮ CROSS JOIN из двух таблиц. Результирующая таблица содержит 15 млн записей (похоже, процесс выполняет перекрестное соединение длиной 30 млн с таблицей из 2600 строк и таблицей из 12000 строк, а затем выполняет некоторую группировку, которая должна разделить ее пополам). Ряды относительно узкие — всего 6 столбцов. Работает уже 5 часов без признаков завершения. Я только что заметил несоответствие счетчика между известным хорошим и тем, что я ожидал бы от перекрестного соединения, поэтому в моем выводе нет группировки или дедупликации, которая уменьшит итоговую таблицу вдвое, но это все еще кажется, что это не собирается завершать какие-либо время скоро.

Сначала я собираюсь исключить эту таблицу из процесса, если это вообще возможно — очевидно, ее можно заменить путем присоединения к обеим таблицам по отдельности, но сейчас я не вижу, где еще она используется.

Но учитывая, что существующий процесс это делает (за меньшее время, на менее мощной машине, используя язык FOCUS), есть ли какие-то варианты повышения производительности больших CROSS JOINов в SQL Server (2005) (аппаратно не вариант, эта коробка 64-битная 8-полосная с 32-гигабайтной оперативкой)?

Подробности:

В FOCUS это написано так (я пытаюсь получить тот же результат, что и CROSS JOIN в SQL):

JOIN CLEAR *
DEFINE FILE COSTCENT
  WBLANK/A1 = ' ';
  END
TABLE FILE COSTCENT
  BY WBLANK BY CC_COSTCENT
  ON TABLE HOLD AS TEMPCC FORMAT FOCUS
  END

DEFINE FILE JOINGLAC
  WBLANK/A1 = ' ';
  END
TABLE FILE JOINGLAC
  BY WBLANK BY ACCOUNT_NO BY LI_LNTM
  ON TABLE HOLD AS TEMPAC FORMAT FOCUS INDEX WBLANK

JOIN CLEAR *
JOIN WBLANK IN TEMPCC TO ALL WBLANK IN TEMPAC
DEFINE FILE TEMPCC
  CA_JCCAC/A16=EDIT(CC_COSTCENT)|EDIT(ACCOUNT_NO);
  END
TABLE FILE TEMPCC
  BY CA_JCCAC BY CC_COSTCENT AS COST CENTER BY ACCOUNT_NO
  BY LI_LNTM
  ON TABLE HOLD AS TEMPCCAC
  END

Таким образом, требуемый результат действительно является CROSS JOIN (он объединяет пустой столбец с каждой стороны).

В SQL:

CREATE TABLE [COSTCENT](
       [COST_CTR_NUM] [int] NOT NULL,
       [CC_CNM] [varchar](40) NULL,
       [CC_DEPT] [varchar](7) NULL,
       [CC_ALSRC] [varchar](6) NULL,
       [CC_HIER_CODE] [varchar](20) NULL,
 CONSTRAINT [PK_LOOKUP_GL_COST_CTR] PRIMARY KEY NONCLUSTERED
(
       [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 [JOINGLAC](
       [ACCOUNT_NO] [int] NULL,
       [LI_LNTM] [int] NULL,
       [PR_PRODUCT] [varchar](5) NULL,
       [PR_GROUP] [varchar](1) NULL,
       [AC_NAME_LONG] [varchar](40) NULL,
       [LI_NM_LONG] [varchar](30) NULL,
       [LI_INC] [int] NULL,
       [LI_MULT] [int] NULL,
       [LI_ANLZ] [int] NULL,
       [LI_TYPE] [varchar](2) NULL,
       [PR_SORT] [varchar](2) NULL,
       [PR_NM] [varchar](26) NULL,
       [PZ_SORT] [varchar](2) NULL,
       [PZNAME] [varchar](26) NULL,
       [WANLZ] [varchar](3) NULL,
       [OPMLNTM] [int] NULL,
       [PS_GROUP] [varchar](5) NULL,
       [PS_SORT] [varchar](2) NULL,
       [PS_NAME] [varchar](26) NULL,
       [PT_GROUP] [varchar](5) NULL,
       [PT_SORT] [varchar](2) NULL,
       [PT_NAME] [varchar](26) NULL
) ON [PRIMARY]

CREATE TABLE [JOINCCAC](
       [CA_JCCAC] [varchar](16) NOT NULL,
       [CA_COSTCENT] [int] NOT NULL,
       [CA_GLACCOUNT] [int] NOT NULL,
       [CA_LNTM] [int] NOT NULL,
       [CA_UNIT] [varchar](6) NOT NULL,
 CONSTRAINT [PK_JOINCCAC_KNOWN_GOOD] PRIMARY KEY CLUSTERED
(
       [CA_JCCAC] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY
= OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

С кодом SQL:

INSERT  INTO [JOINCCAC]
       (
        [CA_JCCAC]
       ,[CA_COSTCENT]
       ,[CA_GLACCOUNT]
       ,[CA_LNTM]
       ,[CA_UNIT]
       )
       SELECT  Util.PADLEFT(CONVERT(varchar, CC.COST_CTR_NUM), '0',
                                     7)
               + Util.PADLEFT(CONVERT(varchar, GL.ACCOUNT_NO), '0',
                                       9) AS CC_JCCAC
              ,CC.COST_CTR_NUM AS CA_COSTCENT
              ,GL.ACCOUNT_NO % 900000000 AS CA_GLACCOUNT
              ,GL.LI_LNTM AS CA_LNTM
              ,udf_BUPDEF(GL.ACCOUNT_NO, CC.COST_CTR_NUM, GL.LI_LNTM, 'N') AS CA_UNIT
       FROM   JOINGLAC AS GL
       CROSS JOIN COSTCENT AS CC

В зависимости от того, как впоследствии будет использоваться эта таблица, ее можно будет исключить из процесса, просто присоединив к обеим исходным таблицам, использованным для ее построения. Тем не менее, это чрезвычайно большие усилия по переносу, и я мог бы не найти использования таблицы в течение некоторого времени, поэтому мне было интересно, есть ли какие-либо приемы для своевременного CROSS JOINing больших таблиц (особенно с учетом того, что существующий процесс в FOCUS это можно сделать быстрее). Таким образом, я мог бы проверить правильность моего построения запроса на замену, а затем разложить его по представлениям или чему-то еще.

Я также рассматриваю возможность выделения UDF и манипуляций со строками и сначала выполнения CROSS JOIN, чтобы немного разбить процесс.

УДАЧНЫЕ РЕЗУЛЬТАТЫ:

Оказывается, пользовательские функции действительно сильно (отрицательно) влияют на производительность. Но также существует большая разница между поперечным соединением 15-метрового ряда и поперечным соединением 30-метрового ряда. У меня нет прав SHOWPLAN (бу-ху), поэтому я не могу сказать, лучше или хуже план, который он использует, после изменения индексов. Я еще не рефакторил его, но ожидаю, что вся таблица скоро исчезнет.


person Cade Roux    schedule 10.12.2008    source источник
comment
МАССИВНОЕ ПЕРЕКРЕСТНОЕ СОЕДИНЕНИЕ Этот синтаксис следует ввести в язык SQL. :)   -  person Amy B    schedule 10.12.2008


Ответы (3)


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

CREATE INDEX COSTCENTCoverCross ON COSTCENT(COST_CTR_NUM)
CREATE INDEX JOINGLACCoverCross ON JOINGLAC(ACCOUNT_NO, LI_LNTM)

Вот мои вопросы для дальнейшей оптимизации:

Когда вы поместите запрос в анализатор запросов и нажмете кнопку «показать предполагаемый план выполнения», он покажет графическое представление того, что он собирается делать.

Тип соединения: там должно быть соединение с вложенным циклом. (другие варианты — объединение слиянием и хеш-соединение). Если вы видите вложенный цикл, то все в порядке. Если вы видите объединение слиянием или хеш-соединение, сообщите нам об этом.

Порядок доступа к столу: пройдите до самого верха и прокрутите до упора вправо. Первым шагом должен быть доступ к таблице. Что это за таблица и какой метод используется (сканирование индекса, сканирование кластеризованного индекса)? Какой метод используется для доступа к другой таблице?

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

Меня беспокоит то, что udf_BUPDEF. Читает ли он из дополнительных таблиц? Util.PADLEFT меня меньше волнует, но все же.. что это такое? Если это не объект базы данных, рассмотрите возможность использования этого:

RIGHT('z00000000000000000000000000' + columnName, 7)

Есть ли триггеры на JOINCCAC? Как насчет индексов? С такой большой вставкой вы захотите удалить все триггеры и индексы в этой таблице.

person Amy B    schedule 10.12.2008
comment
BUPDEF — это огромная функция бизнес-логики, которая была перенесена. Надеюсь, он исчезнет (как и эта таблица), но в нем нет поисковых запросов (или другой пользовательской функции, которую я не упомянул для ясности). - person Cade Roux; 11.12.2008
comment
Производительность UDF была ключевой и ужасной — две UDF могут обрабатывать только около 300 строк в секунду. В настоящее время я занят поиском обходных путей. - person Cade Roux; 26.02.2009

Продолжая то, что говорят другие, функции БД, содержащие запросы, которые используются при выборе, всегда делали мои запросы чрезвычайно медленными. Внезапно я думаю, что запрос выполнялся за 45 секунд, затем я удалил функцию, и тогда результат был 0 секунд :)

Поэтому проверьте, что udf_BUPDEF не выполняет никаких запросов.

person Ben Dempsey    schedule 10.12.2008
comment
Производительность UDF (они скалярны и не обращаются к таблицам) была ключевой и ужасной — две UDF могут обрабатывать только около 300 строк в секунду. В настоящее время я занят поиском обходных путей. - person Cade Roux; 26.02.2009

Разбейте запрос, чтобы сделать его простым перекрестным соединением.


   SELECT  CC.COST_CTR_NUM, GL.ACCOUNT_NO
              ,CC.COST_CTR_NUM AS CA_COSTCENT
              ,GL.ACCOUNT_NO AS CA_GLACCOUNT
              ,GL.LI_LNTM AS CA_LNTM
-- I don't know what is BUPDEF doing? but remove it from the query for time being
--              ,udf_BUPDEF(GL.ACCOUNT_NO, CC.COST_CTR_NUM, GL.LI_LNTM, 'N') AS CA_UNIT
       FROM   JOINGLAC AS GL
       CROSS JOIN COSTCENT AS CC

Видите, насколько хорошо простое перекрестное соединение? (без каких-либо примененных к нему функций)

person shahkalpeshp    schedule 10.12.2008
comment
Если это работает быстро, попробуйте просто выполнить SELECT (с примененными функциями) и посмотрите, все ли в порядке? - person shahkalpeshp; 10.12.2008
comment
Производительность UDF (они скалярны и не обращаются к таблицам) была ключевой и ужасной — две UDF могут обрабатывать только около 300 строк в секунду. В настоящее время я занят поиском обходных путей. - person Cade Roux; 26.02.2009