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

发布于 2024-10-26 00:57:17 字数 1164 浏览 2 评论 0原文

背景

示例数据集

#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' 的

行 问题

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

  • 是否还有其他可以作为最终用户在 Oracle Discoverer Plus 中实施的解决方案?

谢谢!

Background

Sample data set

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

I want a simple select query to yield employees' latest record (by period) only if the status='L'.

The results would look like this:

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

Naive attempt

Obviously, my naive attempt at a query does not work:

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

Which results in the following:

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

Knowing the order that clauses are evaluated in SQL explains why it doesn't work. Here is how my query is evaluated:

  • Isolate rows where status='L'
  • Rank the rows
  • Isolate top rank row

I want the following:

  • Rank rows
  • Isolate the top ranked rows
  • Isolate where status='L'

Questions

  • Is possible--with only a simple modification to the SELECT/WHERE clauses and using only basic predicate operators--to ensure that predicates based on analytic functions in the WHERE clause get evaluated before the non-aggregate predicates?

  • Anyone have other solutions that can be implemented as an end-user in Oracle Discoverer Plus?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

金兰素衣 2024-11-02 00:57:18

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

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

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

我想不出针对您的问题的不同解决方案。

Is it possible to do this without a sub-query

Technically the following is not a sub-query but a derived table

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

I can't think of a different solution to your problem.

一页 2024-11-02 00:57:18

经典的分组依据

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)

The classic Group by

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'

Exists

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)
囚我心虐我身 2024-11-02 00:57:18

我可能不得不做一个“多比”,然后把我的耳朵撞到烤箱门上,并为此熨烫我的手......

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

创建示例数据:

--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

冲向烤箱...

I'll probably have to do a "Dobby" and slam my ear in the oven door and iron my hands for this...

You can create a function which evaluates the current row.
Note that this is inherently non-scalable. But I guess it's better than nothing.

Create the sample data:

--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 the slowest function in the database:

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;
/

Demonstrates the use of the function:

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

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

Rushes towards the oven...

我的痛♀有谁懂 2024-11-02 00:57:18

从中选择*
(SELECT a.*,rank() OVER (ORDER BY period ASC) 序列

(从其中选择 *
(
从双选择 1 个 ID、1 个周期、“L”状态
联合所有
从双选择 1 个 ID、2 个周期、“G”状态
联合所有
从双选择 2 个 ID、3 个周期、“L”状态

其中状态 = 'L'
) 一个

其中序列 = 1

select * from
(SELECT a.*, rank() OVER (ORDER BY period ASC) sequence
from
(select * from
(
select 1 id, 1 period, 'L' status from dual
union all
select 1 id, 2 period, 'G' status from dual
union all
select 2 id, 3 period, 'L' status from dual
)
where status = 'L'
) a
)
where sequence = 1

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文