2011-05-11 66 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