Лучше ли использовать значение параметра или столбца при копировании данных из одной таблицы в другую? - PullRequest
2 голосов
/ 07 февраля 2020

У меня есть оператор SQL для копирования записей из одной таблицы в другую:

INSERT INTO [deletedItems] (
    [id],
    [shopId])
SELECT 
    [id],
    [shopId]
FROM [items]
WHERE shopId = @ShopId

@ ShopId - это параметр, предоставленный команде sql при вызове db из кода моего приложения.

Сможет ли оператор сделать работу лучше, если я изменю его для непосредственного использования предоставленного параметра, поэтому серверу SQL не нужно включать столбец shopId из таблицы продуктов в проекцию?

INSERT INTO [deletedItems](
    [id],
    [shopId])
SELECT 
    [id],
    @ShopId
FROM [items]
WHERE shopId = @ShopId

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

Ответы [ 4 ]

2 голосов
/ 07 февраля 2020

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

Нет, SQL Сервер, не делает этого. В этом можно убедиться, посмотрев план выполнения и «столбцы вывода» для оператора, обращающегося к items.

В общем случае это не безопасное преобразование и может привести к потере информации. Например, если источник соответствует строкам

+--------+
| ShopId |
+--------+
| A123   |
| a123   |
+--------+

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

Если применяется одно из следующих условий

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

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

CREATE TABLE #T(X INT IDENTITY, Y CHAR(4000));

INSERT INTO #T
SELECT TOP 1000000 REPLICATE('A',4000)
FROM sys.all_objects o1, sys.all_objects o2

SELECT X, Y
FROM #T
WHERE Y = REPLICATE('A',4000)
ORDER BY X


SELECT X, REPLICATE('A',4000) AS Y
FROM #T
WHERE Y = REPLICATE('A',4000)
ORDER BY X

Размер строк, входящих в оператор сортировки, в первом случае намного больше, так как он включает столбец большой строки и сортировка переходит в tempdb. В результате выполнение запроса занимает значительно больше времени. Запрос на предоставление памяти для второго запроса такой же, как и для первого, поскольку он не учитывает, что столбец вычисляется после сортировки, но для сортировки данных меньше, и он не проливается. На версиях SQL Сервер, где доступна обратная связь с адаптивным предоставлением памяти, избыточное предоставление будет исправлено, если запрос будет выполняться повторно.

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

enter image description here

2 голосов
/ 07 февраля 2020

У меня нет никаких различий в исполнении. Медленная часть будет искать правильные элементы с помощью @ShopID или операций ввода-вывода.

Что может улучшить производительность вашего запроса, так это наличие индекса для столбца [ShopID], где ID - это первичный ключ или включенный столбец.

0 голосов
/ 07 февраля 2020

Два важных момента.

Вычисление скалярных выражений в SELECT (как правило) мало влияет на производительность запроса. Производительность определяется движением данных.

Таким образом, выбор «константы» вместо выбора столбца из таблицы несущественен.

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

В частности, вы хотите быть уверены, что запрос использует индекс на items(shopId), если таблица охватывает несколько страниц данных.

0 голосов
/ 07 февраля 2020

Будет ли оператор работать лучше, если я изменю его для непосредственного использования предоставленного параметра

То же самое. Потому что у вашего результата есть уникальное предложение ShopId as Where.

INSERT INTO [deletedItems] (
    [id],
    [shopId])
SELECT 
    [id],
    [shopId]
FROM [items]
WHERE shopId = @ShopId -- this condition makes the `shopId` value is become unique
...