在大SQL表中选择条目数的最快方法是什么?

时间:2014-03-02 13:43:53

标签: sql database oracle plsql

我有一张200米行的桌子 让我们调用表格employee_internet_history 行:employee_fullname || website || date || more data
该表在employee_fullname列上有一个索引。

我还有另一张桌子eu_employees 有100行;每一行:employee_fullname || more data

我想创建一个查询,以选择每位员工浏览的前3个网站。

我正在使用Oracle Database所以我考虑使用PL/SQL来实现这一目标。 目前我正在使用

 declare
   cursor top100workers is 
      select * from eu_employees
      where rownum < 100;

 begin
    for worker in top100workers
      LOOP
         DBMS_OUTPUT.PUT_LINE(worker.employee_fullname ||' top 3 webpages:');
         for TOP3 in (
             SELECT  /*+ parallel*/ website,
            COUNT(website) AS num
            from employee_internet_history
            WHERE employee_internet_history.employee_fullname = worker.employee_fullname
            group by website
            order by num desc
         )
         LOOP
         DBMS_OUTPUT.PUT_LINE('website = ' || TOP3.website || ' ,times surferd: '||top3.num);
         end loop;
      end LOOP;
end;
/

对于每个员工,此查询大约需要200秒。 我真正的eu_employee表有超过8000条记录 这意味着用我的方式计算这个时间需要19天。

1)我怎样才能加快速度?

2)为什么需要花费这么长时间? 如果员工的所有记录都被编入索引,则应该使用O(1)来查找它们并计算它们。

此外,查询不依赖于彼此, 3)我可以并行运行几个查询吗?

4)我看到有几个提示可以在并行模式下运行,哪一个最适合我的需要?

5)有没有使用pl/sql的解决方案?

1 个答案:

答案 0 :(得分:4)

通常,“基于集合”的方法(使用查询)比使用PL / SQL更快。

以下查询可以执行您想要的操作:

select eih.*
from (select employee_id, website, count(*) as cnt,
             row_number() over (partition by employee_id order by count(*) desc) as seqnum
      from employee_internet_history eih
      group by employee_id, website
     ) eih
where seqnum <= 3;

我不确定您是否可以让它运行得更快,因为您必须首先在员工/网站级别汇总数据。如果您想了解更多员工信息,请加入eu_employees

顺便说一下,使用employee_fullname作为连接键是一个非常糟糕的主意。人们可能会因为各种原因改变他们的名字。

我还要补充一点,使用employee_internet_history(employee_fullname, website)上的索引可能会使查询运行得更快。您也可以将join遗漏给员工信息。问题中至少没有任何内容表明需要它(除非它用于过滤)。

编辑:

性能高度依赖于您的硬件和内存。您可以通过以下方式使用员工子集来加速查询:

select eih.*
from (select employee_id, website, count(*) as cnt,
             row_number() over (partition by employee_id order by count(*) desc) as seqnum
      from employee_internet_history eih join
           (select ee.*
            from eu_employees ee
            where rownum < 100
           ) ee
           on eih.employee_id = w.employee_id
      group by employee_id, website
     ) eih
where seqnum <= 3;