MySQL - 标记除1个匹配行之外的所有行

时间:2008-10-28 02:40:20

标签: sql mysql

这类似于this question,但似乎有些答案与MySQL不完全兼容(或者我做得不对),而且我有点时间了弄清楚我需要的变化。显然我的SQL比我想象的更生气。我也想改变列值而不是删除,但我认为至少 部分很简单......

我有一张表:

rowid SERIAL
fingerprint TEXT
duplicate BOOLEAN
contents TEXT
created_date DATETIME

我希望除了按指纹的每个组的第一个(通过created_date)之外的所有设置duplicate = true。用重复的指纹标记所有行是很容易的。我坚持的部分是保留第一部分。

填充表格的其中一个应用程序会执行批量加载数据,多个工作人员从不同来源加载数据,而工作人员的数据不一定按日期进行分区,因此尝试将这些数据全部标记为他们进来(插入的第一个不一定是按日期的第一个)。此外,我已经有一堆数据,我需要清理任何一种方式。所以我宁愿只有一个相对有效的查询,我可以在批量加载后进行清理,而不是尝试将其构建到该应用程序中。

谢谢!

6 个答案:

答案 0 :(得分:2)

需要明确告知MySQL您所分组的数据是否大于1024字节(有关详细信息,请参阅this link)。因此,如果指纹列中的数据大于1024字节,则应使用设置max_sort_length变量(有关允许的值的详细信息,请参阅this link,有关如何设置它的this link)一个更大的数字,以便该组不会默默地仅使用您的部分数据进行分组。

一旦您确定MySQL将正确分组您的数据,以下查询将设置重复标记,以便第一个指纹记录重复设置为FALSE / 0,并且任何后续指纹记录都重复设置为TRUE / 1:

    UPDATE mytable m1
INNER JOIN (SELECT fingerprint
                 , MIN(rowid) AS minrow 
              FROM mytable m2 
          GROUP BY fingerprint) m3 
        ON m1.fingerprint = m3.fingerprint
       SET m1.duplicate = m3.minrow != m1.rowid;

请记住,此解决方案不会考虑NULL,如果指纹字段可能为NULL,那么您需要额外的逻辑来处理这种情况。

答案 1 :(得分:0)

假设您可以在数据加载期间脱机,那么两步方法如何:

  • 将每个项目标记为重复。
  • 从每个组中选择最早的行,然后清除重复的标记。

不优雅,但完成工作。

答案 2 :(得分:0)

这是一个有趣的方式:

SET @rowid := 0;

UPDATE mytable
SET duplicate = (rowid = @rowid), 
    rowid = (@rowid:=rowid)
ORDER BY rowid, created_date;
  • 首先将用户变量设置为零,假设它小于表中的任何rowid。
  • 然后使用MySQL UPDATE...ORDER BY功能确保按行rowid依次更新行,然后created_date
  • 对于每一行,如果当前rowid不等于用户变量@rowid,请将duplicate设置为0(false)。仅在遇到rowid的给定值的第一行时才会出现这种情况。
  • 然后将rowid的虚拟集添加到其自己的值中,将@rowid设置为该值作为副作用。
  • 当您UPDATE作为下一行时,如果它与上一行重复,rowid将等于用户变量@rowid,因此duplicate将是设为1(真)。

修改:现在我对此进行了测试,并在设置duplicate的行中更正了错误。

答案 3 :(得分:0)

我不知道MySQL的语法,但在PLSQL中你只是这样做:

UPDATE t1
SET duplicate = 1
FROM MyTable t1
WHERE rowid != (
  SELECT TOP 1 rowid FROM MyTable t2
  WHERE t2.fingerprint = t1.fingerprint ORDER BY created_date DESC
)

这可能有一些语法错误,因为我只是输入袖口/无法测试它,但这是它的要点。


MySQL版本(未经测试):

UPDATE t1
  SET duplicate = 1
FROM MyTable t1
WHERE rowid != (
  SELECT rowid FROM MyTable t2
  WHERE t2.fingerprint = t1.fingerprint
  ORDER BY created_date DESC
  LIMIT 1
)

答案 4 :(得分:0)

使用MySQL的多表UPDATE语法,这是另一种方法:

UPDATE mytable m1
  JOIN mytable m2 ON (m1.rowid = m2.rowid AND m1.created_date < m2.created_date)
SET m2.duplicate = 1;

答案 5 :(得分:0)

未经测试...

UPDATE TheAnonymousTable
   SET duplicate = TRUE
 WHERE rowid NOT IN
       (SELECT rowid
          FROM (SELECT MIN(created_date) AS created_date, fingerprint
                  FROM TheAnonymousTable
                 GROUP BY fingerprint
               ) AS M,
               TheAnonymousTable AS T
         WHERE M.created_date = T.created_date
           AND M.fingerprint  = T.fingerprint
       );

逻辑是最内层查询将每个不同指纹的最早created_date作为表别名M返回。中间查询确定每个行的rowid值;必须这样做(但必要)是令人讨厌的,并且代码假定您不会获得相同指纹和时间戳的两条记录。这为您提供了每个单独指纹的earlist记录的rowid。然后外部查询(UPDATE)在rowid不是最早行之一的所有行上设置'duplicate'标志。

某些DBMS可能对正在更新的表上执行(嵌套)子查询感到不满。