首先,我将解释我需要做什么,然后我想想我能做到的。我目前的计划在理论上似乎效率很低,所以我的问题是是否有更好的方法来实现它。
我有2个表 - 我们称之为'Products'和'Products_Temp',两者都是相同的。我需要下载大量文件(XML或XLS),其中包含供应商的产品详细信息(库存,定价等)。然后将它们解析为Products_Temp表。现在,我打算使用CF Scheduled Tasks来处理下载,并且Navicat会进行实际的解析 - 我很高兴这足够且足够高效。
下一步是我正在努力的地方 - 一旦文件被下载和解析,我需要查找数据中的任何变化。这将与Products表进行比较。如果找到了更改,那么应该添加或更新该行(如果应该删除它,那么我需要标记它而不是仅删除它)。比较所有数据后,应清空products_temp表。
我知道比较表并相应地同步它们的方法,但是我遇到的问题是我将处理来自不同来源的多个文件。我曾考虑只使用products表并附加/更新,但我不确定如何管理'flag deleted'要求。
现在,我知道的唯一方法我可以使它工作是遍历products_temp表,执行各种cfquerys并在完成后删除该行。然而,这似乎非常低效,并且考虑到我们可能会处理数十万行,如果我们每天更新所有内容,则不太可能有效。
对于更好的路线上的任何指示或建议都将不胜感激!
答案 0 :(得分:2)
为了查找更改,我会根据您要匹配的字段查看联接。这可能很慢,取决于字段的数量以及它们是否被索引,但我仍然说它比循环更快。有点像:
SELECT product_id
FROM Products
WHERE product_id NOT IN (
SELECT T.product_id
FROM Products_Temp T
INNER JOIN PRODUCTS P
ON (
P.field1 = T.field1
AND P.field2 = T.field2
...
)
)
找不到匹配的缺失产品:
SELECT P.product_id
FROM Products P
LEFT OUTER JOIN Products_Temp T
ON (P.field1 = T.field1
AND P.field2 = T.field2
...)
WHERE T.product_id IS NULL
答案 1 :(得分:2)
两种回应都有可能。只是为了扩大你的选择..
IF mySQL支持某种散列,基于每行,您可以使用comodoro's suggestion的变体来避免硬删除。
识别已更改
要识别更改,请在主键上执行内部联接并检查哈希值。如果它们不同,则产品已更改并应更新:
UPDATE Products p INNER JOIN Products_Temp tmp ON tmp.ProductID = p.ProductID
SET p.ProductName = tmp.ProductName
, p.Stock = tmp.Stock
, ...
, p.DateLastChanged = now()
, p.IsDiscontinued = 0
WHERE tmp.TheRowHash <> p.TheRowHash
识别已删除
使用简单的外部联接来标识临时表中不存在的记录,并将它们标记为“已删除”
UPDATE Products p LEFT JOIN Products_Temp tmp ON tmp.ProductID = p.ProductID
SET p.DateLastChanged = now()
, p.IsDiscontinued = 1
WHERE tmp.ProductID IS NULL
识别新
最后,使用类似的外部联接来插入任何“新”产品。
INSERT INTO Products ( ProductName, Stock, DateLastChanged, IsDiscontinued, .. )
SELECT tmp.ProductName, tmp.Stock, now() AS DateLastChanged, 0 AS IsDiscontinued, ...
FROM Products_Temp tmp LEFT JOIN Products p ON tmp.ProductID = p.ProductID
WHERE p.ProductID IS NULL
如果每行散列不可行,则替代方法是Sharondio's suggestion的变体。
向临时表添加“状态”列,并通过一系列连接将所有导入的记录标记为“新”,“已更改”或“未更改”。 (默认值应为“已更改”)。
识别联合国改变
首先在所有字段上使用内部联接来标识未更改的产品。 (注意,如果您的表包含任何可以为空的字段,请记住使用类似coalesce
的内容,否则,结果可能会因为null
值不等于任何值而产生偏差。
UPDATE Products_Temp tmp INNER JOIN Products p ON tmp.ProductID = p.ProductID
SET tmp.Status = 'Unchanged'
WHERE p.ProductName = tmp.ProductName
AND p.Stock = tmp.Stock
...
识别新
与之前一样,使用外部联接来标识“新”记录。
UPDATE Products_Temp tmp LEFT JOIN Products p ON tmp.ProductID = p.ProductID
SET tmp.Status = 'New'
WHERE p.ProductID IS NULL
通过消除过程,临时表中的所有其他记录都被“更改”。计算完状态后,您可以更新Products表:
/* update changed products */
UPDATE Products p INNER JOIN Products_Temp tmp ON tmp.ProductID = p.ProductID
SET p.ProductName = tmp.ProductName
, p.Stock = tmp.Stock
, ...
, p.DateLastChanged = now()
, p.IsDiscontinued = 0
WHERE tmp.status = 'Changed'
/* insert new products */
INSERT INTO Products ( ProductName, Stock, DateLastChanged, IsDiscontinued, .. )
SELECT tmp.ProductName, tmp.Stock, now() AS DateLastChanged, 0 AS IsDiscontinued, ...
FROM Products_Temp tmp
WHERE tmp.Status = 'New'
/* flag deleted records */
UPDATE Products p LEFT JOIN Products_Temp tmp ON tmp.ProductID = p.ProductID
SET p.DateLastChanged = now()
, p.IsDiscontinued = 1
WHERE tmp.ProductID IS NULL
答案 2 :(得分:1)
我必须解决一次类似的问题,也许解决方案适用于你的情况(我不太了解Coldfusion)。为什么不(对于每个源)只删除对应于该源的表产品中的所有内容并将其替换为来自同一来源的Products_Temp?它假设您可以为每个源创建一个唯一的字段。 SQL代码看起来像:
DELETE FROM Products WHERE source_id = x; INSERT INTO Products (field1, field2, ..., source_id) SELECT field1, field2, ..., x FROM Products_Temp;
此外,如果源没有太大变化,您可以考虑在下载后制作哈希并跳过更新,如果它没有更改以保存一些数据库访问。