Создание первичного ключа для временной таблицы - когда?

У меня есть хранимая процедура, которая работает с большим объемом данных. У меня эти данные вставляются во временную таблицу. Общий поток событий выглядит примерно так:

CREATE #TempTable (
    Col1    NUMERIC(18,0) NOT NULL,    --This will not be an identity column.
    ,Col2   INT NOT NULL,
    ,Col3   BIGINT,

    ,Col4   VARCHAR(25) NOT NULL,
    --Etc...

    --
    --Create primary key here?
)


INSERT INTO #TempTable
SELECT ...
FROM MyTable
WHERE ...

INSERT INTO #TempTable
SELECT ...
FROM MyTable2
WHERE ...

--
-- ...or create primary key here?

У меня вопрос: когда лучше всего создать первичный ключ в моей таблице #TempTable? Я предположил, что я должен создать ограничение / индекс первичного ключа после вставки всех данных, потому что индекс должен быть реорганизуется по мере создания информации о первичном ключе. Но я понял, что мое подчеркивающее предположение могло быть неверным ...

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

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

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


person Frank V    schedule 22.06.2009    source источник


Ответы (9)


Это зависит от многого.

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

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

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

Однако, как отмечали другие, создание индекса может завершиться ошибкой. Кроме того, временная таблица не существует изолированно. Предположительно, есть лучший индекс для чтения данных из него для следующего шага. Этот индекс должен быть либо на месте, либо создан. Здесь вы должны найти компромисс между скоростью здесь и надежностью (сначала примените PK и любые другие ограничения), а потом скоростью (имейте хотя бы кластерный индекс, если вы собираетесь его использовать. ).

person Cade Roux    schedule 22.06.2009
comment
Интересный. Спасибо. Полезный. Не могли бы вы развить несколько примеров? - person Frank V; 23.06.2009
comment
@Cade, для кластерного индекса, вы имеете в виду физический порядок на диске, а не логический порядок (таблицы не имеют логического порядка). - person Peter Radocchia; 23.06.2009
comment
Нет, физический порядок на диске может быть любым. Кластеризованный индекс - это просто данные, хранящиеся в листьях в индексе btree, а не в куче. В SQL Server и на диске еще может быть фрагментация. - person Cade Roux; 23.06.2009
comment
Я понимаю что ты имеешь ввиду. Дело принято. - person Peter Radocchia; 23.06.2009

Если модель восстановления вашей базы данных настроена на простую или с неполным протоколированием, SELECT ... INTO ... UNION ALL может быть самым быстрым решением. SELECT .. INTO - это массовая операция, и массовые операции минимально регистрируются.

eg:

-- first, create the table
SELECT ...
INTO #TempTable
FROM MyTable
WHERE ...
UNION ALL
SELECT ...
FROM MyTable2
WHERE ...

-- now, add a non-clustered primary key:
-- this will *not* recreate the table in the background
-- it will only create a separate index
-- the table will remain stored as a heap
ALTER TABLE #TempTable ADD PRIMARY KEY NONCLUSTERED (NonNullableKeyField)

-- alternatively:
-- this *will* recreate the table in the background
-- and reorder the rows according to the primary key
-- CLUSTERED key word is optional, primary keys are clustered by default
ALTER TABLE #TempTable ADD PRIMARY KEY CLUSTERED (NonNullableKeyField) 

В остальном у Кейда Ру был хороший совет: до или после.

person Peter Radocchia    schedule 23.06.2009

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

person Justin    schedule 22.06.2009
comment
Я не буду использовать личность. Вы разместили это, пока я обновлял свой вопрос. Мой первичный ключ будет состоять из полей NUMERIC (18,0) и VARCHAR (25). - person Frank V; 23.06.2009
comment
Первичные ключи по умолчанию кластеризованы. Это упорядочит ваши данные в последовательном порядке на основе значений PK. Я согласен с этим ответом, это должно произойти до того, как вы вставите. Также обратите внимание: если вы добавите дополнительные некластеризованные индексы. Создание кластеризованного PK после приведет к тому, что SQL Server перестроит некластеризованные индексы. - person DBAndrew; 23.06.2009
comment
@Justin Как я могу создать первичный ключ перед вставками. Можете ли вы дополнить свое предложение небольшим примером запроса? - person itro; 21.05.2019

