Задержка вставки из-за ограничений внешнего ключа - PullRequest
0 голосов
/ 11 мая 2011

Я пытаюсь выполнить запрос:

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`)

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

У меня вопрос: есть ли способ выполнить этот запрос, но подождать до следующего запроса (который обновляет таблицу Product), прежде чем выполнять вставленную часть запроса выше? Также следует отметить, что если запрос выполняется после создания записи Product, он больше не будет видеть p. Id как нулевой и, следовательно, сбой, поэтому его необходимо выполнить до создания записи Product.

---> Редактировать <--- Концепция, которую я пытаюсь реализовать, заключается в следующем: Для начала я импортирую набор данных во временную таблицу, таблица <code>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 (Product. Id)

По сути, я пытаюсь сделать следующее: 1. Каждый раз, когда новый продукт (или ранее деактивированный продукт) появляется в наборе данных поставщика, запись создается в таблице ProductState как «Активировано». 2. Каждый раз, когда продукт (который активирован) не отображается в наборе данных поставщика, запись создается как «Деактивированная» в таблице ProductState.

Цель таблицы ProductState - отслеживать состояния активации и деактивации продукта. Также ProductState является отношением Multi-To-One к Таблице продуктов, и состояние продукта будет меняться только один раз в день, поэтому мой PKEY будет ProductId и ChangedDate.

Ответы [ 2 ]

2 голосов
/ 11 мая 2011

При использовании внешних ключей вам обязательно нужно сначала получить данные в таблице Product, прежде чем войти в состояние, подумайте об этом с помощью следующей логики: «Как может нечто, что не существует, иметь состояние»?

Итак, псевдокод того, что вы должны сделать:

  1. Читать в списке товаров поставщика
  2. Сравните их с существующим списком в таблице товаров
  3. Если найдены новые: 3.1 Вставить в таблицу Product, 3.2 Вставить в таблицу ProductState
  4. Если отсутствует в списке поставщиков: 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
2 голосов
/ 11 мая 2011

Я думаю, вы должны:

  • начать транзакцию
  • вставьте в Products таблицу
  • вставьте в ProductState таблицу
  • совершить транзакцию

Это позволит избежать любых ошибок внешнего ключа, но также обеспечит точность ваших данных. Вы никоим образом не хотите «избегать» ограничения внешнего ключа, и InnoDB (который, я уверен, вы используете) никогда не откладывает эти ограничения, пока вы не отключите их полностью.

Также нет, вы не можете вставить в несколько таблиц в одном операторе INSERT ... SELECT.

...