【问题标题】:number range format conversion数字范围格式转换
【发布时间】:2019-01-13 03:20:52
【问题描述】:

给定一系列蜂窝电话号码,格式为 ['7810000000', '7819999999'],我需要一种算法,该算法可以生成多行最小可能长度的行,如果后缀为 '%',它将完全覆盖给定范围。例如,上面的范围将表示为单行“781”。换句话说,该范围内的任何数字都可以表示为 781%。例如,此表示可用于存储 tarrifs。数据库中的单行可用于对整个范围进行评级。还有许多其他任务最好采用这种格式。 ['526251630000','526251634999'] 范围的算法会产生

52625160
52625161
52625162
52625163
52625164

对于范围 ['12300345','12367000'] 我们应该得到

12300345
12300346
12300347
12300348
12300349
1230035
1230036
1230037
1230038
1230039
123004
123005
123006
123007
123008
123009
12301
12302
12303
12304
12305
12306
12307
12308
12309
1231
1232
1233
1234
1235
12361
12362
12363
12364
12365
12366
12367000

我们需要在 Oracle(SQL/PLQSL) 中完成这种转换。任何信息或链接将不胜感激。提前谢谢你。

【问题讨论】:

    标签: sql oracle algorithm range cellular-network


    【解决方案1】:

    蛮力方法,将范围开始/结束作为绑定变量:

    var start_val varchar2(14);
    var end_val varchar2(14);
    
    exec :start_val := '526251630000';
    exec :end_val := '526251634999';
    
    with dns (dn) as (
      select to_number(:start_val) + level - 1
      from dual
      connect by level <= to_number(:end_val) - to_number(:start_val) + 1
    ),
    exponents (ex) as (
      select level - 1 from dual
      connect by level <= length(:start_val)
    ),
    tmp (dn, ex, pow, root, cnt ) as (
      select d.dn,
        e.ex,
        power(10, e.ex),
        trunc(d.dn / power(10, e.ex)),
        count(d.dn) over (partition by trunc(d.dn / power(10, e.ex)))
      from dns d
      cross join exponents e
    )
    select distinct to_char(min(root) keep (dense_rank first order by ex desc)
        over (partition by dn)) as result
    from tmp
    where pow = cnt
    order by result;
    

    得到

    RESULT                                  
    ----------------------------------------
    526251630
    526251631
    526251632
    526251633
    526251634
    

    dns CTE 将范围扩展到它包含的所有值。 exponents CTE 生成您可能想要使用的所有可能的 10 次幂(即从范围值的右侧删除多少位数)。然后tmp CTE 将这些交叉连接并计算每个“子范围”中出现的数量,通过除以 10 的幂计算得出。最后的查询然后过滤整个子范围 - 其中值的数量该范围与子范围的大小相同,因此不会丢失拆分 - 并为每个 DN 找到最小(即最短)的子范围根。然后最终得到它们的不同值。

    对于第二个范围:

    exec :start_val := '12300345';
    exec :end_val := '12367000';
    

    同样的查询得到:

    RESULT                                  
    ----------------------------------------
    12300345
    12300346
    12300347
    12300348
    12300349
    1230035
    1230036
    1230037
    1230038
    1230039
    123004
    123005
    123006
    123007
    123008
    123009
    12301
    12302
    12303
    12304
    12305
    12306
    12307
    12308
    12309
    1231
    1232
    1233
    1234
    1235
    12360
    12361
    12362
    12363
    12364
    12365
    12366
    12367000
    

    CTE 和交叉连接意味着这需要做很多工作,随着范围变大,它会变慢 - 可能会失败。

    我确信有更有效的方法可以做到这一点,希望有人会想出一个,但这可能会让你开始。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-12
      • 2016-02-29
      • 1970-01-01
      • 2022-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多