我们有一些房产,其所有者和地区可能会随着时间而变化。我想显示在时间内状态发生变化的属性的状态(默认状态和最后状态)(默认v最后状态不同=默认v表中的最后一行不同)。
有表states
:
stateID propertyID owner area timestamp stateYear
1 1 David Smith 20 123456789 2017
2 2 Amanda Green 74 123456799 2017
3 1 David Smith 19 123456999 2017
1 3 Amanda Green 12 123459999 2018
2 1 David Smith 20 123499999 2018
3 4 Jack Stone 62 123999999 2018
4 2 Amanda Green 73 129999999 2018
表properties
中插入了每个属性的默认状态:
propertyID userID defaultStateID defaultStateYear
1 1 1 2017
2 1 2 2017
3 2 1 2018
4 3 3 2018
以下是我尝试使用的代码:
SELECT s.*
FROM properties p JOIN
(SELECT s2.propertyID, s2.owner as owner, s2.area as area, max(s2.timestamp) as maxtimestamp
FROM states s2
GROUP BY s2.propertyID
HAVING COUNT(DISTINCT owner) >= 2 OR/** where owner changed **/
COUNT(DISTINCT area) >= 2/** or where area changed **/
) sp
ON sp.propertyID = p.propertyID JOIN
states s
ON s.propertyID = p.propertyID AND
(/** default state + last state **/
(s.stateYear = p.defaultStateYear AND
s.stateId = p.defaultStateID) OR
s.timestamp = sp.maxtimestamp
)
WHERE p.userID = 1/** only properties of user 1 **/
ORDER BY sp.propertyID ASC, s.stateYear ASC, s.stateID ASC;
用我的代码
我期望获得的是每个属性的状态(默认值+最后状态),其状态在指定用户的时间(默认为最后状态)期间发生变化。
但是,我的代码甚至显示状态,即在默认状态和最后状态之间发生更改的属性(但默认状态和最后状态相同) - 我不想得到这些状态。
预期结果:
stateID propertyID owner area timestamp stateYear
2 2 Amanda Green 74 123456799 2017
4 2 Amanda Green 73 129999999 2018
答案 0 :(得分:1)
我不确定这是否正确,即使它返回预期结果(由于非完整的样本数据)。
但是 - 以下查询将返回默认状态和上一个状态,但每个propertyID
只有一行:
SELECT p.propertyID
, ds.stateID as defaultStateId
, ds.stateYear as defaultStateYear
, ls.stateID as lastStateId
, ls.stateYear as lastStateYear
FROM properties p
JOIN states ds -- default state
ON ds.propertyID = p.propertyID
AND ds.stateID = p.defaultStateID
JOIN states ls -- last state
ON ls.propertyID = p.propertyID
AND ls.timestamp = (
SELECT MAX(s.timestamp)
FROM states s
WHERE s.propertyID = p.propertyID
)
WHERE p.userID = 1
http://sqlfiddle.com/#!9/5b3a1/21
结果:
| propertyID | defaultStateId | defaultOwner | defaultArea | defaultTimestamp | defaultStateYear | lastStateId | lastOwner | lastArea | lastTimestamp | lastStateYear |
|------------|----------------|--------------|-------------|------------------|------------------|-------------|--------------|----------|---------------|---------------|
| 1 | 1 | David Smith | 20 | 123456789 | 2017 | 2 | David Smith | 20 | 123499999 | 2018 |
| 2 | 2 | Amanda Green | 74 | 123456799 | 2017 | 4 | Amanda Green | 73 | 129999999 | 2018 |
查找具有默认状态的行是一种微不足道的ds.stateID = p.defaultStateID
- 但应该只有一个相应的条目。
对于" last"我们可以在ON子句中使用SELECT MAX(s.timestamp)
子查询。
现在 - 有了这个 - 我们可以在WHERE子句中添加额外的过滤器。
最后状态必须与默认状态不同:
AND ls.stateID <> p.defaultStateID
两个选定行中的区域和所有者必须不同:
AND ls.area <> ds.area
AND ls.owner <> ds.owner
但请注意,这与&#34;时间不一致&#34;。因为它们在两行中可能相等,但在它们之间的行中具有另一个值。这将不会为您的示例数据返回任何行。所以我现在需要猜测你的改造。如果您的意思是&#34;区域或所有者必须是不同的&#34; - 那就是
AND (ls.area <> ds.area OR ls.owner <> ds.owner)
现在它返回预期的结果。但你可能也很幸运。
如果您需要两个单独行中的数据,则可以向查询添加另一个联接
JOIN states s
ON (s.stateID, s.stateYear) IN (
(ds.stateID, ds.stateYear),
(ls.stateID, ls.stateYear)
)
您应该在此处使用主键来标识默认和最后状态。连接条件的另一种方式是
JOIN states s
ON (s.stateID = ds.stateID AND s.stateYear = ds.stateYear)
OR (s.stateID = ls.stateID AND s.stateYear = ls.stateYear)
我不确定MySQL是否能够优化其中一个条件。
最终查询可能类似于
SELECT s.*
FROM properties p
JOIN states ds -- default state
ON ds.propertyID = p.propertyID
AND ds.stateID = p.defaultStateID
JOIN states ls -- last state
ON ls.propertyID = p.propertyID
AND ls.timestamp = (
SELECT MAX(s.timestamp)
FROM states s
WHERE s.propertyID = p.propertyID
)
JOIN states s
ON (s.stateID = ds.stateID AND s.stateYear = ds.stateYear)
OR (s.stateID = ls.stateID AND s.stateYear = ls.stateYear)
WHERE p.userID = 1
AND ls.stateID <> p.defaultStateID
AND (ls.area <> ds.area OR ls.owner <> ds.owner)
ORDER BY s.propertyID ASC, s.stateYear ASC, s.stateID ASC;
返回预期结果
| stateID | propertyID | owner | area | timestamp | stateYear |
|---------|------------|--------------|------|-----------|-----------|
| 2 | 2 | Amanda Green | 74 | 123456799 | 2017 |
| 4 | 2 | Amanda Green | 73 | 129999999 | 2018 |