【问题标题】:SQL - Can Cursors be avoided in this case?SQL - 在这种情况下可以避免游标吗?
【发布时间】:2023-03-06 16:57:01
【问题描述】:

如果对以下内容很重要,将对查询本身使用 Netezza 后端 + SPSS Modeler 和/或 Advanced Query Tool。我无法访问 CLI。我试图了解是否需要游标和遍历排序表来处理以下内容:

想象一个有 2 列的表,第一列是非唯一 ID,第二列是日期。任何给定的 ID 都可能在一个或多个日期出现在表中多次。

我的目标是从该表中选择日期间隔不少于固定天数(例如 90 天)的行。例如:

| ID |  DATE      |
===================
  X    2014-01-01
  X    2014-02-01
  X    2014-07-01
  Y    2014-02-01
  Y    2014-06-01
  Y    2014-07-01

在上面的示例中,我要为 X 选择的行将是 Jan 1 和 Jul 1(不包括 2 月 1 日,因为它距 1 月 1 日不到 90 天),而 Y 的行将是 Feb 1 和 Jun 1 (排除 7 月 1 日,因为它在前一个案例的 90 天内。

实际上,表中可能有超过 1 亿行。没有游标可以做到这一点吗?最佳方法是什么?

提前感谢您的任何建议!

编辑:在此处扩展了测试表数据。 SQL Fiddle

在上面编辑的示例中,所需的输出将是

| ID |  DATE      |
===================
  X    2014-01-01
  X    2014-04-01
  X    2014-10-01
  Y    2014-01-15
  Y    2014-04-15
  Y    2014-10-15
  Z    2014-01-01
  Z    2014-04-01
  Z    2014-10-01

【问题讨论】:

  • 那么当您说间隔不少于 90 天时,即从特定 ID 的最低日期算起 90 天?
  • 例如,如果您有一行 X 2014-10-01,该怎么办?在这种情况下,您希望选择哪些行?
  • 好问题 - Shree.pat18) 是的,在我发布的示例中,我想从给定 ID 的最低值开始,但在实际实施中,我们可能需要做相反的事情并从最高值开始.我只是为这个例子选择了一个方向。 Nizam) 如果有行 X 2014-10-01,那么选择 ID=X 的行将是 Jan 1、Jul 1 和 Oct 1,因为 Oct 1 距离 2014-07-01 超过 90 天。
  • 我认为您可以删除 sql-server 和 mysql 标签。例如,您会接受仅适用于 sql-server 的答案吗?使用 CTE 递归、CROSS APPLY 等...?
  • 是否可以使用变量表(sql-server)?

标签: mysql sql sql-server recursion netezza


【解决方案1】:

如果您接受在 SQL Server 中有效的内容,则以下代码将有效:

With CTE as (
    select A.ID, A.DATA, MIN(B.DATA) DATA1 
    from Table1 A
    inner join Table1 B
      on A.ID = B.ID
      and DATEADD(DAY, 90, A.DATA) <= B.DATA
    GROUP BY A.ID, A.DATA
), REC AS (
   SELECT ID, MIN(DATA) DATA
   FROM Table1
   GROUP BY ID
   UNION ALL
   SELECT A.ID, B.DATA1
   FROM REC A
   INNER JOIN CTE B
     ON A.ID = B.ID
     AND A.DATA = B.DATA
)

SELECT *
FROM REC
ORDER BY ID, DATA

它是 CTE 的用户递归。通过为每个 ID 选择最小日期,递归始终采用大于 90 天的最小日期。但这仅适用于 SQL Server。

SQL Fiddle

更新

当您获得在其他地方实施的想法时,有不止一种方法来实施它是很有趣的。在 SQL Server 中也可以在 TSQL 中实现这种方式:

DECLARE @TABLE1 TABLE (ID VARCHAR(1), DATA DATE, DATA1 DATE)

  INSERT INTO @TABLE1
    select A.ID, A.DATA, MIN(B.DATA) DATA1 
    from (
       SELECT ID, MIN(DATA) DATA
       FROM Table1
       GROUP BY ID
    ) A
    inner join Table1 B
      on A.ID = B.ID
      and DATEADD(DAY, 90, A.DATA) <= B.DATA
    GROUP BY A.ID, A.DATA

