纠正数据库latest-flag中的错误数据

时间:2017-01-13 13:04:21

标签: mysql sql database select join

Background

我的数据库正在跟踪用户及其更改的用户名。每个真实的人可以随着时间的推移拥有许多用户名,但只有一个是“最新的”(当前的)。这是通过使用from-date和to-date实现的,如果它是最新的,toDate为null并且最新列设置为1.不幸的是,数据已损坏,现在一些用户没有最新的标志。 另一个错误是,有些人必须将日期视为空,即使它不应该是。

Question

如何找到需要将latest-flag设置/更新为1的行? (暂时不需要担心第二个错误,但是找到正确的行更新最新标志可能会更加棘手)。

在下面的示例中,正确的SELECT应该返回第5行和第8行(但不是第6行)。

Example

PersonId = 1是一个正确的人。 PersonId = 2缺少第5行中的最新值= 1。 PersonId = 3在第6行中有第二个错误(toDate = null),在第8行中缺少最新= 1。

为了避免第二个错误(第5行),我们知道fromDate对于需要纠正的行总是更大(更新)。

我试图加入,但我无法做到正确......

以下是“用户”表:

+----+--------+----------+------------+------------+--------+
| id | name   | personId | fromDate   | toDate     | latest |
+----+--------+----------+------------+------------+--------+
|  1 | Perold |        1 | 2016-12-01 | 2016-12-31 |      0 |
|  2 | Pernew |        1 | 2016-12-31 | NULL       |      1 |
|  3 | Body   |        2 | 2016-01-01 | 2016-12-01 |      0 |
|  4 | Bo     |        2 | 2016-12-01 | 2016-12-31 |      0 |
|  5 | Bonew  |        2 | 2016-12-31 | NULL       |      0 | <-- Wrong latest
|  6 | Joe    |        3 | 2016-01-01 | NULL       |      0 | <-- Wrong toDate, correct latest
|  7 | Joey   |        3 | 2016-12-01 | 2016-12-31 |      0 |
|  8 | Jo     |        3 | 2016-12-31 | NULL       |      0 | <-- Wrong latest
+----+--------+----------+------------+------------+--------+

3 个答案:

答案 0 :(得分:1)

对于#1,您可以找到带有子查询的错误行,该子查询为每个用户找到最大fromDate。这是唯一一个latest = 1。

如下所示(我没有测试过):

SELECT u.id FROM users u WHERE u.latest = 0 
AND u.fromDate = (SELECT MAX(u_sorted.fromDate) FROM user u_sorted WHERE u_sorted.personId=u.personId);

第二部分很容易修复#1,因此您可以依赖latest列。这次你要查找除toDate为空的最新行之外的所有行。

SELECT id FROM user WHERE latest = 0 AND toDate IS NULL;

现在唯一的技巧是修复这些行。您可能希望将缺少的toDate设置为比该人员下一个按时间顺序条目的fromDate提前一天。 (就个人而言,我会删除toDate列并将其替换为用户配置文件中的is_active列存储,但这是另一个主题。)

注意:上述查询假设fromDate列未损坏,没有重复,并且不为空。

答案 1 :(得分:0)

断言fromDate始终是一个值,并且每个persnId的日期从Date到日期都是密集填充的,这是我的建议。

首先我们计算下一个toDate值并猜测它是否是具有窗口函数的personId的第一个和/或最后一个记录。

然后,我们应用一些规则(CASES)来获取仅显示错误值的记录的有效值。最后,过滤只获取此记录。在最后的结果集中,我们可以看到原始记录值和newToDate和newLatest记录值。我们必须只提供显示newX值(可能是一个或两个)的字段

SELECT * 
  FROM (SELECT id,
               name, 
               personId
               fromDate, 
               toDate, 
               latest, 
               CASE WHEN isFirst = 1 AND toDate IS NULL THEN nextValue 
                    WHEN isLast = 0  AND toDate IS NULL THEN nextValue 
                    ELSE NULL END newToDate, 
               CASE WHEN isLast <> latest   
                    THEN isLast 
                    ELSE NULL END newLatest
          FROM (SELECT yourTable.*, 
                       LEAD(yourTable.fromDate) OVER (PARTITION BY yourT able.personId ORDER BY yourTable.fromDate  ASC) nextValue,
                       (CASE WHEN yourTable.fromDate = FIRST_VALUE(yourTable.fromDate)  OVER (PARTITION BY yourTable.personId ORDER BY  yourTable.fromDate  ASC)  
                             THEN 1 
                             ELSE 0 END) AS isFirst,
                       (CASE WHEN yourTable.fromDate = FIRST_VALUE (yourTable.fromDate) OVER (PARTITION BY yourTable.personId ORDER BY yourTable.fromDate  DESC) 
                             THEN 1 
                             ELSE 0 END) AS isLast
                  FROM yourTable)) 
 WHERE newToDate IS NOT NULL   
    OR newLatest IS NOT NULL 

答案 2 :(得分:0)

我最终弄明白了。这是最终结果。我是通过加入但是在其他解决方案的帮助下完成的。

SELECT u1.id
   FROM USERS u1
   INNER JOIN users u2 ON u2.id = u1.id AND u2.latest = 0 AND u2.todate is null
   INNER JOIN  
    (
        SELECT  personid, MAX(fromdate) max_date
        FROM    users ux
        GROUP   BY personId
    ) x ON  u2.personid = x.personid AND
            u2.fromdate = x.max_date
    WHERE NOT EXISTS( SELECT NULL FROM users u3 WHERE u1.PersonId = u3.PersonId AND u3.latest = 1); 

第一个内连接必须摆脱非空的todate。它出现在真实数据中。最后一个“不存在的地方”是摆脱已经正确的用户。