【问题标题】:How to move sub-query where condition out of sub-query?如何将子查询的条件移出子查询?
【发布时间】:2014-06-15 11:19:00
【问题描述】:

在 Oracle 中,我有一个相当大的 select 语句。我想将它保存为一个视图,这样我就可以从 .NET 程序中调用它,并将它用作我们必须生成的一些统计数据的基础。使用视图时,它总是按日期过滤。

我想要这样的东西:

select * from my_view
where my_view.date = '2014-01-01';

问题是我需要在子查询的where条件中使用日期作为参数:

select * from table1
left outer join (
    select * from table2
    where table2.date = :somedate)
  on table1.Id = table2.Id;

...不能做成视图。

有没有办法,我可以将日期比较移到子查询之外,而不会弄乱左外连接?

不该做什么:

这会创建一个内部连接 ​​- 这不是我想要的:

select * from table1
left outer join 
  table2
  on table1.Id = table2.Id
where table2.date = '2014-01-01';

此选择过滤掉 table1 中的行,这些行不在指定日期的行上连接,但在另一个日期的行上连接:

select * from table1
left outer join 
  table2
  on table1.Id = table2.Id
where table2.Id is null
   or table2.date = '2014-01-01';

【问题讨论】:

标签: sql oracle join subquery outer-join


【解决方案1】:

有多种方法可以对视图进行参数化 - 例如使用包或应用程序上下文(您可以使用内置上下文 CLIENTCONTEXT)。但是所有这些方法都需要完全控制参数,并且您必须使用附加功能设置参数值,这可能是一个严重的缺点:

SQL> create or replace view param_view
  2  as
  3  select d.name, e.firstname, e.lastname, e.email
  4  from department d left join employee e
  5  on (d.id = e.departmentid
  6  and e.firstname = sys_context('CLIENTCONTEXT','EMPNAME'))
  7  /

View created.

SQL> exec dbms_session.set_context('CLIENTCONTEXT','EMPNAME','Scott')

PL/SQL procedure completed

SQL> select * from param_view;

NAME                 FIRSTNAME  LASTNAME             EMAIL                      
-------------------- ---------- -------------------- --------------------       
Department A         Scott      Tiger                xxx@gmail.com              
Department B                                                                    
Department C                                                                    

SQL> exec dbms_session.set_context('CLIENTCONTEXT','EMPNAME','Allen')

PL/SQL procedure completed

SQL> select * from param_view;

NAME                 FIRSTNAME  LASTNAME             EMAIL                      
-------------------- ---------- -------------------- --------------------       
Department A         Allen      Dirk                 yyy@gmail.com              
Department B                                                                    
Department C   

SQL> create or replace package pck_test
  2  is
  3   function get return varchar2;
  4   procedure set (x in varchar2);
  5  end;
  6  /

Package created.

SQL> create or replace package body pck_test
  2  is
  3   name employee.firstname%type;
  4  
  5   function get return varchar2
  6   is
  7   begin
  8     return name;
  9   end;
 10  
 11   procedure set (x in varchar2)
 12   is
 13   begin
 14     name := x;
 15   end;
 16  
 17  end;
 18  /

Package body created.

SQL> create or replace view param_view
  2  as
  3  select d.name, e.firstname, e.lastname, e.email
  4  from department d left join employee e
  5  on (d.id = e.departmentid
  6  and e.firstname = pck_test.get)
  7  /

View created

SQL> exec pck_test.set('Scott')

PL/SQL procedure completed.

SQL> select * from param_view;

NAME                 FIRSTNAME  LASTNAME             EMAIL                      
-------------------- ---------- -------------------- --------------------       
Department A         Scott      Tiger                xxx@gmail.com              
Department B                                                                    
Department C                                                                    

SQL> exec pck_test.set('Allen')

PL/SQL procedure completed.

SQL> select * from param_view;

NAME                 FIRSTNAME  LASTNAME             EMAIL                      
-------------------- ---------- -------------------- --------------------       
Department A         Allen      Dirk                 yyy@gmail.com              
Department B                                                                    
Department C                                                                    

SQL> select d.name, e.firstname, e.lastname, e.email
  2  from department d left join employee e
  3  on (d.id = e.departmentid)
  4  /

NAME                 FIRSTNAME  LASTNAME             EMAIL                      
-------------------- ---------- -------------------- --------------------       
Department A         Allen      Dirk                 yyy@gmail.com              
Department A         Scott      Tiger                xxx@gmail.com              
Department B                                                                    
Department C  

【讨论】:

    【解决方案2】:

    如果您只是想避免在底部创建内连接,请添加合并语句,例如:

    coalesce(table1.date,'2015-01-28') = '2015-01-28'
    

    这将不会创建内部连接,因为所有空值仍将返回。

    【讨论】:

      【解决方案3】:

      已删除原始回复。 这是我想出的

      create view my_view as
      select * from (
      select t1.*, t2.*, to_date(null) filter_date from table1 t1 left outer join table2 t2
      on t2.id <> t2.id
      union all
      select t1.*, t2.*, t2.date filter_date from table1 t1 inner outer join table2 t2
      on t1.id = t2.id)
      
      1. 第一个数据集是从 t1 获取所有记录
      2. 第二个数据集是从 t1 与 t2 内连接获取所有记录 因此,在查询中,您将始终有 1 或 2 条记录可供选择。如果有 2 条记录,请选择带日期的一条:

        select * from (
        select *, row_number() over (partition by id order by filter_date NULLS LAST) rnum
        from my_view where filter_date = :date or filter_date is null
        ) where rnum = 1
        

         select * from (
          select *, rownum rnum
          from my_view where filter_date = :date or filter_date is null
          order by filter_date NULLS LAST
          ) where rnum = 1
      

      【讨论】:

      • 不,这不是我需要的。这会将连接变成内部连接。我不会得到 table1 中不加入 table2 行的行,因为 filter_date 将为空。
      • 是的!你是对的,我错过了。修改选择以包含“filter_date is null”条件
      猜你喜欢
      • 1970-01-01
      • 2021-01-16
      • 2013-05-09
      • 2013-03-12
      • 1970-01-01
      • 1970-01-01
      • 2020-05-31
      • 2011-04-13
      • 2015-05-04
      相关资源
      最近更新 更多