匹配五列中的任意三列值

时间:2016-01-20 09:08:19

标签: sql oracle oracle11g

我实际上是一次从三个列字段中选择Oracle数据库中的记录。用户可以提供定义的五列中的任何三列。我尝试了下面的两个查询,而Query-1要快得多,第二个问题在这里尽管Query-1有额外的加入。这背后的原因是什么?

查询-1

select y.id,y.form_no, y.ownership, y.issue_date, y.issue_place, y.pin_no,surname, y.given_names, y.date_of_birth_bs
    from (
        select form_no,
        case when lower(place_of_birth) = 'morang' then 1 else 0 end as PlaceOfBirth,
        case when date_of_birth_ad = to_date('1985-09-01', 'yyyy-MM-dd') then 1 else 0 end as DateOfBirthAd,
        case when issue_date = '20600219' then 1 else 0 end as DateOfIssue,
        case when lower(issue_place) = 'morang' then 1 else 0 end as PlaceOfIssue,
        case when lower(ownership) = '12005-1746' then 1 else 0 end as ownershipNo
        from application
    ) x, application y
where x.form_no = y.form_no and (x.PlaceOfBirth + x.DateOfBirthAd + x.DateOfIssue + x.PlaceOfIssue + x.ownershipNo) > 2;

查询-2

select id,form_no, ownership, issue_date, issue_place, pin_no,surname, given_names, date_of_birth_bs
from application y
where
    (case when lower(place_of_birth) = 'morang' then 1 else 0 end)+
    (case when date_of_birth_ad = to_date('1985-09-01', 'yyyy-MM-dd') then 1 else 0 end)+
    (case when issue_date = '20600219' then 1 else 0 end)+
    (case when lower(issue_place) = 'morang' then 1 else 0 end)+
    (case when lower(ownership) = '12005-1746' then 1 else 0 end) > 2;

此外,还有其他方法可以修改查询以使其更快吗?

1 个答案:

答案 0 :(得分:1)

对于第二个查询,您可以定义基于函数的索引

create index fidx on application ((case when lower(place_of_birth) = 'morang' then 1 else 0 end)+
    (case when date_of_birth_ad = to_date('1985-09-01', 'yyyy-MM-dd') then 1 else 0 end)+
    (case when issue_date = '20600219' then 1 else 0 end)+
    (case when lower(issue_place) = 'morang' then 1 else 0 end)+
    (case when lower(ownership) = '12005-1746' then 1 else 0 end));

获取INDEX RANGE SCAN访问权限。

-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             | 40307 |  5117K|   112   (0)| 00:00:02 |
|   1 |  TABLE ACCESS BY INDEX ROWID| APPLICATION | 40307 |  5117K|   112   (0)| 00:00:02 |
|*  2 |   INDEX RANGE SCAN          | FIDX        |  7255 |       |    19   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

   2 - access(CASE LOWER("PLACE_OF_BIRTH") WHEN 'morang' THEN 1 ELSE 0 END +CASE 
              "DATE_OF_BIRTH_AD" WHEN TO_DATE(' 1985-09-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') 
              THEN 1 ELSE 0 END +CASE "ISSUE_DATE" WHEN '20600219' THEN 1 ELSE 0 END +CASE 
              LOWER("ISSUE_PLACE") WHEN 'morang' THEN 1 ELSE 0 END +CASE LOWER("OWNERSHIP") WHEN 
              '12005-1746' THEN 1 ELSE 0 END >2)

此方法的问题在于它仅适用于查询中的确切文字值。 如果你可以使用其他参数(例如使用绑定变量),这将无效。

作为替代方案,您可以重写查询添加一些 OR connected 访问谓词并为其添加专用索引。

示例

create index fidx1 on application ( lower(place_of_birth) );
create index fidx2 on application ( issue_date );
create index fidx3 on application ( lower(ownership) );

增强查询将是

select id,form_no, ownership, issue_date, issue_place, pin_no,surname, given_names, date_of_birth_bs
from application y
where /*ACCESS */
    (lower(place_of_birth) = 'morang' or
     issue_date = '20600219' or
     lower(ownership) = '12005-1746' ) and
    /* FILTER */
    ((case when lower(place_of_birth) = 'morang' then 1 else 0 end)+
    (case when date_of_birth_ad = to_date('1985-09-01', 'yyyy-MM-dd') then 1 else 0 end)+
    (case when issue_date = '20600219' then 1 else 0 end)+
    (case when lower(issue_place) = 'morang' then 1 else 0 end)+
    (case when lower(ownership) = '12005-1746' then 1 else 0 end) > 2);

请注意添加的三个访问谓词

(lower(place_of_birth) = 'morang' or
 issue_date = '20600219' or
 lower(ownership) = '12005-1746' )

启用INDEX CONCATENATION ACCESS

--------------------------------------------------------------------------------------------
| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             |   824 |   125K|  1597   (1)| 00:00:20 |
|   1 |  CONCATENATION               |             |       |       |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID| APPLICATION |   411 | 64116 |   798   (1)| 00:00:10 |
|*  3 |    INDEX RANGE SCAN          | FIDX3       |  3404 |       |     3   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS BY INDEX ROWID| APPLICATION |   407 | 63492 |     2   (0)| 00:00:01 |
|*  5 |    INDEX RANGE SCAN          | FIDX1       |  3404 |       |     1   (0)| 00:00:01 |
|*  6 |   TABLE ACCESS BY INDEX ROWID| APPLICATION |     6 |   936 |   798   (1)| 00:00:10 |
|*  7 |    INDEX RANGE SCAN          | FIDX2       |  3404 |       |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

为什么三个OR连接的谓词?

如果您有五列中的三列匹配,您可以跳过其中两列并将其余三列与OR匹配 - 其中一列必须匹配。

因此,调整查询的一个方法是获取三个最具选择性的列/表达式,并在它们上定义基于索引/函数的索引,并添加访问谓词,如上所示。

提前测试 - 你必须进行三次索引范围扫描并连接结果 - 所以如果索引访问的成本很高(索引访问返回太多记录),你将回到FULL TABLE扫描 (这可能是你当前的访问路径)。