Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MySQL在重复密钥更新中使用唯一密钥中的可空列_Mysql_Nullable_Summarization - Fatal编程技术网

MySQL在重复密钥更新中使用唯一密钥中的可空列

MySQL在重复密钥更新中使用唯一密钥中的可空列,mysql,nullable,summarization,Mysql,Nullable,Summarization,我们的MySQL web analytics数据库包含一个汇总表,随着新活动的导入,该汇总表会在一天中不断更新。我们使用ON DUPLICATE KEY UPDATE以使汇总覆盖早期的计算,但由于汇总表的唯一键中的一列是可选的FK,并且包含空值,因此很难实现 这些空的意思是“不存在,所有这些情况都是等效的”。当然,MySQL通常将null视为“未知”,并且所有这些情况都不是等价的 基本结构如下: 一个“活动”表,包含每个会话的一个条目,每个条目都属于一个活动,对于某些条目有可选的筛选器和事务ID

我们的MySQL web analytics数据库包含一个汇总表,随着新活动的导入,该汇总表会在一天中不断更新。我们使用ON DUPLICATE KEY UPDATE以使汇总覆盖早期的计算,但由于汇总表的唯一键中的一列是可选的FK,并且包含空值,因此很难实现

这些空的意思是“不存在,所有这些情况都是等效的”。当然,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`)
);
“摘要”表,包含活动表中会话总数的每日汇总,以及包含事务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为空的情况的摘要。在这些情况下,ON DUPLICATE KEY UPDATE子句与现有行不匹配,并且每次都会写入一个新行。这是由于“NULL!=NULL”这一事实造成的。但是,在比较唯一键时,我们需要的是“NULL=NULL”

我正在寻找解决方法的想法,或是我们迄今为止提出的建议的反馈。到目前为止,我们已经想到的解决办法如下

  • 在运行摘要之前,删除包含空键值的所有摘要条目。(这就是我们现在正在做的事情) 如果在摘要过程中执行查询,则会产生负面的副作用,即返回缺少数据的结果

  • 将默认的NULL列更改为默认的0,这允许一致地匹配唯一键。 这会产生负面影响,使针对汇总表的查询开发过于复杂。它迫使我们使用大量的“CASE filter\u id=0,然后NULL ELSE filter\u id END”,并且由于所有其他表的filter\u id都有实际的NULL值,因此导致了笨拙的连接

  • 创建一个返回“CASE filter\u id=0然后NULL ELSE filter\u id END”的视图,并直接使用此视图而不是表。 汇总表包含几十万行,我被告知视图性能相当差

  • 允许创建重复条目,并在汇总完成后删除旧条目。 与提前删除它们有类似的问题

  • 添加一个包含0表示NULL的代理项列,并在唯一键中使用该代理项(实际上,如果所有列都不是NULL,我们可以使用主键)。
    这个解决方案似乎是合理的,除了上面的例子只是一个例子;实际数据库包含六个摘要表,其中一个在唯一键中包含四个可为空的列。有些人担心开销太大

  • 您是否有更好的解决方法、表结构、更新过程或MySQL最佳实践可以提供帮助

    编辑:澄清“空值的含义”

    包含空列的摘要行中的数据仅在以下意义上被视为属于一起:在摘要报告中是一个“全面”行,汇总那些不存在或未知数据点的项目。因此,在汇总表本身的上下文中,其含义是“那些没有已知值的条目的总和”。另一方面,在关系表中,这些确实是空结果

    将它们放入汇总表的唯一键中的唯一原因是允许在重新计算汇总报告时自动更新(通过重复键更新)

    也许更好的描述方式是通过一个具体的示例,其中一个汇总表根据受访者提供的业务地址的邮政编码前缀按地理位置分组结果。并非所有受访者都提供业务地址,因此事务和地址表之间的关系完全正确为空。在该数据的汇总表中,将为每个邮政编码前缀生成一行,其中包含该区域内的数据汇总。生成另一行以显示不知道邮政编码前缀的数据摘要


    将其余的数据表更改为显式的“THERE_IS_NO_ZIP_CODE”0值,并在ZipCodePrefix表中放置一条表示该值的特殊记录,这是不正确的——这种关系实际上是空的。

    我认为(2)中的某些内容确实是最好的选择——或者至少,如果你是从零开始的话。在SQL中,NULL表示未知。如果你想要一些其他的含义,你真的应该使用一个特殊的值,0当然是一个不错的选择

    您应该在整个数据库中执行此操作,而不仅仅是在这个表中。那你就不应该有奇怪的特殊情况了。事实上,您应该能够去掉很多当前的筛选器(例如:当前,如果您想要没有筛选器的摘要行,则使用特殊情况“filter is null”,而不是正常情况下的“filter=?”)

    您还应该继续在引用的表中创建一个“notpresent”条目,以保持FK约束有效(并避免特殊情况)

    PS:没有主键的表不是关系表,应该避免使用

    编辑1 嗯,在这种情况下,你真的需要重复密钥更新吗?如果你在做插入。。。选择,然后您可能会选择。但是如果你的应用程序正在提供数据,只需手工操作-进行更新(映射)
    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`)
    ;
    
    CREATE TABLE IF NOT EXISTS bar ( id INT PRIMARY KEY AUTO_INCREMENT, datebin DATE NOT NULL, baz1_id INT DEFAULT NULL, vbaz1_id INT AS (COALESCE(baz1_id, -1)) STORED, baz2_id INT DEFAULT NULL, vbaz2_id INT AS (COALESCE(baz2_id, -1)) STORED, blam DOUBLE NOT NULL, UNIQUE(datebin, vbaz1_id, vbaz2_id) ); INSERT INTO bar (datebin, baz1_id, baz2_id, blam) VALUES ('2016-06-01', null, null, 777) ON DUPLICATE KEY UPDATE blam = VALUES(blam);
    UPDATE `Summary` s
        LEFT JOIN `Activity` a
        ON s.`campaign_id` = a.`campaign_id`
    SET s.`sessions`     = a.COUNT(`session_id`)                ,
    SET s.`transactions` = a.COUNT(`transaction_id` IS NOT NULL)
    WHERE s.`day`         = a.`day`
    AND   s.`campaign_id` = a.`campaign_id`
    AND   s.`filter_id` IS NULL
    AND   a.`filter_id` IS 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
        WHERE `filter_id` IS NOT NULL
        GROUP BY `day`, `campaign_id`, `filter_id`
    ON DUPLICATE KEY UPDATE
        `sessions` = VALUES(`sessions`)
        , `transactions` = VALUES(`transactions`);