通过一列修改选择行两次

时间:2013-04-01 11:33:20

标签: sql oracle plsql union

我有table和sql(在Oracle上运行):

T(这只是一个例子,表格很大)

a  b  c
-------
1  4  7
2  5  5
3  6  8

SQL:

SELECT a, b, c
FROM t

union all

SELECT 'R',b,c
FROM t
WHERE b = c AND (condition to another tables, etc)

它返回:

1  4  7
2  5  5
3  6  8
R  5  5

这里可以避免使用UNION(并且不添加JOIN)吗?换句话说 - 是否可以优化查询以避免Oracle查看表T两次?

1 个答案:

答案 0 :(得分:1)

这将读取您的表一次。 连接是通过辅助表完成的,该辅助表只包含两个值(当然这在内存中 - 没有I / O)

with t as(
  select '1' a,  '4' b,  '7' c from dual union all
  select '2',  '5',  '5' from dual union all
  select '3',  '6',  '8' from dual
)
select decode(aux.col,1,t.a,'R'), t.b, t.c 
from t
join (select '1' col from dual union all select '2' from dual) aux
on (aux.col='1' or t.b=t.c);

查询不依赖于'1'和'2'。它可以是:

select decode(aux.col, 'bla', t.a,'R'), t.b, t.c 
from t
join (select 'bla' col from dual union all select 'otherbla' from dual) aux
on (aux.col='bla' or t.b=t.c);

更新:此外,如果b = c记录的数量很小,您可以加快实际查询的速度,从而创建索引:

 create index fbi on t (b-c);

然后使用WHERE b = c

替换您的查询WHERE b - c = 0

UPDATE2 只是为了了解这些查询的执行方式:

create table t(a varchar2(10), b varchar2(10), c varchar2(10));

insert into t 
select mod(dbms_random.random(),1000),
  mod(dbms_random.random(),1000),
  mod(dbms_random.random(),1000)
from dual
connect by level < 1000000;

exec DBMS_STATS.GATHER_TABLE_STATS('DEV','T');

--1
SELECT a, b, c
FROM t;
---------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   999K|    11M|   700   (3)|
|   1 |  TABLE ACCESS FULL| T    |   999K|    11M|   700   (3)|
---------------------------------------------------------------

--2
SELECT a, b, c
FROM t
union all
SELECT 'R',b,c
FROM t
WHERE b = c;
----------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)|
----------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |  1009K|    11M|  1426  (53)|
|   1 |  UNION-ALL         |      |       |       |            |
|   2 |   TABLE ACCESS FULL| T    |   999K|    11M|   700   (3)|
|   3 |   TABLE ACCESS FULL| T    | 10000 |    97K|   726   (7)|
---------------------------------------------------------------- 


--3
select decode(aux.col, 'bla', t.a,'R'), t.b, t.c 
from t
join (select 'bla' col from dual union all select 'otherbla' from dual) aux
on (aux.col='bla' or t.b=t.c);


----------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)|
----------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 20990 |   368K|  1402   (3)|
|   1 |  NESTED LOOPS      |      | 20990 |   368K|  1402   (3)|
|   2 |   VIEW             |      |     2 |    12 |     4   (0)|
|   3 |    UNION-ALL       |      |       |       |            |
|   4 |     FAST DUAL      |      |     1 |       |     2   (0)|
|   5 |     FAST DUAL      |      |     1 |       |     2   (0)|
|   6 |   TABLE ACCESS FULL| T    | 10495 |   122K|   699   (3)|
----------------------------------------------------------------
--if the leading table is dual, can be used an /*+ordered*/ hint 
--after select clause


--4
create index fbi on t (b-c);
SELECT a, b, c
FROM t
union all
SELECT 'R',b,c
FROM t
WHERE b - c = 0;
--------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |  1009K|    11M|  1384  (51)|
|   1 |  UNION-ALL                   |      |       |       |            |
|   2 |   TABLE ACCESS FULL          | T    |   999K|    11M|   700   (3)|
|   3 |   TABLE ACCESS BY INDEX ROWID| T    | 10000 |   117K|   683   (1)|
|   4 |    INDEX RANGE SCAN          | FBI  |  4000 |       |     3   (0)|
--------------------------------------------------------------------------

请记住,Oracle不知道如何预测或加入3中的子句,所以最好强制执行所需的执行路径。您应该测试在2到3和4之间进行选择(使用索引成本的后面)。