2011-05-11 61 views
0

我想運行一個查詢插入:延誤的原因是外鍵約束

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

(我不太清楚查詢是正確的,但它似乎是工作),但我跑進以下問題。在創建「產品」表中的條目之前,我正在運行此查詢,並且由於該條目尚未出現在產品表中,我正在獲取外鍵約束問題。

我的問題是,是否有一種方法來運行此查詢,但等到下一個查詢(它更新產品表)執行上述查詢的插入部分之前?另外需要注意的是,如果查詢在產品條目創建後運行,它將不會再看到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; 

外鍵與產品表中標識關係(ProductId

基本上我試圖做到這一點是: 1 。任何時候在供應商數據集中顯示新產品(或以前停用的產品)時,該記錄都將作爲「已激活」在ProductState表中創建。 2.任何時候產品(即已激活)都不會顯示在供應商數據集中,該記錄在ProductState表中創建爲「已禁用」。

ProductState表的目的是跟蹤產品的激活和停用狀態。此外,ProductState與Product Table是多對一關係,產品的狀態每天只會更改一次,因此我的PKEY將爲ProductId和ChangedDate。

+0

在一個側面說明,是否有執行上的選擇查詢多個插入的方式(這可能是解決方案,插入到多個表與一個選擇查詢) – 2011-05-11 16:29:55

+0

在插入產品之前需要插入ProductState的任何原因? – 2011-05-11 16:42:44

+0

@tsOverflow我的邏輯(這可能是錯誤在這裏),是一旦產品條目存在,上述查詢將不再發現它爲空(因爲它存在),但再次與邏輯問題,如果它已經存在(但被停用),它也會在查詢中失敗。 – 2011-05-11 17:00:03

回答

2

對於外鍵,在進入狀態之前,您肯定需要首先在產品表中獲得數據,然後用這個邏輯思考:「不存在的東西如何能有狀態」?

所以,你應該做的僞代碼:

  1. 閱讀的供應商的產品列表
  2. 比較它們現有的列表中你的產品表
  3. 如果發現新的:3.1它插入到產品表3.2它插入到ProductState表
  4. 如果從供應商的名單丟失:4.1它插入到ProductState表

所有這些都應該在1筆交易中完成。請注意,除非您確實要刪除與其關聯的所有信息,否則不應刪除產品表中的內容。也刪除您已存儲的所有「狀態」。

而不是試圖做到這一切在1查詢 - 最好的辦法是創建一個存儲過程,按照上述步驟完成工作。我認爲它過於複雜(或者在這種情況下,可能不可能)在1個查詢中完成所有操作。

編輯:事情是這樣的:

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 
+0

我認爲這正是我所期待的。我之前沒有使用過程,但是您的示例使我清楚瞭解需要遵循的邏輯,並且我實際上正在考慮通過幾個臨時表(新/刪除)作爲修復的一部分,通過程序可以簡化一切。一旦我將這個方法付諸實施,會很快回復你的解決方案。 – 2011-05-11 18:21:31

+0

接受的解決方案 - 完美的工作,**和**我現在將把其他一些邏輯(審計信息)移動到其他程序中,這很有效! – 2011-05-11 19:02:06

+0

很好,它工作出來! – 2011-05-11 19:22:13

2

我想你應該:

  • 啓動事務
  • 做你插入到Products
  • 做你插入到ProductState
  • 提交事務

這將避免任何外鍵錯誤,但也將確保您的數據始終是準確的TE。您不希望以任何方式「避免」外鍵約束,InnoDB(我相信您使用的)不會推遲這些約束,除非您完全關閉它們。

也不,你不能在一個INSERT ... SELECT語句中插入多個表。

+0

我得到了開始/提交事務部分,但是,我需要在更新產品表之前至少運行查詢的選擇部分,然後在插入部分之後(但仍需要選擇結果),我是不清楚如何使用mysql查詢來執行該邏輯(並且肯定希望避免通過php逐行處理,因爲這是非常緩慢的並且處理臨時表和插入/選擇查詢的整點) – 2011-05-11 16:39:11

+0

@Aaron Murray - 坦率地說,你做錯了什麼,或者我完全誤解了你。在創建ProductState記錄之前,您的產品表插入/更新必須可能,或者您的數據庫模型存在嚴重問題。你能用文字和表格(通過編輯你的問題)來解釋你的桌子應該是什麼樣子嗎?這將幫助我們確定最佳解決方案。 – Henry 2011-05-11 16:42:36

+0

我需要在更新產品表之前至少運行查詢的選擇部分>>這將如何實現?我在這裏和亨利在一起,你的邏輯中有些奇怪的東西。 – 2011-05-11 16:48:43