如何移动子查询条件超出子查询?

时间:2014-04-29 13:26:28

标签: sql oracle join subquery outer-join

在Oracle中,我有一个非常大的select语句。我想将它保存为视图,以便我可以从.NET程序中调用它并将其用作某些统计信息的基础,我们必须生成。使用视图时,它总是在日期过滤。

我想要这样的事情:

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

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

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

3 个答案:

答案 0 :(得分: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  

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

删除原始回复。 这就是我提出的问题

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. 获取所有记录
  3. 第二个数据集是从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
    
  4.    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