【问题标题】:Several subqueries to WITH table in OracleOracle中WITH表的几个子查询
【发布时间】:2013-09-15 10:38:59
【问题描述】:

假设我有下表:

create table Employee
  (
  ID                 VARCHAR2(4 BYTE)         NOT NULL,
  First_Name         VARCHAR2(10 BYTE),
  Last_Name          VARCHAR2(10 BYTE),
  Start_Date         DATE,
  End_Date           DATE,
  Salary             Number(8,2),
  City               VARCHAR2(10 BYTE),
  Description        VARCHAR2(15 BYTE)
  )
  ;
insert into employee (
ID,  First_Name, Last_Name, Start_Date,                               
End_Date,                       Salary,  City,       Description)
select 21 + (level * 2) - 1,'Jason',    'Martin',  to_date('19960725','YYYYMMDD'), 
to_date ('20060725','YYYYMMDD'), 1234.56+level/3*5, 'Toronto',  'Programmer'
from dual
connect by level < 2501
;

当我尝试运行以下查询时:

with tt2 as (
select t.*, 
case when rownum = 1 then 1 when rownum = 3 then 3 else null end rfn 

-- RFN 是一个复杂情况的示例,它仅将数字分配给需要的行。请不要修复它

from (select id, first_name, salary from employee) t
)

select  
(select id from tt2 where rfn = 3) w
from dual

它返回所有类似于 rfn = 3 的 ID(2500 行,共 26 行)

这里是解释计划

"PLAN_TABLE_OUTPUT"
"Plan hash value: 2716438026"
" "
"--------------------------------------------------------------------------------------------------------"
"| Id  | Operation                  | Name                      | Rows  | Bytes | Cost (%CPU)| Time     |"
"--------------------------------------------------------------------------------------------------------"
"|   0 | SELECT STATEMENT           |                           |  2500 |       |    12   (0)| 00:00:01 |"
"|*  1 |  VIEW                      |                           |  2500 | 42500 |     4   (0)| 00:00:01 |"
"|   2 |   TABLE ACCESS FULL        | SYS_TEMP_0FD9D6645_8CDDE2 |  2500 | 60000 |     4   (0)| 00:00:01 |"
"|   3 |  TEMP TABLE TRANSFORMATION |                           |       |       |            |          |"
"|   4 |   LOAD AS SELECT           | SYS_TEMP_0FD9D6645_8CDDE2 |       |       |            |          |"
"|   5 |    COUNT                   |                           |       |       |            |          |"
"|   6 |     TABLE ACCESS FULL      | EMPLOYEE                  |  2500 | 60000 |     8   (0)| 00:00:01 |"
"|   7 |   VIEW                     |                           |  2500 |       |     4   (0)| 00:00:01 |"
"|   8 |    TABLE ACCESS FULL       | SYS_TEMP_0FD9D6645_8CDDE2 |  2500 | 60000 |     4   (0)| 00:00:01 |"
"--------------------------------------------------------------------------------------------------------"
" "
"Predicate Information (identified by operation id):"
"---------------------------------------------------"
" "
"   1 - filter(""RFN""=3)"
" "
"Note"
"-----"
"   - dynamic sampling used for this statement (level=2)"

--编辑--

例如,我在 WITH 中有表:

"ID" "FIRST_NAME" "SALARY" "RFN" “22”“杰森”1236.23 1 “24”“杰森”1237.89 “26”“杰森”1239.56 3

我想选择 RFN = 1 的任何人的薪水,给我一个号码 1236.23

但我也想在同一个查询中选择 RFN = 3 的薪水, 给我一个号码 1239.56。

等等。

但是。对于此查询中的每一个,它都会对 N 行进行 FULL_TABLE_SCAN。

with tt2 as (
select t.*, case when rownum = 1 then 1 when rownum = 3 then 3 else null end rfn
from (select id, first_name, salary from employee) t
)
select  
(select salary from tt2 where rfn = 1) w,
(select salary from tt2 where rfn = 3) w
from dual

解释表:

