使此 sql 查询更快或使用 pl sql?

make this sql query faster or use pl sql?

因此,在 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 和计数 = 1。 如果确实如此,我们必须确保该行的数量 > 25 并且该行的 cycle_ctr 计数器是最小的。

如您所见,我们在 MIN(CYCLE_CTR).

上做 select 时正在重复

编辑:在 ACCOUNT_CYCLE_ACTIVITY table 的列 ACCOUNT_ID 上定义了一个索引。

我们的 table 是 ACCOUNT_CYCLE_ACTIVITY。如果有 ACTIVITY_TYPE_CODE = '021' and ACTIVITY_GROUP_CODE = 'R12' and 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 |
---------------------------------------------------------------------------------------------------------------

我可能会从使用 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' 
       )
  );

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

基本上就是这两行

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

是连接第一和第二的连接条件select,这个连接第一和第三。

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

我重写的查询是这样的:

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。我尝试创建一个 Fiddle,但像往常一样,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 );