这类似于this question,但似乎有些答案与MySQL不完全兼容(或者我做得不对),而且我有点时间了弄清楚我需要的变化。显然我的SQL比我想象的更生气。我也想改变列值而不是删除,但我认为至少 部分很简单......
我有一张表:
rowid SERIAL fingerprint TEXT duplicate BOOLEAN contents TEXT created_date DATETIME
我希望除了按指纹的每个组的第一个(通过created_date)之外的所有设置duplicate = true。用重复的指纹标记所有行是很容易的。我坚持的部分是保留第一部分。
填充表格的其中一个应用程序会执行批量加载数据,多个工作人员从不同来源加载数据,而工作人员的数据不一定按日期进行分区,因此尝试将这些数据全部标记为他们进来(插入的第一个不一定是按日期的第一个)。此外,我已经有一堆数据,我需要清理任何一种方式。所以我宁愿只有一个相对有效的查询,我可以在批量加载后进行清理,而不是尝试将其构建到该应用程序中。
谢谢!
答案 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;
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可能对正在更新的表上执行(嵌套)子查询感到不满。