('Plan hash value: 1823688231');
(' ');
('--------------------------------------------------------------------------------------------------------');
('| Id  | Operation                  | Name                      | Rows  | Bytes | Cost (%CPU)| Time     |');
('--------------------------------------------------------------------------------------------------------');
('|   0 | SELECT STATEMENT           |                           |  2500 |       |    12   (0)| 00:00:01 |');
('|*  1 |  VIEW                      |                           |  2500 | 40000 |     4   (0)| 00:00:01 |');
('|   2 |   TABLE ACCESS FULL        | SYS_TEMP_0FD9D6651_8CDDE2 |  2500 | 60000 |     4   (0)| 00:00:01 |');
('|*  3 |  VIEW                      |                           |  2500 | 40000 |     4   (0)| 00:00:01 |');
('|   4 |   TABLE ACCESS FULL        | SYS_TEMP_0FD9D6651_8CDDE2 |  2500 | 60000 |     4   (0)| 00:00:01 |');
('|   5 |  TEMP TABLE TRANSFORMATION |                           |       |       |            |          |');
('|   6 |   LOAD AS SELECT           | SYS_TEMP_0FD9D6651_8CDDE2 |       |       |            |          |');
('|   7 |    COUNT                   |                           |       |       |            |          |');
('|   8 |     TABLE ACCESS FULL      | EMPLOYEE                  |  2500 | 60000 |     8   (0)| 00:00:01 |');
('|   9 |   VIEW                     |                           |  2500 |       |     4   (0)| 00:00:01 |');
('|  10 |    TABLE ACCESS FULL       | SYS_TEMP_0FD9D6651_8CDDE2 |  2500 | 60000 |     4   (0)| 00:00:01 |');
('--------------------------------------------------------------------------------------------------------');
(' ');
('Predicate Information (identified by operation id):');
('---------------------------------------------------');
(' ');
('   1 - filter("RFN"=1)');
('   3 - filter("RFN"=3)');
(' ');
('Note');
('-----');
('   - dynamic sampling used for this statement (level=2)');

--为更清楚而编辑--

预期结果来自:

"ID" "FIRST_NAME" "SALARY" "RFN" “22”“杰森”1236.23 1 “24”“杰森”1237.89 “26”“杰森”1239.56 3 “26”“杰克”1249.56 6 “26”“维基”119.56 8 工资 工资 1 工资 2 姓名 姓名 2 1236.23 1249.56 119.56 维基杰克

来自~以下语句:

with tt2 as (
select t.*, case when row_number() over (order by id) = 1 then 1 when row_number() over     (order by id) = 3 then 3 else null end rfn
from (select id, first_name, salary from employee) t
)
select 
(select salary from tt2 where rfn = 1) salary,
(select salary from tt2 where rfn = 6) salary1,
(select salary from tt2 where rfn = 8) salary2,
(select first_name from tt2 where rfn = 8) name,
(select first_name from tt2 where rfn = 6) name2

from dual

没有 2500 行的 5 次全表扫描

【问题讨论】:

  • 这段代码很难理解,可能是因为它的含义完全被您使用的抽象所掩盖。基于 rownum 的 case 语句(您将其注释为“仅示例”)在您从中选择的查询的上下文中没有任何意义。你能解释一下这一切的意义,以及你真正想要解决的问题吗?
  • 我在 WITH 中创建一个临时表。然后通过 CASE 我为该表中的每一行分配特定的不同数字。比我想从该表中选择特定值。按新获得的号码。
  • 你仍然没有解释你真正想要做什么。以及为什么 ROW_NUMBER 不起作用,推荐超过 ROWNUM。顺便说一句,“奇怪的原因”并不奇怪,这正是您所写的:为所有行返回相同的 id。
  • 我从来没有说过我需要所有行都使用相同的 id。 WITH 表中只有一个具有特定 row_ID 的值(由 WITH 表中的 CASE 创建),而没有得到 2500 行作为回报。上面添加的示例。
  • 您没有这么说,但您在 SELECT 中告诉 Oracle 返回此结果。也许你应该先开始学习 SQL 的基础知识 :-)

标签: sql oracle subquery with-statement


【解决方案1】:

您要求所有行,您必须更改为:

with tt2 as (
select t.*, case when rownum = 1 then 1 when rownum = 3 then 3 else null end rfn
from (select id, first_name, salary from employee) t
)
select salary from tt2 where rfn in (1,3)

但是为什么要这个结果呢?如果没有 ORDER BY,ROWNUM 就不是确定性的,不能保证每次都具有相同的顺序。您可能需要像这样使用 ROW_NUMBER:

WITH tt2 AS (SELECT t.*, ROW_NUMBER() OVER (ORDER BY ID) rn)
SELECT salary 
FROM tt2 
WHERE rn IN (1,3)

【讨论】:

  • 这绝对不是我想要的。在我的情况下,“当 row_number() over (order by id) = 1 then 1 when row_number() over (order by id) = 3 then 3 else null end rfn”的情况也不会产生任何影响。这是一个复杂情况的示例,它只对需要的行进行数字。请不要修复它。
  • CASE WHEN 不会减少处理的行数。这对优化器来说更复杂。而且你永远不应该判断一个小表的计划,最好检查一个大表,优化器可能会在那个计划上付出更多的努力。
  • 这只是一个例子。我的主要查询需要大约 15-30 秒才能运行,而该查询中的 WITH 表只需要 1.3 秒。它有超过 2.5k 行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-14
  • 2021-02-09
  • 2015-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多