我们的MySQL网站分析数据库包含一个汇总表,当导入新的活动时,汇总表会一天更新。我们使用ON DUPLICATE KEY UPDATE,以便汇总覆盖以前的计算,但因为汇总表的UNIQUE KEY中的一列是可选的FK并且包含NULL值,所以遇到困难。MySQL在重复密钥更新中使用唯一密钥中的空值列
这些NULL旨在表示“不存在,并且所有这些情况都是等价的”。当然,MySQL通常将NULL视为意义“未知的,并且所有这些情况都不相同”。
基本结构如下:含有为每个会话的条目,每个属于一个运动,与一些条目可选的过滤器和事务ID
的“活动”表。
CREATE TABLE `Activity` (
`session_id` INTEGER AUTO_INCREMENT
, `campaign_id` INTEGER NOT NULL
, `filter_id` INTEGER DEFAULT NULL
, `transaction_id` INTEGER DEFAULT NULL
, PRIMARY KEY (`session_id`)
);
A“摘要”表含有活性表,d那些含有一个事务ID的会话的总数会话的总数的每日汇总。这些摘要是分开的,每个广告系列和(可选)过滤器组合都有一个摘要。这是使用MyISAM的非事务性表。
CREATE TABLE `Summary` (
`day` DATE NOT NULL
, `campaign_id` INTEGER NOT NULL
, `filter_id` INTEGER DEFAULT NULL
, `sessions` INTEGER UNSIGNED DEFAULT NULL
, `transactions` INTEGER UNSIGNED DEFAULT NULL
, UNIQUE KEY (`day`, `campaign_id`, `filter_id`)
) ENGINE=MyISAM;
实际汇总查询是类似于以下,计数会话和交易的数量,然后通过运动和(可选)过滤器分组。除了案件的总结,其中过滤器_id是NULL
INSERT INTO `Summary`
(`day`, `campaign_id`, `filter_id`, `sessions`, `transactions`)
SELECT `day`, `campaign_id`, `filter_id
, COUNT(`session_id`) AS `sessions`
, COUNT(`transaction_id` IS NOT NULL) AS `transactions`
FROM Activity
GROUP BY `day`, `campaign_id`, `filter_id`
ON DUPLICATE KEY UPDATE
`sessions` = VALUES(`sessions`)
, `transactions` = VALUES(`transactions`)
;
一切都很正常。在这些情况下,ON DUPLICATE KEY UPDATE子句与现有行不匹配,并且每次都写入一个新行。这是由于“NULL!= NULL”的事实。但是,比较唯一键时,我们需要的是“NULL = NULL”。
我正在寻找有关我们已经提出的解决方法或反馈意见的建议。我们现在想到的解决方法如下。
在运行汇总之前,删除包含NULL键值的所有汇总条目。 (这是我们现在正在做的) 如果在汇总过程中执行查询,则会产生返回带有缺失数据的结果的负面影响。
将DEFAULT NULL列更改为DEFAULT 0,这允许UNIQUE KEY一致地匹配。 这具有消极的副作用,使查询的发展过度复杂化。它迫使我们使用大量的“CASE filter_id = 0 THEN NULL ELSE filter_id END”,并且由于所有其他表都具有实际的filter_id NULL,所以会导致难以加入。
创建一个返回“CASE filter_id = 0 THEN NULL ELSE filter_id END”的视图,并直接使用该视图来代替表。 总结表包含几十万行,并且我被告知视图性能很差。
允许创建重复条目,并在汇总完成后删除旧条目。 提前删除它们有类似的问题。
为NULL添加一个包含0的代理列,并在UNIQUE KEY中使用该代理(实际上,如果所有列都不为NULL,我们可以使用PRIMARY KEY)。
这个解决方案似乎是合理的,只是上面的例子只是一个例子;实际的数据库包含六十张摘要表,其中一个摘要表包含UNIQUE KEY中的四个可为空的列。有些人担心开销太多。
您是否有更好的解决方法,表结构,更新过程或MySQL最佳实践可以提供帮助?
编辑:为了澄清
包含NULL列的汇总行中的数据“零的意思是”被认为只有在这个意义上一起属于那个被一个“包罗万象”的总结报告行总结那些数据点不存在或未知的项目。因此,在汇总表本身的范围内,其含义是“没有任何值已知的那些条目的总和”。另一方面,在关系表中,这些确实是NULL结果。
将它们放入汇总表中唯一键的唯一原因是允许在重新计算汇总报告时自动更新(通过ON DUPLICATE KEY UPDATE)。
也许更好的方式来描述它是由一个具体的例子,其中一个汇总表分组结果地理上由回应者给出的商业地址的邮政编码前缀。并非所有的受访者都提供了一个商业地址,所以事务和地址表之间的关系非常正确。在此数据的汇总表中,将为每个邮政编码前缀生成一行,其中包含该区域内的数据汇总。将生成一个附加行以显示未知邮政编码前缀的数据摘要。
将其余数据表更改为具有明确的“THERE_IS_NO_ZIP_CODE”0值,并在表示此值的ZipCodePrefix表中放置特殊记录是不恰当的 - 该关系确实为NULL。
是,汇总表是相当不明确的关系表。它只是保存报告结果的便利容器。 我的声明说:“这些零点意在表示‘不存在,且所有此类案件是等价的’”,也许是误导性的。在包含规范化数据的关系表中,filter_id和其他可空关系在摘要表中作为唯一关键字的一部分提及,确实具有“未知”的含义,并且不是任何主键或唯一键的一部分。 见编辑,上面。 – ryandenki 2009-08-19 07:50:05
没错。我们使用INSERT ... SELECT,使用那里的ON DUPLICATE KEY子句来更新整天的条目。实际上,两年前的第一个实现就像你所建议的那样 - 首先选择数据,执行一些额外的操作,然后发出单独的INSERTS,并用WHERE子句考虑IS NULL情况。 该方法的优点是,插入各行的锁比INSERT ... SELECT方法的锁要短。但是这些锁只在使用行复制的主机上,我们可以用一个SQL语句替换所有的应用程序端代码。 – ryandenki 2009-08-20 02:41:18