在其他谓词之前评估分析函数的WHERE谓词(Oracle分析函数)

时间:2011-03-25 21:15:13

标签: sql oracle where-clause operator-precedence analytic-functions

背景

样本数据集

#Employee
Id | Period | Status 
---------------------
1  |  1 |   L    
1  |  2 |   G    
2  |  3 |   L    

我想要一个简单的选择查询,只有当状态='L'时才能产生员工的最新记录(按期间)。

结果如下:

#Desired Results
Id | Period | Status | Sequence
-------------------------------
2  |  3     |   L    |   1

天真的尝试

显然,我对查询的天真尝试不起作用:

#select query
SELECT *, RANK() OVER (PARTITION BY id ORDER BY period ASC) sequence
FROM employees
WHERE   status = 'L' 
 AND    sequence = 1

结果如下:

#Naive (incorrect) Results
ID | Period | Status | Sequence
-------------------------------
1  |  1 |   L    |   1
2  |  3 |   L    |   1

了解在SQL中评估子句的顺序解释了为什么它不起作用。以下是我的查询评估方式:

  • 隔离status ='L'
  • 的行
  • 对行进行排名
  • 隔离顶级行

我想要以下内容:

  • 排名行
  • 隔离排名靠前的行
  • 隔离status ='L'

问题

  • 是否可以 - 只对SELECT / WHERE子句进行简单修改并仅使用基本谓词运算符 - 以确保在非聚合谓词之前评估基于WHERE子句中的分析函数的谓词?

  • 任何人都可以在Oracle Discoverer Plus中作为最终用户实施其他解决方案吗?

谢谢!

3 个答案:

答案 0 :(得分:3)

  

是否可以在没有子查询的情况下执行此操作

从技术上讲,以下不是子查询,而是派生表

SELECT * 
FROM (
    SELECT *, 
           RANK() OVER (PARTITION BY id ORDER BY period ASC) sequence
    FROM employees
) t
WHERE status = 'L' 
  AND sequence = 1

我想不出你问题的不同解决方案。

答案 1 :(得分:0)

经典集团

SELECT e.id, e.period, e.status, 1 sequence
FROM
(
    SELECT id, min(period) period
    FROM employees
    GROUP BY id
) X
JOIN employees e on e.period=X.period and e.id=X.id
WHERE e.status = 'L'

已存在

select e.id, e.period, e.status, 1 sequence
FROM employees e
WHERE e.status = 'L'
  AND NOT EXISTS (select *
                  from employees e2
                  where e2.id=e.id and e2.period>e.period)

答案 2 :(得分:0)

我可能不得不做一个“Dobby”,然后把我的耳朵塞进烤箱门,然后熨烫我的手......

您可以创建一个评估当前行的函数 请注意,这本质上是不可扩展的。但我想这比没有好。

创建样本数据:

--drop table employee purge;

create table employee(
    id     number  not null
   ,period number  not null
   ,status char(1) not null
   ,constraint employee_pk primary key(id, period)
);

insert into employee(id,period, status) values(1, 1, 'L');
insert into employee(id,period, status) values(1, 2, 'G');
insert into employee(id,period, status) values(2, 3, 'L');

commit;

在数据库中创建最慢的函数:

create or replace function i_am_slow(
    ip_id     employee.id%type
   ,ip_period employee.period%type
)
return varchar2
as
   l_count number := 0;
begin
    select count(*)
      into l_count
      from employee e
     where e.id     = ip_id
       and e.period = ip_period
       and e.status = 'L'
       and not exists(
            select 'x'
              from employee e2
             where e2.id = e.id
               and e2.period > e.period);

    if l_count = 1 then
        return 'Y';
    end if;

    return 'N';
end;
/

演示使用该功能:

select id, period, status
  from employee
 where i_am_slow(id, period) = 'Y';

        ID     PERIOD STATUS
---------- ---------- ------
         2          3 L

冲向烤箱......