如何在PL / SQL中比较整数值的范围?

时间:2018-11-18 15:32:11

标签: sql oracle plsql compare between

我正在尝试比较测试表和参考表之间的整数值范围。如果测试表中的任何值范围与参考表中的可用范围重叠,则应将其删除。

很抱歉,虽然不清楚,但是下面是示例数据:

TEST_TABLE:

MIN          MAX
10           121
122          648
1200         1599

REFERENCE_TABLE:

MIN          MAX
50           106
200          1400
1450         1500

修改后的测试表:(运行PL / SQL后的预期结果)

MIN          MAX
10           49
107          121
122          199
1401        1449
1501        1599

在上面示例的第一行中,将10-121分为两行:10-49和107-121,因为值50、51,...,106包含在reference_table(50-106);等等。

这是到目前为止我用嵌套循环编写的内容。我创建了两个额外的临时表,这些临时表将存储在引用表中可以找到的所有值。然后它将创建要插入到test_table的新范围集。

但这似乎无法正常工作,并且 可能会导致性能问题,尤其是当我们要处理数百万甚至更高的值时:

CREATE TABLE new_table (num_value NUMBER);
CREATE TABLE new_table_next (num_value NUMBER, next_value NUMBER);

-PL / SQL启动

DECLARE
  l_count NUMBER;
  l_now_min NUMBER;
  l_now_max NUMBER;

  l_final_min NUMBER;
  l_final_max NUMBER;

BEGIN

  FOR now IN (SELECT min_num, max_num FROM test_table) LOOP
    l_now_min:=now.min_num;
    l_now_max:=now.max_num;
    WHILE (l_now_min < l_now_max) LOOP

      SELECT COUNT(*) -- to check if number is found in reference table
      INTO l_count
      FROM reference_table refr
      WHERE l_now_min  >= refr.min_num
      AND l_now_min   <= refr.max_num;

      IF l_count        > 0 THEN

        INSERT INTO new_table (num_value) VALUES (l_now_min);
        COMMIT;

      END IF;

      l_now_min:=l_now_min+1;

    END LOOP;

    INSERT INTO new_table_next (num_value, next_value)
    VALUES (SELECT num_value, (SELECT MIN (num_value) FROM new_table t2 WHERE t2.num_value > t.num_value) AS next_value FROM new_table t);

    DELETE FROM test_table t
    WHERE now.min_num   = t.min_num
    AND now.max_num   = t.max_num;
    COMMIT;

    SELECT (num_value + 1) INTO l_final_min FROM new_table_next;
    SELECT (next_value - num_value - 2) INTO l_final_max FROM new_table_next;

    INSERT INTO test_table (min_num, max_num)
        VALUES (l_final_min, l_final_max);
    COMMIT;

    DELETE FROM new_table;
    DELETE FROM new_table_next;

    COMMIT;
  END LOOP;
END;
/

请帮助,我被困住了。 :)

4 个答案:

答案 0 :(得分:1)

此方法背后的想法是展开两个表,并跟踪数字是在参考表中还是在原始表中。这确实很麻烦,因为相邻的值可能会引起问题。

然后,我们的想法是在两个维度上都执行“间隙和孤岛”类型的解决方案-然后仅保留原始表中的值,而不保留第二个表中的值。也许这可以称为“排他性孤岛与孤岛”。

这是一个有效的版本:

public void InitializeLevelUp()
{
    StartCoroutine(LevelUp());
}
public IEnumerator LevelUp()
{
     playerLevelText.text = ("You have gained a level!");
     strenghtAttribute++;
     intellectAttribute++;
     playerLevel++;
     yield return new WaitForSeconds(2f);
     playerLevelText.text = "";
     //alternatively, set the text object inactive   
}

here是db <>小提琴。

获得重叠的反问题要容易得多。将参考表“反转”以进行处理可能是可行的。

with vals as (
      select min as x, 1 as inc, 0 as is_ref
      from test_table
      union all
      select max + 1, -1 as inc, 0 as is_ref
      from test_table
      union all
      select min as x, 0, 1 as is_ref
      from reference_table
      union all
      select max + 1 as x, 0, -1 as is_ref
      from reference_table
     )
select min, max
from (select refgrp, incgrp, ref, inc2, min(x) as min, (lead(min(x), 1, max(x) + 1) over (order by min(x))  - 1) as max
      from (select v.*,
                  row_number() over (order by x) - row_number() over (partition by ref order by x) as refgrp,
                  row_number() over (order by x) - row_number() over (partition by inc2 order by x) as incgrp
            from (select v.*, sum(is_ref) over (order by x, inc) as ref,
                         sum(inc) over (order by x, inc) as inc2
                  from vals v
                 ) v
           ) v
      group by refgrp, incgrp, ref, inc2
     ) v
where ref = 0 and inc2 = 1 and min < max
order by min;

答案 1 :(得分:1)

这是从我在Teradata上执行的类似任务(使用日期而不是数字)修改而成的,它基于与Gordon相同的基础数据(所有开始/结束值组合在一个列表中),但是使用了更简单的逻辑:

