【问题标题】:Evaluate WHERE predicates on analytic functions before other predicates (Oracle analytic functions)在其他谓词之前评估分析函数上的 WHERE 谓词(Oracle 分析函数)
【发布时间】:2011-07-23 05:34:39
【问题描述】:

背景

样本数据集

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

我想要一个简单的选择查询,仅当 status='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' 的行
  • 对行进行排名
  • 隔离顶级行

我想要以下:

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

问题

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

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

谢谢!

【问题讨论】:

  • 这部分看不懂without derived tables, sub-queries, joins, or a GROUP BY clause?所以你想烤蛋糕不加面粉牛奶鸡蛋?
  • 为什么不想使用派生表或子查询?
  • 我有特殊的限制。我正在尝试在 GUI 查询构建器 Oracle Discoverer Plus 中以最终用户的身份实现此查询。在 Discoverer 中,我无权访问控制所有连接的 SQL 或 EUL(“最终用户层”)。所以,我正在寻找一种解决方法。
  • 当面对这样的工具时,我发现最简单的方法是创建一个 VIEW,将所需的数据作为一个简单的对象提供给工具以供选择。
  • 我同意 Richard 的观点 - 为此创建一个视图(在我的示例中基本上是派生表)

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


【解决方案1】:

是否可以在没有子查询的情况下做到这一点

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

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

我想不出其他解决方案来解决您的问题。

【讨论】:

    【解决方案2】:

    经典的 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'
    

    存在

    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)
    

    【讨论】:

    • 谢谢,理查德。不幸的是,我无法使用 EXISTS 谓词或 GROUP BY。
    【解决方案3】:

    我可能不得不做一个“多比”,把我的耳朵塞进烤箱门并为此熨烫我的双手......

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

    创建示例数据:

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

    冲向烤箱……

    【讨论】:

    • 不幸的是,这个解决方案在我的限制下不起作用。有关更多详细信息,请参阅更新的问题和问题 cmets。感谢您跳出框框思考!
    • 虽然脾气暴躁的 DBA 的约束并不是真正的约束。如果报告值得实施,则应创建视图。不要成为浪费公司资金的人:)
    • 我认为说我在浪费公司的钱是不公平的。现在我意识到我要求的是 SQL 中的一个非常糟糕的 hack,但可能是 Discoverer 中的一个巧妙的解决方法,可以减少维护负载。但是,我对 SQL 的了解还不够,无法做到这一点。我将相应地编辑帖子,使其成为面向发现者的帖子。
    • 但是,我现在也意识到我应该与我的 DBA 一起提出最佳实践解决方案——而不是让 Stack Overflow 为我的晦涩问题集思广益。谢谢。
    • @Bago,我重新阅读了我的评论并意识到这听起来很粗鲁,这不是我的本意。我很高兴您决定与您的 DBA 一起解决问题。你们两个会想出一个好的解决方案。当没有文件/会议时,我也发明了约束,有时也是出于无知,但幸运的是我周围的人在我成为浪费金钱的人之前戳了我的眼睛;)
    【解决方案4】:

    选择 * 来自 (SELECT a.*, rank() OVER (ORDER BY period ASC) 序列 从 (选择 * 从 ( 从 dual 中选择 1 个 id、1 个句点、“L”状态 联合所有 选择 1 id, 2 period, 'G' status from dual 联合所有 选择 2 id, 3 period, 'L' status from dual ) 其中状态 = 'L' ) 一种 ) 其中序列 = 1

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多