#mysql #insert #foreign-keys #delay
#mysql #вставить #внешние ключи #задержка
Вопрос:
Я пытаюсь выполнить запрос:
INSERT
INTO `ProductState` (`ProductId`, `ChangedOn`, `State`)
SELECT t.`ProductId`, t.`ProcessedOn`, 'Activated'
FROM `tmpImport` t
LEFT JOIN `Product` p
ON t.`ProductId` = p.`Id`
WHERE p.`Id` IS NULL
ON DUPLICATE KEY UPDATE
`ChangedOn` = VALUES(`ChangedOn`)
(Я не совсем уверен, что запрос правильный, но, похоже, он работает), однако я сталкиваюсь со следующей проблемой. Я запускаю этот запрос перед созданием записи в таблице ‘Products’ и получаю проблему с ограничением внешнего ключа из-за того, что записи еще нет в таблице Products.
Мой вопрос в том, есть ли способ выполнить этот запрос, но дождаться следующего запроса (который обновляет таблицу Product), прежде чем выполнять вставную часть запроса выше? Также следует отметить, что если запрос выполняется после создания записи продукта, он больше не будет видеть, что p. Id
равен нулю и, следовательно, сбой, поэтому его необходимо выполнить до создания записи продукта.
—> Редактировать <— Концепция, которой я пытаюсь достичь, заключается в следующем: Для начала я импортирую набор данных во временную таблицу, Product
таблица представляет собой список всех продуктов, которые добавлены (или были добавлены в прошлом) через набор данных из временной таблицы. Что мне нужно, так это отдельная таблица, которая обеспечивает изменение состояния продукта, поскольку иногда продукт становится недоступным (его больше нет в наборе данных, предоставленном поставщиком).
Таблица ProductState выглядит следующим образом:
CREATE TABLE IF NOT EXISTS `ProductState` (
`ProductId` VARCHAR(32) NOT NULL ,
`ChangedOn` DATE NOT NULL ,
`State` ENUM('Activated','Deactivated') NULL ,
PRIMARY KEY (`ProductId`, `ChangedOn`) ,
INDEX `fk_ProductState_Product` (`ProductId` ASC) ,
CONSTRAINT `fk_ProductState_Product`
FOREIGN KEY (`ProductId` )
REFERENCES `Product` (`Id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci;
Внешний ключ — это идентифицирующая связь с таблицей продуктов ( Product
. Id
)
По сути, я пытаюсь выполнить следующее: 1. Всякий раз, когда новый продукт (или ранее деактивированный продукт) появляется в наборе данных поставщика, запись создается в таблице ProductState как «Активированный». 2. Всякий раз, когда продукт (который активирован) не отображается в наборе данных поставщика, запись создается как «Деактивированная» в таблице ProductState.
Целью таблицы ProductState является отслеживание состояний активации и деактивации продукта. Кроме того, ProductState имеет отношение «Несколько к одному» с таблицей Product, и состояние продукта будет меняться только один раз в день, поэтому моим PKEY будут ProductID и ChangedDate.
Комментарии:
1. Кстати, есть ли способ выполнить несколько вставок в запросах select (это может быть решением, вставка в несколько таблиц с помощью одного запроса select)
2. есть ли причина, по которой вам нужно вставить в ProductState перед вставкой продукта?
3. @tsOverflow моя логика (которая здесь может быть ошибкой) заключается в том, что как только запись Product существует, приведенный выше запрос больше не будет считать ее нулевой (потому что она существует), но опять же с проблемами логики, если она уже существует (но деактивирована), она также завершится ошибкой в запросе.
Ответ №1:
С внешними ключами вам определенно нужно сначала иметь данные в таблице Product, прежде чем вводить состояние, подумайте об этом с помощью этой логики: «Как может что-то, чего не существует, иметь состояние»?
Итак, псевдокод того, что вы должны сделать:
- Прочитайте в списке продуктов поставщика
- Сравните их с существующим списком в вашей таблице продуктов
- Если найдены новые: 3.1 Вставьте его в таблицу Product, 3.2 Вставьте его в таблицу ProductState
- Если отсутствует в списке поставщика: 4.1 Вставьте его в таблицу ProductState
Все это должно быть сделано за 1 транзакцию. Обратите внимание, что вам не следует удалять элементы из таблицы Product, если вы действительно не хотите удалить всю информацию, связанную с ней, т.Е. также удалите все «состояния», которые вы сохранили.
Вместо того, чтобы пытаться сделать все это в одном запросе, лучше всего создать хранимую процедуру, которая выполняет работу, описанную выше, шаг за шагом. Я думаю, что становится слишком сложно (или, в данном случае, вероятно, невозможно) выполнить все в одном запросе.
Редактировать: Что-то вроде этого:
CREATE PROCEDURE `some_procedure_name` ()
BEGIN
-- Breakdown the tmpImport table to 2 tables: new and removed
SELECT * INTO _temp_new_products
FROM`tmpImport` t
LEFT JOIN `Product` p
ON t.`ProductId` = p.`Id`
WHERE p.`Id` IS NULL
SELECT * INTO _temp_removed_products
FROM `Product` p
LEFT JOIN `tmpImport` t
ON t.`ProductId` = p.`Id`
WHERE t.`ProductId` IS NULL
-- For each entry in _temp_new_products:
-- 1. Insert into Product table
-- 2. Insert into ProductState table 'activated'
-- For each entry in _temp_removed_products:
-- 1. Insert into ProductState table 'deactivated'
-- drop the temporary tables
DROP TABLE _temp_new_products
DROP TABLE _temp_removed_products
END
Комментарии:
1. Я думаю, это было именно то, что я искал. Я раньше не использовал процедуры, но ваш пример дает мне четкое понимание того, какой «логике» мне нужно следовать, и я на самом деле думал сделать это через пару временных таблиц (новых / удаленных) как часть исправления, с помощью процедур это может все упростить. Вскоре свяжусь с вами, чтобы принять ваше решение, как только я введу это в действие.
2. Решение принято — это сработало отлично, и теперь я собираюсь перенести некоторую другую логику (информацию аудита) в другие процедуры, которая работает гладко!
Ответ №2:
Я думаю, вам следует:
- запуск транзакции
- выполните свою вставку в
Products
таблицу - выполните свою вставку в
ProductState
таблицу - зафиксируйте транзакцию
Это позволит избежать любых ошибок внешнего ключа, но также гарантирует, что ваши данные всегда точны. Вы не хотите «избегать» ограничения внешнего ключа каким-либо образом, и InnoDB (который, я уверен, вы используете) никогда не откладывает эти ограничения, если вы не отключите их полностью.
Также нет, вы не можете вставить в несколько таблиц за одну вставку… Оператор SELECT.
Комментарии:
1. Я получаю часть транзакции запуска / фиксации, однако мне нужно выполнить часть запроса select по крайней мере до обновления таблицы Product, а затем часть insert после (но все еще требуются результаты из select) Я не совсем понимаю, как выполнять эту логику с запросами mysql (и определенно хочу избежать обработки через php построчно, поскольку это чрезвычайно медленно и весь смысл работы с временными таблицами и запросами insert / select)
2. @Aaron Murray — честно говоря, вы делаете что-то не так, или я вас совершенно неправильно понимаю. Вставка / обновление вашей таблицы Product должно быть возможно до создания записи ProductState, иначе у вашей модели базы данных возникнут большие проблемы. Можете ли вы объяснить словами и таблицами (отредактировав свой вопрос), как именно должны выглядеть ваши таблицы «до» и «после»? Это поможет нам определить наилучшее решение.
3. Мне нужно выполнить часть запроса select по крайней мере до обновления таблицы Product >> Как это было бы возможно? Я согласен с Генри, в вашей логике есть что-то странное.
4. Вы вполне можете быть правы, моя логика может быть не в порядке — я обновил свое описание
5. @tsOverflow Я обновил свое описание, вполне возможно, что в моей логике есть ошибка.