使这个SQL查询更快或使用pl sql?

时间:2015-03-02 16:07:34

标签: sql oracle plsql

因此,oracle服务器上面的查询大约需要一个小时才能执行。 这是一种让它更快的方法吗?

SELECT * 
FROM ACCOUNT_CYCLE_ACTIVITY aca1 
WHERE aca1.ACTIVITY_TYPE_CODE='021' 
  AND aca1.ACTIVITY_GROUP_CODE='R12' 
  AND aca1.CYCLE_ACTIVITY_COUNT='999'   
  AND 
  EXISTS   
  (
      SELECT 'a' 
      FROM ACCOUNT_CYCLE_ACTIVITY aca2     
      WHERE  aca1.account_id = aca2.account_id 
        AND aca2.ACTIVITY_TYPE_CODE='021'  
        AND aca2.ACTIVITY_GROUP_CODE='R12' 
        AND aca2.CYCLE_ACTIVITY_COUNT ='1' 
        AND aca2.cycle_activity_amount > 25
        AND  (aca2.cycle_ctr > aca1.cycle_ctr) 
        AND aca2.cycle_ctr =  
        (
            SELECT MIN(cycle_ctr) 
            FROM ACCOUNT_CYCLE_ACTIVITY aca3 
            WHERE aca3.account_id = aca1.account_id 
              AND aca3.ACTIVITY_TYPE_CODE='021'  
              AND aca3.ACTIVITY_GROUP_CODE='R12' 
              AND aca3.CYCLE_ACTIVITY_COUNT ='1' 
       )
  );

所以基本上这就是它试图做的事情。 找到一个R12,021和999值的行, 对于所有这些行,我们必须确保存在具有相同帐户ID的另一行,但是R12,021和count = 1。 如果确实如此,我们必须确保该行的数量> 25,该行的cycle_ctr计数器最小。

正如您所看到的,我们在MIN(CYCLE_CTR)上进行选择时正在重复。

编辑:ACCOUNT_CYCLE_ACTIVITY表的列ACCOUNT_ID上有一个索引定义。

我们的表格为ACCOUNT_CYCLE_ACTIVITY。如果有一行ACTIVITY_TYPE_CODE ='021'且ACTIVITY_GROUP_CODE ='R12'且CYCLE_ACTIVITY_COUNT ='999',则代表标识行。

如果具有类似标识行的帐户具有其他021 R12行,则从标识行查询CYCLE_CTR值最小的行,该行大于CYCLE_CTR。如果找到一行,则找到的行的CYCLE_ACTIVITY_AMOUNT是> 25和CYCLE_ACTIVITY_COUNT = 1,报告帐户。

请注意,标识行仅用于标识,不会报告。

例如,这是一个应该报告的account_id上的SELECT。

  Account_ID    Group_Code  Type_code   Cycle_ctr   Activity_Amount Activity_count
 53116267       R12          021       14          0               999
 53116267       R12          021       25          35              1
 53116267       R12          021       22          35              1
 53116267       R12          021       20          35              1

除了999和1之外,还有其他几个Activity_count,因此需要WHERE子句。

同样,如果上面的例子如下:

 Account_ID     Group_Code  Type_code   Cycle_ctr   Activity_Amount Activity_count
     53116267       R12          021       14          0               999
     53116267       R12          021       25          35              1
     53116267       R12          021       22          35              1
     53116267       R12          021       20         **20**           1

它不会被报告,因为具有最低cycle_ctr的行的activity_amount大于标识行的cycle_ctr是20,小于25。

解释

之后的计划
      explain plan for select * from account_activity;
      select * from table(dbms_xplan.display);

Plan hash value: 1692077632

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name             | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                  |   470M|    12G|   798K  (1)| 02:39:38 |       |       |
|   1 |  PARTITION HASH ALL        |                  |   470M|    12G|   798K  (1)| 02:39:38 |     1 |    64 |
|   2 |   TABLE ACCESS STORAGE FULL| ACCOUNT_ACTIVITY |   470M|    12G|   798K  (1)| 02:39:38 |     1 |    64 |
---------------------------------------------------------------------------------------------------------------

