【问题标题】:finding consecutive date pairs in SQL在 SQL 中查找连续的日期对
【发布时间】:2011-08-23 18:28:48
【问题描述】:

我在这里有一个问题,看起来有点像我在搜索中找到的一些问题,但针对稍微不同的问题提供了解决方案,重要的是,这些问题在 SQL 2000 中不起作用。

我有一个非常大的表,其中包含大量冗余数据,我试图将它们减少到有用的条目。这是一个历史表,它的工作方式是,如果两个条目在按日期排序时本质上是重复的并且是连续的,则可以删除后者。当从该条目的生效日期与下一个非重复条目之间的日期请求历史数据时,将使用较早条目中的数据。

数据看起来像这样:

id     user_id effective_date important_value useless_value
1      1       1/3/2007       3               0
2      1       1/4/2007       3               1
3      1       1/6/2007       NULL            1
4      1       2/1/2007       3               0
5      2       1/5/2007       12              1
6      3       1/1/1899       7               0

对于这个样本集,如果user_id 和important_value 相同,我们会认为两个连续的行重复。从这个样本集中,我们将只删除id=2 的行,保留2007 年1 月3 日的信息,显示important_value 在2007 年1 月6 日更改,然后在2 再次显示相关更改-1-2007。

我目前的方法既笨拙又耗时,我知道一定有更好的方法。我编写了一个脚本,该脚本使用游标遍历 user_id 值(因为这会将巨大的表分解为可管理的部分),并为该用户创建一个仅包含行的临时表。然后为了获得连续的条目,它获取临时表,在临时表中没有其他条目且日期介于两个日期之间的条件下将其连接到自身。在下面的伪代码中,UDF_SameOrNull 是一个函数,如果传入的两个值相同或均为 NULL,则返回 1。

WHILE (@@fetch_status <> -1)
BEGIN
  SELECT * FROM History INTO #history WHERE user_id = @UserId

  --return entries to delete
  SELECT h2.id
  INTO #delete_history_ids
  FROM #history h1
  JOIN #history h2 ON
    h1.effective_date < h2.effective_date
    AND dbo.UDF_SameOrNull(h1.important_value, h2.important_value)=1
  WHERE NOT EXISTS (SELECT 1 FROM #history hx WHERE hx.effective_date > h1.effective_date and hx.effective_date < h2.effective_date)

  DELETE h1
  FROM History h1
  JOIN #delete_history_ids dh ON
    h1.id = dh.id 

  FETCH NEXT FROM UserCursor INTO @UserId
END 

它还会遍历同一组重复项,直到没有重复项,因为取出行会创建新的连续对,这些对可能是重复项。为了简单起见,我把它省略了。

不幸的是,我必须使用 SQL Server 2000 来完成这项任务,而且我很确定它不支持 ROW_NUMBER() 以更优雅地查找连续条目。

感谢阅读。对于伪代码中的任何不必要的背景故事或错误,我深表歉意。

【问题讨论】:

  • 对于 8GB 的​​表,您很可能会针对特定用户或用户子集发出查询。是这样吗?
  • 是的,通常只有一个用户。要了解每个用户的数据,对于 7000 个用户,大约有 1000 万行。

标签: sql sql-server tsql sql-server-2000 date


【解决方案1】:

好的,我想我想出了这个问题,很好的问题!

首先,我假设effective_date 列不会为user_id 重复。如果不是这种情况,我认为可以对其进行修改以使其正常工作 - 如果我们需要考虑这一点,请告诉我。

该过程基本上采用相等user_id 和important_value 和先前effective_date 的值和自联接表。然后,我们在 user_id 上再执行 1 次自联接,通过验证这 2 条记录之间没有出现 effective_date 记录,有效地检查上述 2 条联接记录是否是连续的。

现在它只是一个 select 语句 - 它应该选择所有要删除的记录。因此,如果您验证它返回的数据是否正确,只需将 select * 更改为 delete tcheck。

如果您有任何问题,请告诉我。

select 
    * 
from 
    History tcheck
    inner join History tprev
        on  tprev.[user_id] = tcheck.[user_id]
            and tprev.important_value = tcheck.important_value
            and tprev.effective_date < tcheck.effective_date
    left join History checkbtwn
        on  tcheck.[user_id] = checkbtwn.[user_id]
            and checkbtwn.effective_date < tcheck.effective_date
            and checkbtwn.effective_date > tprev.effective_date
where
    checkbtwn.[user_id] is null

【讨论】:

  • 首先,感谢您的回复和标签。这种方法使用原始表而不是简化的临时表。原表很大(根据 sp_spaceused 大约有 8GB 的​​数据),连接 3 次会不会不好管理?
  • 如果索引正确,您可能会对它的执行速度感到惊讶。您可能无法在一个批次中执行此操作。通过用户 ID 组(例如 1-1000、1001-2000 等)来执行此操作可能更有意义。但是,我认为你不需要一次只做一个 user_id。只有一种方法可以找出答案!
【解决方案2】:

好的,伙计们,我昨晚做了一些思考,我想我找到了答案。我希望这有助于其他必须匹配数据中连续对并且由于某种原因也被困在 SQL Server 2000 中的人。

我受到了使用 ROW_NUMBER() 的其他结果的启发,我使用了一种非常相似的方法,但使用了标识列。

--create table with identity column
CREATE TABLE #history (
  id int, 
  user_id int, 
  effective_date datetime, 
  important_value int, 
  useless_value int,
  idx int IDENTITY(1,1)
)

--insert rows ordered by effective_date and now indexed in order
INSERT INTO #history
SELECT * FROM History 
WHERE user_id = @user_id
ORDER BY effective_date

--get pairs where consecutive values match
SELECT * 
FROM #history h1
JOIN #history h2 ON
  h1.idx+1 = h2.idx
WHERE h1.important_value = h2.important_value

使用这种方法,我仍然需要迭代结果,直到它什么都不返回,但我想不出任何解决方法,而且这种方法比我的上一种方法领先几英里。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-17
    • 1970-01-01
    • 1970-01-01
    • 2021-08-19
    • 1970-01-01
    • 2019-03-24
    • 2021-08-07
    相关资源
    最近更新 更多