【问题标题】:How can I find the column value that was modified latest from audit table using "left self exclusion join"?如何使用“左自排除连接”从审计表中找到最新修改的列值?
【发布时间】:2017-12-19 03:59:06
【问题描述】:

我有以下表格设计(它是不可变的,而且很糟糕):

表 1:“ids”:id1,id2。

将每个 id1 映射到 1 个或多个 id2 值。

表 2“审计”:id2、时间戳、unique_id。

将每个 id2 映射到其更改的审计跟踪。 unique_id 保证对表中的每一行都是唯一的

目标:对于 id1 的每个不同值;选择一行;包含根据表 2 最新修改的单个 id2 值(在表 1 中映射到 id1 的值中)。

我已尝试执行this SO question 中概述的“左自我排除加入”方法,但无法完全弄清楚如何使其工作。我的查询看起来像:

select i1.id1, a2.id2
from   ids i1
right join audit a1 on i1.id2=a1.id2
left join ids i2 on i1.id1=i2.id1
left outer join audit a2 on i2.id2=a2.id2
where a1.timestamp < a2.timestamp
and a1.unique_id!=a2.unique_id
-- and a1.id2 is null

问题是,这返回零行,因为在查询中(不包括最后注释掉的行)没有任何行的 id2 为零,所以我猜我的外连接错误。

我哪里错了?

示例 DB 小提琴在这里:http://sqlfiddle.com/#!6/f5c45/4

CREATE TABLE ids (id1 int, id2 int)
CREATE TABLE audit (id2 int, timestamp int, unique_id int)

insert into ids values (1,11)
insert into ids values (1,12)
insert into ids  values (2,23)
insert into audit values (11,101,10000)
insert into audit values (11,104,10001)
insert into audit values (12,102,10002)
insert into audit values (12,103,10003)
insert into audit values (23,101,10004)

此数据的预期结果集:

id1   id2   explanation
1     11    id2=11 last modified at 104, id2=12 at 103.
2     23    Only 1 row. That case tripped my query too.

澄清:我知道这个问题可以通过相关子查询来解决,而无需使用左自排除连接。我对如何做到这一点不感兴趣,我对我的左自排除连接查询有什么问题感兴趣。

【问题讨论】:

  • 我诚挚地要求不提供相当于“这是一个仅子查询的方法”的答案/cmets。我的主要目标是了解为什么“左自我排除加入”不起作用。
  • 如果我理解你的问题,我不知道,但你可以测试它删除第一次加入时的“正确”方向吗?也许对你有帮助。
  • @GustavoAdolfo - 我做到了。没有效果(事实上,原始版本没有“正确”连接,这是我尝试解决的问题)
  • 需要看一些数据(文本表格,不是图片)
  • 好的,现在知道了(修改了我的答案)。我确实需要查看数据(谢谢),并且我使用了一些列别名以使其对我更有意义。

标签: sql tsql outer-join


【解决方案1】:

修改后的答案:SQL Fiddle

CREATE TABLE ids (id1 int, id2 int)
CREATE TABLE audit (id2 int, timestamp int, unique_id int)

insert into ids values (1,11)
insert into ids values (1,12)
insert into ids  values (2,23)
insert into audit values (11,101,10000)
insert into audit values (11,104,10001)
insert into audit values (12,102,10002)
insert into audit values (12,103,10003)
insert into audit values (23,101,10004)

查询 1

SELECT
      audit1.*
FROM (
  select data.id1 as data_id, audit.id2 as fk, audit.timestamp, audit.unique_id
  from audit
  INNER JOIN ids data ON audit.id2 = data.id2
) as audit1
LEFT OUTER JOIN (
  select data.id1 as data_id, audit.id2 as fk, audit.timestamp, audit.unique_id
  from audit
  INNER JOIN ids data ON audit.id2 = data.id2
  ) AS audit2 ON audit1.data_id = audit2.data_id 
             AND audit1.timestamp < audit2.timestamp
             AND audit1.unique_id <> audit2.unique_id
WHERE audit2.data_id is null

Results

| data_id | fk | timestamp | unique_id |
|---------|----|-----------|-----------|
|       1 | 11 |       104 |     10001 |
|       2 | 23 |       101 |     10004 |

【讨论】:

  • 对不起,这是错误的。它返回 3 行而不是 2 行。我将测试数据(以及示例小提琴 URL)编辑到问题中
  • 正确。但是,我试图为每个 id1 值获取 1 行,而不是每个 id2 值(这是从 id1 映射的 1-N)
  • 是的,样本数据在审计的 id2 中有 11、12 和 23 = 每个 audit.id2 1 行
  • 除非晚上 11 点让我头疼,否则 11 12 和 23 广告 id2 值。不是id1。 Id1 值是 1 和 2。我也将预期结果集添加到问题中
  • 我想我让步了,列名让我头疼。
【解决方案2】:

链接的 SO 问题中的自排除连接方法的工作方式是,您设置从一个表到自身的左外连接,以便不是您想要的行的每一行都至少有一个链接,而你想要的行没有。所以不是

left outer join audit a2 on i2.id2=a2.id2

你需要类似的东西

left outer join audit a2 on a1.id2=a2.id2 AND a1.timestamp < a2.timestamp

这意味着不是给定 ID 的最高时间戳的每一行都将链接到至少另一个 a2 行,但最高时间戳(您想要的那个)不会。然后使用 WHERE A2.id2 IS NULL 子句删除所有不需要的行。

请注意,随着具有相同 id 的项目数量的增加,会创建更多的中间数据(并被忽略)。 5 个项目有 4 行作为最低时间戳,3 行作为下一个,等等。大小是阶乘 (n - 1),可以很快变大。

【讨论】:

    猜你喜欢
    • 2021-08-06
    • 1970-01-01
    • 2014-11-06
    • 1970-01-01
    • 1970-01-01
    • 2021-10-05
    • 2018-11-30
    • 1970-01-01
    • 2020-08-03
    相关资源
    最近更新 更多