3 个答案:

答案 0 :(得分:2)

我可能会首先使用WITH语句来希望减少选择数据的次数,并使其更具可读性。我建议的另一件事是通过某种联接替换存在。

with base as 
(
    select *
    from account_cycle_activity
    where activity_type_code = '021'
      and activity_group_code = 'R12'
)
SELECT * 
FROM base aca1 
WHERE aca1.CYCLE_ACTIVITY_COUNT='999'   
  AND 
  EXISTS   
  (
      SELECT 'a' 
      FROM base aca2     
      WHERE  aca1.account_id = aca2.account_id 
        AND aca2.CYCLE_ACTIVITY_COUNT ='1' 
        AND aca2.cycle_activity_amount > 25
        AND  (aca2.cycle_ctr > aca1.cycle_ctr) 
        AND aca2.cycle_ctr =  
        (
            SELECT MIN(cycle_ctr) 
            FROM base aca3 
            WHERE aca3.account_id = aca1.account_id 
              AND aca3.CYCLE_ACTIVITY_COUNT ='1' 
       )
  );

答案 1 :(得分:1)

使用显式连接重写查询,而不是使用EXISTS重写。

基本上这两行

WHERE  aca1.account_id = aca2.account_id 
AND  (aca2.cycle_ctr > aca1.cycle_ctr) 

是加入第一个和第二个选择的连接条件,这个加入第一个和第三个。

WHERE aca3.account_id = aca1.account_id

查询应如下所示

select distinct aca1.*
FROM ACCOUNT_CYCLE_ACTIVITY aca1, ACCOUNT_CYCLE_ACTIVITY aca2, ACCOUNT_CYCLE_ACTIVITY aca3
WHERE
 join conditions and other selection conditions

答案 2 :(得分:1)

我对查询的重写是这样的:

Select  Aca1.* 
From    Account_Cycle_Activity Aca1
Join    Account_Cycle_Activity Aca2
    On  Aca2.Account_Id = Aca1.Account_Id
    And Aca2.Group_Code = Aca1.Group_Code
    And Aca2.Type_Code  = Aca1.Type_Code
    And Aca2.Activity_Amount > 25
    And Aca2.Activity_Count = 1
    And Aca2.Cycle_Ctr > Aca1.Cycle_Ctr
    And Aca2.Cycle_Ctr =(
            Select  Min( Cycle_Ctr ) 
            From    Account_Cycle_Activity Aca3 
            Where   Aca3.Account_Id = Aca1.Account_Id 
              And   Aca3.Type_Code  = Aca1.Type_Code 
              And   Aca3.Group_Code = Aca1.Group_Code
              And   Aca3.Activity_Count =1 
       )
Where Aca1.Type_Code    = 21
  And Aca1.Group_Code   = 'R12' 
  And Aca1.Activity_Count = 999;

但是执行计划并没有那么不同,更重要的是,成本14是相同的。然而,然后我添加了两个索引,成本从14降到2.我试图创建一个小提琴,但像往常一样,Oracle部分不起作用。所以这就是:

Create Table Account_Cycle_Activity(
    Account_Id      Int Not Null,
    Group_Code      Char( 3 ) Not Null,
    Type_Code       Int Not Null,
    Cycle_Ctr       Int Not Null,
    Activity_Amount Int Not Null,
    Activity_Count  Int Not Null
);

insert into Account_Cycle_Activity
    select  53116267, 'R12', 21, 14, 0, 999 from dual union all
    select  53116267, 'R12', 21, 25, 35,  1 from dual union all
    Select  53116267, 'R12', 21, 22, 35,  1 From Dual Union All
    select  53116267, 'R12', 21, 20, 35,  1 from dual;

-- Execute the query before creating these indexes and again after.
Create Index Ix_Account_Cycle_Activity1
    On Account_Cycle_Activity( Account_Id, Group_Code, Type_Code, Activity_Amount, Activity_Count );
Create Index Ix_Account_Cycle_Activity2
    On Account_Cycle_Activity( Cycle_Ctr );