Oracle更快的重叠检查

时间:2012-04-12 13:15:57

标签: sql database oracle

我有一个包含两列beginrange和endrange的表。不应该允许重叠范围。这些列上有索引,我们尝试了许多sql条件,如

inputBegin between beginRange and endRange or
inputEnd between beginRange and endRange 

not ( inputEnd < beginRange or inputStart > endRange )

等 哪个工作正常,除非它们非常慢,因为该表包含超过5mil的记录。

有没有写一个高效的重叠检查?

修改: 我想到了另外一个解决方案,只有当count()在带有索引的NOT NULL列上完成时,oracle才会计算索引。如果beginRange和endRange为NOT NULL且两者都有索引,我们可以有三个总和:

count(endRange) where inputBegin > endRange
+
count(beginRange) where inputEnd < beginRange
=
count(beginRange/endRange)

所以使用UNION ALL我会得到三行,在代码中我需要检查前两个的总和是否等于第三个。当然我假设只计算索引,不会访问任何行。还有别的吗?

4 个答案:

答案 0 :(得分:1)

我不确定你是否愿意:

  1. 检查您要插入的行是否与某些现有行重叠,或
  2. 搜索所有现有行并识别重叠的行?

  3. 如果(1),那么你基本上已经在做什么......

    SELECT *
    FROM YOUR_TABLE
    WHERE :inputEnd > beginRange AND :inputStart < endRange;
    

    ...会给你重叠并且应该非常高效,只要你有一个复合索引,其组件在相反方向:{beginRange ASC, endRange DESC}


    如果(2),那么您可以使用这样的窗口:

    SELECT *
    FROM (
        SELECT
            YOUR_TABLE.*,
            LEAD(beginRange) OVER (ORDER BY beginRange) nextBeginRange
        FROM YOUR_TABLE
    )
    WHERE endRange > nextBeginRange;
    

    这将为您提供与其下一个范围重叠的每个范围(其中“next”的含义在beginRange排序的上下文中定义)。

    严格来说,这甚至不需要复合索引(除非你想要covering) - 只需{beginRange}上的一个简单索引即可确保良好的性能。

答案 1 :(得分:1)

这是一个答案 - 如果可以做出某些断言:

您有一个包含beginRangeendRange列的表格,其中没有两个现有行重叠(beginRange, endRange)

您想要使用(inputStart, inputEnd)插入新行,但请检查它是否与表格中的任何现有行重叠。

然后你可以使用这个应该很快的条件 - 使用startRange上的简单索引:

WHERE input_Start <
      ( SELECT endRange
        FROM
          ( SELECT endRange
                 , ROW_NUMBER() OVER(ORDER BY startRange DESC) AS rn 
            FROM tableX
            WHERE startRange < input_End
          ) tmp
        WHERE rn = 1
      )


  --- TRUE  --> Overlaps
  --- FALSE --> No overlap

答案 2 :(得分:0)

没有一个索引可以满足此查询。这实际上意味着您最好创建两个索引,并运行两个查询,然后UNIONing结果......

1)在InputBegin上创建索引
  2)在InputEnd上创建单独的索引   3)运行以下查询

SELECT * FROM yourTable WHERE InputEnd   < ExclusionPeriodStart 
UNION ALL
SELECT * FROM yourTable WHERE InputBegin > ExclusionPeriodEnd

然后,第一个查询可以在InputEnd索引上使用范围搜索。 然后第二个查询也可以使用范围搜索,但是使用不同的索引。

通过将查询分开,两个不同的要求不会相互干扰,并且可以使用最佳索引。

您也已经知道(通过了解您的数据)结果中没有重叠(没有记录可以在完成之前启动,因此两个查询中都不会出现记录)。这意味着可以使用UNION ALL代替较慢的UNION

据我所知,没有办法比这更快地执行此查询。 (在5米的记录中,在小型数据集上扫描整个表格可能更快。)


编辑该答案假定您正在尝试查找未出现在固定范围内的所有记录。如果你想针对其他每条记录检查每条记录,那么你需要一种不同的方法......

检查每个重叠都很昂贵。此外,如果您有这四个范围,那么找出要删除的范围是不可能的......

1 -->--> 4
      3 -->--> 6
            5 -->--> 8
                  7 -->--> 9

您应该删除范围1和3,还是2和4?

你可以做的是找到另一个范围与它们重叠的所有范围。

你不想要的是发现A与B重叠,而B与A重叠。

SELECT
  *
FROM
  yourTable   AS first_range
INNER JOIN
  yourTable   AS second_range
    ON  second_range.start_date >= first_range.start_date
    AND second_range.start_date <= first_range.end_date

这将直接扫描整个表格的first_range。但是因为你只检查第二个范围的start_date,所以它可以在start_date索引上使用范围搜索来解决任何冲突。

EDIT2 :或者您可能需要与第一个答案相反?

如果您希望的所有范围与设定范围发生冲突,则可以对相同方法进行修改。

SELECT * FROM yourTable WHERE InputEnd   >= ExclusionPeriodStart 
INTERSECT
SELECT * FROM yourTable WHERE InputBegin <= ExclusionPeriodEnd
然而,这可能不是很好。您将在query1中占用该表的一定百分比,并将其与几乎所有表的其余部分相交。相反,您可以依靠简单的方法,但随后添加优化...

SELECT
  *
FROM
  yourTable
WHERE
    InputStart <= ExclusionPeriodEnd
AND InputEnd   >= ExclusionPeriodStart

WHERE子句中的第一个条件可以使用范围搜索来解析,然后扫描所有结果记录以测试第二个条件。那么,我们是否可以缩小扫描范围(currently (start of table) -> (ExclusionPeriodEnd))

我们 如果 我们知道一条额外的信息:任何一个范围的最大长度......

SELECT
  *
FROM
  yourTable
WHERE
    InputStart <= ExclusionPeriodEnd
AND InputStart >= ExclusionPeriodStart - (maximumLength)
AND InputEnd   >= ExclusionPeriodStart

现在,前两个条件形成一个范围搜索,并提供一个小得多的数据集来扫描最后一个条件。

你怎么知道最大的lnegth?你可以扫描整个表格,但这是一次优化的自我失败尝试。

相反,您可以索引计算字段;给出范围最大长度的计算。 SELECT MAX(calculatedField) FROM yourTable然后避免扫描整个表格。或者你可以用触发器跟踪。这对于INSERTS来说很好,但是当你有一个删除时有点麻烦(如果你删除最长的范围,你是否再次扫描整个表以找到新的最长范围?可能不是,你可能想要保持旧的最大长度代替)。

答案 3 :(得分:0)

假设现有范围不重叠,则{beginRange}应该是(主要或备用)密钥,并检测新范围是否与某些现有范围重叠可以像这样:

SELECT *
FROM YOUR_TABLE
WHERE beginRange = (
    SELECT MAX(beginRange)
    FROM YOUR_TABLE
    WHERE beginRange < :inputEnd
)
AND :inputStart < endRange
  • 如果新范围与某些现有范围重叠,则此查询将返回“最高”范围。
  • 如果没有重叠,则返回空结果集。

{beginRange}键下面的索引足以提高效率(我们只需要支持“MAX扫描”)。