使此 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 );
因此,在 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 );