Еще более важно, чем соображения производительности, если вы не АБСОЛЮТНО, на 100% уверены, что в таблицу будут вставлены уникальные значения, сначала создайте первичный ключ. В противном случае создать первичный ключ не удастся.

Это предотвращает вставку повторяющихся / неверных данных.

person Jeff Meatball Yang    schedule 22.06.2009
comment
Для меня это не проблема. Я понимаю, что для некоторых это может быть проблемой, но для меня это не проблема. - person Frank V; 23.06.2009
comment
Я думаю, что это имеет смысл только потому, что он более четко демонстрирует то, что вы логически пытаетесь сделать, и позволяет движку помочь вам, когда предположения, которые раньше были безопасными (то есть уверенность в том, что в таблице после вставки не будет повторяющихся данных) больше небезопасно (например, в результате некоторых изменений в другой части базы данных). - person binki; 11.01.2018

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

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

Но, возможно, у Sql Server есть действительно умный способ проверки уникальности. Так что, если хочешь быть уверенным, измерь!

person Andomar    schedule 22.06.2009
comment
У SQL Server действительно есть действительно умный способ проверки уникальности. - person Geoff Griswald; 07.04.2020

Мне было интересно, могу ли я улучшить очень-очень «дорогую» хранимую процедуру, влекущую за собой кучу проверок при каждой вставке в таблицы, и наткнулся на этот ответ. В Sproc открыто несколько временных таблиц, которые ссылаются друг на друга. Я добавил первичный ключ в оператор CREATE TABLE (хотя в моем выборе используются операторы WHERE NOT EXISTS для вставки данных и обеспечения уникальности), и мое время выполнения было СОВЕРШЕННО сокращено. Я настоятельно рекомендую использовать первичные ключи. Всегда хотя бы пробуйте это делать, даже если думаете, что вам это не нужно.

person Community    schedule 11.08.2012
comment
Контекст вопроса заключался не столько в том, создавать ли первичный ключ или нет, а в том, в какой момент времени это более эффективно - заполнение до данных или заполнение после данных. До этого вопроса был сделан вывод, что ключ / индекс помогает сократить время выполнения. - person Frank V; 14.08.2012

Я не думаю, что в вашем случае это имеет какое-то существенное значение:

  • либо вы платите штраф понемногу, с каждой отдельной вставкой
  • или вы заплатите больший штраф после того, как все вставки будут сделаны, но только один раз

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

Но в остальном - нет особой разницы.

Марк

person marc_s    schedule 22.06.2009
comment
степень разбивает и ведение журнала и прочее? Это не нужно учитывать? - person Frank V; 23.06.2009

Я не планировал на это отвечать, так как не уверен на 100% в своих знаниях. Но поскольку это не похоже на то, что вы получаете большой отклик ...

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

Итак, если вы уверены, что ваши данные чистые (без повторяющихся данных PK), я бы сказал, вставьте, а затем добавьте PK.

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

person John MacIntyre    schedule 22.06.2009
comment
Спасибо за ваш ответ. Я действительно уверен, что у меня не будет проблемы с дубликатами ... - person Frank V; 23.06.2009

Когда вы добавляете PK при создании таблицы - проверка вставки равна O(Tn) (где Tn - это «n-е треугольное число», то есть 1 + 2 + 3 ... + n), потому что когда вы вставляете x-ю строку, она проверяется на соответствие ранее вставленным строкам «x - 1».

Когда вы добавляете PK после вставки всех значений - проверяется O(n^2), потому что, когда вы вставляете x-ю строку, она проверяется на соответствие всем n существующим строкам.

Первый, очевидно, быстрее, поскольку O(Tn) меньше O(n^2)

P.S. Пример: если вы вставляете 5 строк, это 1 + 2 + 3 + 4 + 5 = 15 операций против 5^2 = 25 операций

person Alex from Jitbit    schedule 14.05.2018