sqloracle-database

Using an intermediate view seems to exclude rows with null values


I am observing a behaviour in Oracle SQL which I don't understand.

create table persons (
  id number not null,
  client_id number not null
    constraint foo_fk
      references clients,

  person_code number -- can be null
    constraint foobar_fk
      references codes, --some other table that contains only codes

  is_active char,

  constraint foo_pk
    primary key (id, client_id)

)
/


create view active_persons_v as
select
    id, client_id, person_code

  from persons where is_active = 'Y'
/


create table clients (
  id number not null
    constraint whatever_pk
    primary key
)
/

I'm running the two following queries :

(with the view)


select
  clients.id
  active_persons_v.id,
  active_persons_v.person_code

from clients
left join active_persons_v -- use the view
  on clients.id = active_persons_v.client_id
where
  client.id = 4444;

(without the view)

select
  clients.id
  persons.id,
  persons.person_code

from clients
left join persons --use the table
  on clients.id = persons.client_id
where
  client.id = 4444
  and persons.is_active = 'Y'; -- filter like the view would

I'm getting a different row count:

I don't understand why. The second query is supposed to replicate the first query exactly.

I have noticed one thing, though: the result of the second query is exactly the same as the result of the first one if I flipped the order of the two tables in the LEFT JOIN.

To me, this makes no sense. Does anyone have an idea?


Solution

  • from clients
    left join persons --use the table
      on clients.id = persons.client_id
    where
      client.id = 4444
      and persons.is_active = 'Y'; -- filter like the view would
    

    No, you are not. This is how the view filters rows:

    from clients
    left join persons --use the table
      on clients.id = persons.client_id and persons.is_active = 'Y'
    where
      client.id = 4444;
    

    When you outer join rows, it means that in case there is no match, a dummy row with all nulls gets joined. Your where clause is dismissing these rows.