【问题标题】:DB query or logic optimization needed需要数据库查询或逻辑优化
【发布时间】:2017-04-02 03:37:15
【问题描述】:

我正在对 200 万条记录执行以下查询 (oracle11g)。大约需要 2.2 秒。

我的查询:

select SUBSCRIBER_NUM, SUBSCRIBER_STATUS, P_ID
from C_S_FORWARD_INFO
where '07052620' LIKE SUBSCRIBER_NUM || '%'
  and SCP_VER = 1

下面是我的桌子。

CREATE TABLE C_S_FORWARD_INFO 
  ( 
    SUBSCRIBER_NUM                  VARCHAR2(30 BYTE) NOT NULL, 
    P_ID                            NUMBER, 
  SUBSCRIBER_STATUS NUMBER(1,0) DEFAULT 0 NOT NULL,
    ACCOUNT_NUMBER                  INTEGER NOT NULL,   
    MAJOR_VERSION_ID                NUMBER(10,0) DEFAULT 1 NOT NULL, 
    MINOR_VERSION_ID             NUMBER(10,0) DEFAULT 1 NOT NULL, 
    SCP_VER                         NUMBER(1,0) DEFAULT 0 CHECK (SCP_VER IN (0,1)), 
  );

ALTER TABLE C_S_FORWARD_INFO ADD CONSTRAINT C_S_FORWARD_INFO_PK
PRIMARY KEY (SUBSCRIBER_NUM,ACCOUNT_NUMBER,MAJOR_VERSION_ID, MINOR_VERSION_ID);

数据库记录(例如,实际上它有 200 万条记录)

Row 1 => 07052620,1,1,10, 1, 1, 1;
Row 2 => 0705262,2,1,10, 1, 1, 1;
Row 3 => 070526,3,1,10, 1, 1, 1;
Row 4 => 070526200001,4, 1,10, 1, 1, 1;
Row 5 => 07052,5,1,10, 1, 1, 1;
......

预期结果:第 1 行(我通过上面的查询得到这个,逻辑:从最长匹配开始,直到 '07052620')

如何优化上述查询。或编写任何其他逻辑以在.2 秒内获得预期结果。在我的查询中,'07052620' 是作为存储过程输入的动态数字。

20/11 - 更新:

我在下面尝试过(VAR_CALLING_NUM = 07052620):

