SQL Server DELETE и SELECT ведут себя по-разному с тем же предложением WHERE
У меня есть таблица, заполняемая ежедневным запланированным заданием, которое удаляет последние 7 дней данных, а затем повторно заполняет семь последних дней стоимостью данных из другого источника (мэйнфрейм).
Недавно пользователи сообщили о нескольких дубликатах, которые восходят к началу октября 2011 года.... по величине сотен тысяч строк.
Я заметил странное поведение с удалением, которое выполняется для каждого задания:
DELETE FROM fm104d
WHERE location = '18'
AND (CONVERT(datetime,CASE WHEN ISDATE(pull_date)=0 THEN '19000101'
ELSE pull_date END)) > DATEADD(day, -7, getdate())
Приведенное выше значение возвращает "(пострадали 0 строк)".
Когда я запускаю вышеуказанное после замены DELETE на SELECT *, я получаю 32 000 + строк взамен.
Почему SELECT и DELETE ведут себя по-другому?
UPDATE
Вот реальный план выполнения:
http://pastie.org/2869202
Ответы
Ответ 1
Ты не поверишь этому. Я не на самом деле, так как это почти не имеет логического смысла, но в конце концов, решение, которое сработало... должно было добавить индекс.
Кредит для этого относится к моему местному администратору баз данных "Думал ли о добавлении индекса? Я просто сделал это, чтобы проверить, и достаточно уверен, что он работает".
Здесь индекс добавлен:
CREATE INDEX ixDBO_fir104d__SOURCE_LOCATION__Include
ON [dbo].[fir104d] ([SOURCE_LOCATION])
INCLUDE ([Transaction_Date],[PULL_DATE])
GO
Я запускаю задание как запланированное и, конечно же, все как есть.
Я предполагаю, что в плане объяснения есть что сказать, что он не использовал индекс/неправильный индекс, но мой ум разработчиков не может сильно понять этот уровень детализации.
Спасибо всем за потраченное время и усилия.
UPDATE
Полученные новости от другого разработчика о том, что данные в этой таблице дополнительно повреждены до того момента, когда потребовалось "несколько часов участия DBA для решения" вместе с разработчиком, который должен выполнить некоторые другие исправления данных (чтение: перезагрузка файлов данных),
В конце дня, добавив индекс, вероятно, было хорошо, учитывая, как работает запланированное задание, по-видимому, в истории было еще больше!
Ответ 2
Попробуйте следующее:
DELETE FROM fm104d where fm104d.id in
(
select id from fm104d
WHERE location = '18'
AND (CONVERT(datetime,CASE WHEN ISDATE(pull_date)=0 THEN '19000101'
ELSE pull_date END)) > DATEADD(day, -7, getdate())
)aaa
и дать ответ, если он удалит
p.s.: это не решение, но приведет к решению.
Ответ 3
Мне кажется, что вы никогда не захотите удалить, когда pull_date
не является датой.
Попробуйте исключить явные замены строк... возможно, существует разбор между SELECT
и DELETE
DELETE
FROM
fm104d
WHERE
[location] = '18' --NOTE if this is an int, then just try with 18, no dits
AND (
CASE ISDATE([pull_date])
WHEN 1 THEN
CAST([pull_date] AS DATETIME)
ELSE
NULL
END > DATEADD(DAY, -7, GETDATE())
)
РЕДАКТИРОВАТЬ: Обратите внимание, что это не соответствует вашему SQL, потому что в вашем случае, если вы вернетесь в январе, 1900, он удалит вашу строку независимо.... Я предположил, это не было вашим намерением.
Ответ 4
Одним из возможных объяснений может быть то, что есть две таблицы, каждая в другой схеме. Возможно, если у вас есть права select
для обеих схем, но delete
права только для одного, SQL Server может выбрать другую таблицу для delete
.
Чтобы убедиться в этом, префикс вашей таблицы с именем схемы (схема по умолчанию dbo
)
FROM schema1.fm104d
(Не тестировалось, просто мысль, нет доступа к установке SQL Server atm.)
Ответ 5
Для вашего выбора добавьте ISDATE(pull_date)
в список выбора, чтобы определить, какая часть аргумента case влияет на них. Посмотрите также на pull_date и посмотрите, есть ли шаблон в формате строки, общей среди этих правонарушителей, которые отказываются быть удаленными.
Это может иметь некоторое отношение к детерминизму Convert и IsDate:
"ISDATE детерминирован только в том случае, если вы используете его с функцией CONVERT, если указан параметр стиля CONVERT, а стиль не равен 0, 100, 9 или 109."
См. пару примеров здесь, где convert вложен внутри isdate:
http://www.sqlmonster.com/Uwe/Forum.aspx/sql-server-programming/181/CAST-CONVERT-nondeterministic
Итак, попробуйте настроить предложение where и посмотреть, поможет ли это. Также обратите внимание, что "Возвращаемое значение ISDATE может зависеть от настроек LANGUAGE и DATEFORMAT". Так что, возможно, что-то на вашем сервере изменилось в этом отношении. Почему это повлияло на удаление, но не на выбор, все еще странно.
Ответ 6
Как пробовать это, посмотрите, можете ли вы сначала оценить столбец pull_date, а затем удалить записи.
DELETE FROM fm104d
WHERE Location = 18
AND Pull_date IN
(
SELECT CONVERT (DATETIME,
CASE
WHEN ISDATE(pull_Date) = 0
THEN '19000101'
ELSE pull_date
END) AS pull_date
FROM fm104d
WHERE pull_date > DATEADD(DAY, -7, GETDATE())
)