仅显示随时间变化的行

时间:2018-05-19 17:43:52

标签: php mysql

我们有一些房产,其所有者和地区可能会随着时间而变化。我想显示在时间内状态发生变化的属性的状态(默认状态和最后状态)(默认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;
用我的代码

SQL Fiddle

我期望获得的是每个属性的状态(默认值+最后状态),其状态在指定用户的时间(默认为最后状态)期间发生变化。

但是,我的代码甚至显示状态,即在默认状态和最后状态之间发生更改的属性(但默认状态和最后状态相同) - 我不想得到这些状态。

预期结果:

stateID propertyID  owner           area    timestamp   stateYear
2       2           Amanda Green    74      123456799   2017
4       2           Amanda Green    73      129999999   2018

1 个答案:

答案 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 |