while var1<=len LOOP 
temp1 := SUBSTR(VAR_CALLING_NUM, 1, var1); 
temp1 := concat('''',temp1); 
temp1 := concat(temp1,''''); 
temp6 := temp6 || temp1 || ',' ; 
var1:=var1+1; 
END LOOP; 
temp6 := SUBSTR(temp6, 1,length(temp6)-1);

select SUBSCRIBER_NUM, SUBSCRIBER_STATUS, P_ID from C_S_FORWARD_INFO where SUBSCRIBER_NUM IN ( temp6 ) and SCP_VER = 1 order by length(subscriber_num) desc; 

但这并没有给我结果。看起来查询没有动态采用 temp6。请帮忙

【问题讨论】:

  • 您当前的查询存在逻辑问题,此外您还有优化问题。你想解决哪一个?
  • 您希望表中满足这些条件的行的比例是多少?
  • 另外,数据集中的订户号码有多少个唯一值,输入号码的变化有多大?会不会是“0”?还是总是至少有这么多字符?
  • @David Aldridge :查询应该给我第 1、2、3 和第 5 行。它不应该给我第 4 行。目前它没有给出任何记录。我怀疑我在 'IN' 中动态传递的 temp6,查询没有接受它。但是,temp6 是“'0', '07', '070', '0705', '07052', '070526', '0705262', '07052620'”。但不确定查询是否是动态的,可能是我做错了(SUBSCRIBER_NUM IN(temp6))。请帮忙。
  • 在实际情况下输入的数字不会是0。它最多可以是30位数字。但是,没有最低限制。

标签: sql oracle stored-procedures database-performance


【解决方案1】:

如果这是您的查询:

select SUBSCRIBER_NUM, SUBSCRIBER_STATUS, P_ID
from C_S_FORWARD_INFO
where '07052620' LIKE SUBSCRIBER_NUM || '%' and
      SCP_VER = 1;

然后,我建议将其重写为:

select SUBSCRIBER_NUM, SUBSCRIBER_STATUS, P_ID
from C_S_FORWARD_INFO
where SUBSCRIBER_NUM IN ('0', '07', '070', '0705', '07052', '070526', '0705262', '07052620') and
      SCP_VER = 1
order by length(subscriber_num) desc
fetch first 1 row only;

然后,在(SCP_VER, SUBSCRIBER_NUM)上添加一个索引:

create index idx_forwardinfo_2 on c_s_forward_info(SCP_VER, SUBSCRIBER_NUM);

【讨论】:

  • 谢谢,但这个号码 (07052620) 可以是 30 位数字,所以在查询中我们将在 'IN' 中有 30 个组合
  • 请求您的意见
  • @VJS 。 . .没关系。 Oracle 仍应使用索引。
  • 我在下面尝试过(VAR_CALLING_NUM = 07052620):while var1
  • @ Gordon Linoff :在问题中添加了我尝试过的内容。请求您帮忙。
【解决方案2】:

以下查询向您的原始尝试添加了一个子查询,它将结果限制为匹配的结果,但也限制为最长的匹配项。它任意返回一条记录,但如果您想返回最长匹配的平局,则可以轻松修改它。

select SUBSCRIBER_NUM,
       SUBSCRIBER_STATUS,
       P_ID
from C_S_FORWARD_INFO
where '07052620' LIKE SUBSCRIBER_NUM || '%'  and
      SCP_VER = 1 and
LENGTH(SUBSCRIBER_NUM) = (select max(length(SUBSCRIBER_NUM)) from C_S_FORWARD_INFO
                          where '07052620' like SUBSCRIBER_NUM || '%' and SCP_VER = 1)
offset 0 rows fetch next 1 rows only;

【讨论】:

  • 谢谢。让我试试。只是一件事,我是数据库新手,如何将索引添加到 SUBSCRIBER_NUM 列。需要运行任何查询吗?抱歉,如果您发现它是有线问题。
  • 我应该执行:为索引创建索引 C_S_FORWARD_INFO_IDX ON C_S_FORWARD_INFO (SUBSCRIBER_NUM)
  • @Tim Biegeleisen:我没有使用您的查询获得预期结果。案例:如果 DB 中不存在第 1 行,那么我的预期结果是第 2 行,因为 0705262 是最长的匹配开头。你能看看吗。使用您的查询,我得到了第 4 行,这不是预期的。
  • 如果你真的只想要第一条记录,那么通配符有什么意义呢?只需使用等于。
  • @Tim Biegeleisen:我需要从最长匹配开始。通过平等是不可能的。在查询中,我们有“where '07052620'”,所以我预期的行选择是 0、07、070、0705、07052、070526、0705262、07052620,因为 07052620 是最长的匹配,我会选择这个。
【解决方案3】:

这可能会或可能不会帮助您解决性能问题:

WITH pred_vals AS (SELECT SUBSTR('07052620', 1, LENGTH('07052620') + 1 - LEVEL) str,
                          LEVEL priority
                   FROM   dual
                   CONNECT BY LEVEL <= LENGTH('07052620')),
     main_join AS (SELECT fi.subscriber_num,
                          fi.subscriber_status,
                          fi.p_id
                          row_number() OVER (ORDER BY pv.priority) rn
                   FROM   c_s_forward_info fi
                          INNER JOIN pred_vals pv ON (fi.subscriber_num = pv.str)
                   WHERE  scp_ver = 1)
SELECT subscriber_num,
       subscriber_status,
       p_id
FROM   main_join
WHERE  rn = 1;

我建议您在以下列的 c_s_forward_info 上建立一个多列索引:(subscriber_num, scp_ver,subscriber_status, p_id)

这有望让查询仅针对索引运行。

该查询首先将您传递的字符串分解为您想要再次匹配的各种组合(这是字符串中的前 N ​​个字符,其中 N 是介于 1 和字符串长度之间的任意值传入)。

一旦我们有了这些要匹配的字符串,我们就可以将它们直接连接到订阅者列,这将允许优化器在认为这样做更有效时使用索引。

然后,我们可以计算 row_number()(或者如果要显示与最高优先级连接行匹配的所有行,也可以计算 dense_rank()),然后选择最上面的行。

【讨论】:

    猜你喜欢
    • 2021-06-28
    • 2014-02-15
    • 1970-01-01
    • 1970-01-01
    • 2016-02-02
    • 2014-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多