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
+----+--------+----------+------------+------------+--------+
答案 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。它出现在真实数据中。最后一个“不存在的地方”是摆脱已经正确的用户。