DECLARE @AUX INT = 0

WHILE (SELECT COUNT(*) FROM @TABLE1) <> @AUX
BEGIN

  SELECT @AUX = COUNT(*) FROM @TABLE1

  INSERT INTO @TABLE1
    select *
    from ( 
       select A.ID, A.DATA, MIN(B.DATA) DATA1 
       from @Table1 A
       inner join Table1 B
         on A.ID = B.ID
         and DATEADD(DAY, 90, A.DATA1) <= B.DATA
       GROUP BY A.ID, A.DATA    
    ) A
    where not exists (
        SELECT 1
        FROM @TABLE1
        WHERE ID = A.ID
         AND DATA = A.DATA
         AND DATA1 = A.DATA1
    )

END

SELECT ID, DATA
FROM @TABLE1
UNION
SELECT ID, DATA1
FROM @TABLE1
UNION
SELECT ID, MIN(DATA) DATA
FROM Table1
GROUP BY ID

SQL Fiddle

【讨论】:

  • 感谢您的反馈。我对 netezza 很陌生,在任何实现中都有一些事情要做至少是一个开始的地方......我现在正在检查 NZSQL 文档,看看它是否支持类似的东西。
  • 你的答案中第一组sql代码没有发现gap,它只找到相隔90天以上的记录,如果你通过了1-1-2014、2-1-2014的记录, 2014 年 3 月 1 日、2014 年 4 月 1 日、2014 年 5 月 1 日,即使 4-1 和 5-1 之间的差距小于 90 天,它也会标记 1-1-2014 和 5-1-2014。
  • 你的第二组代码有同样的问题,根本不处理间隙。
  • 它将标记 2014-01-01 和 2014-04-01,就像 OP 指定的一样(请参阅sqlfiddle.com/#!3/4a5691/1
  • 我正在努力。到目前为止,NZSQL 似乎不支持通过 CTE 进行递归,但我在 SQLFiddle 中同意,前提是该解决方案似乎确实产生了正确的结果。现在分析第二个解决方案,我会尽快报告。我会投票,但我之前从未使用过 SE(为这篇文章创建)并且低于 15 noob-check。
【解决方案2】:

我找到了一个成功的 SPSS/Netezza 原生迭代解决方案。 SPSS 支持@OFFSET(Field, integer) 函数,可以提前或后面读取。我之前曾尝试使用此函数,但在尝试使用等于同一字段的“字段”时遇到了与递归相关的错误,该函数的结果与负整数一起读取先前的结果。

今天在另一个项目中工作时,我发现 @OFFSET() 的文档很差,虽然我相信它可以从代表预读的正整数从第一到最后的行顺序运行,但这实际上是倒退的。它实际上是从最后一行到第一行解决的,正整数偏移实际上意味着读取前一行。使用我的原始方法重试并更正整数偏移量上的符号消除了递归错误并解决了问题。

实际的解决方案可以是这样的。为了清楚起见,这个描述过于冗长,实际上这些项目中的大多数都可以浓缩到同一个步骤中,并且它忽略了一个 ID 具有多个相同日期实例的可能性(这不难处理使用额外的比较中的逻辑,但对我的需要不是必需的)。

  1. 在 ID 上选择排序集,然后选择日期。
  2. 通过@OFFSET(ID, 1) 定义一个新的列PR_ID来存储上一行的ID
  3. 定义一个名为 LST_CNT_DT 的新列(最后一个可数列 date) 如果 PR_ID ID 则包含当前行的日期。否则, 比较当前行日期与天数之间的差异 @OFFSET(LST_CNT_DT,1) [即相同字段的前一行的值],如果差值 >=90,则存储当前行的日期。否则存储@OFFSET(LST_CNT_DT,1)。
  4. 从此新集中选择 LST_CNT_DT = DT 的所有行(其中 DT 是当前行的日期)。

不像 MsSQL 中可用的 CTE 递归方法那样优雅,但可以完全在 SPSS v15 中构建,并在几分钟内在整个表上执行。

【讨论】:

    猜你喜欢
    • 2011-12-06
    • 1970-01-01
    • 2011-05-08
    • 2023-03-24
    • 1970-01-01
    • 2020-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多