function update(p1,p2) {
    const c=document.getElementById("screen");
    const ctx=c.getContext("2d");
    requestAnimationFrame(update); // calls the inner update to start the animation

    function update() { // this inner function remebers variables from the outer function
                        // which is called closing over, or closure.
                        // That means that p1,p2, c, and ctx are remembered by this function
                        // as long as it keeps calling  requestAnimationFrame(update)
      clearCanvas(ctx);

      p1.movement();
      p2.movement();

      p1.draw(ctx);
      p2.draw(ctx);

      p1.removeBlast();
      p2.removeBlast();

      for(i=0;i<p1.blastCount;i++)
      {
          p1.blastList[i].draw(ctx);
      }
      for(i=0;i<p2.blastCount;i++)
      {
          p2.blastList[i].draw(ctx);
      }

      requestAnimationFrame(update);  // calls the inner function
  }
}

请参见db-fiddle

答案 2 :(得分:0)

这是执行此操作的一种方法。我将测试数据放在WITH子句中,而不是创建表(我发现这样做更容易进行测试)。我使用了您的列名(MIN和MAX);但是,这些都是非常差的选择,因为MIN和MAX是Oracle关键字。它们肯定会引起混乱,并且可能导致查询错误。

策略很简单-首先对REFERENCE_TABLE中的范围进行补全,这也将是间隔的并集(使用NULL作为负无穷大和正无穷大的标记);然后将TEST_TABLE中的每个间隔与REFERENCE_TABLE的补码中的每个间隔相交。下面的解决方案的最终(外部)查询中显示了如何完成此操作。

with
  test_table (min, max) as (
    select   10,  121 from dual union all
    select  122,  648 from dual union all
    select 1200, 1599 from dual
  )
, reference_table (min, max) as (
    select   50,  106 from dual union all
    select  200, 1400 from dual union all
    select 1450, 1500 from dual
  )
, 
  prep (min, max) as (
    select lag(max) over (order by max) + 1 as min
         , min - 1                          as max
      from ( select min, max   from reference_table
             union  all
             select null, null from dual
           )
  )
select   greatest(t.min, nvl(p.min, t.min)) as min
     ,   least   (t.max, nvl(p.max, t.max)) as max
from     test_table t inner join prep p
                      on  t.min <= nvl(p.max, t.max)
                      and t.max >= nvl(p.min, t.min)
order by min
;

       MIN        MAX
---------- ----------
        10         49
       107        121
       122        199
      1401       1449
      1501       1599

答案 3 :(得分:0)

解决问题的示例:

CREATE TABLE xrange_reception
(
    vdeb    NUMBER,
    vfin    NUMBER
);

CREATE TABLE xrange_transfert
(
    vdeb    NUMBER,
    vfin    NUMBER
);

CREATE TABLE xrange_resultat
(
    vdeb    NUMBER,
    vfin    NUMBER
);

insert into xrange_reception values (10,50);

insert into  xrange_transfert values (15,25);
insert into  xrange_transfert values (30,33);
insert into  xrange_transfert values (40,45);

DECLARE
    CURSOR cr_rec IS SELECT * FROM xrange_reception;

    CURSOR cr_tra IS
          SELECT *
            FROM xrange_transfert
        ORDER BY vdeb;

    i              NUMBER;
    vdebSui        NUMBER;
BEGIN
    FOR rc IN cr_rec
    LOOP
        i := 1;
        vdebSui := NULL;

        FOR tr IN cr_tra
        LOOP
             IF tr.vdeb BETWEEN rc.vdeb AND rc.vfin
            THEN
                IF i = 1 AND tr.vdeb > rc.vdeb
                THEN
                    INSERT INTO xrange_resultat (vdeb, vfin)
                         VALUES (rc.vdeb, tr.vdeb - 1);
                ELSIF i = cr_rec%ROWCOUNT AND tr.vfin < rc.vfin
                THEN
                    INSERT INTO xrange_resultat (vdeb, vfin)
                         VALUES (tr.vfin, rc.vfin);
                ELSIF vdebSui < tr.vdeb
                THEN
                    INSERT INTO xrange_resultat (vdeb, vfin)
                         VALUES (vdebSui + 1, tr.vdeb - 1);
                END IF;

                vdebSui := tr.vfin;
                i := i + 1;
            END IF;
        END LOOP;

        IF vdebSui IS NOT NULL THEN
            IF vdebSui < rc.vfin 
            THEN
                INSERT INTO xrange_resultat (vdeb, vfin)
                     VALUES (vdebSui + 1, rc.vfin);
            END IF;

        ELSE
            INSERT INTO xrange_resultat (vdeb, vfin)
                 VALUES (rc.vdeb, rc.vfin);
        END IF;
    END LOOP;
END;

所以:

表xrange_reception:

vdeb  vfin
10    50

表xrange_transfert:

vdeb  vfin
15    25
30    33
40    45

表xrange_resultat:

vdeb  vfin
10    14
26    29
34    39
46    50