Left outer join - how to return a boolean for existence in the second table?

Alexander Farber picture Alexander Farber · Nov 21, 2014 · Viewed 7.7k times · Source

In PostgreSQL 9 on CentOS 6 there are 60000 records in pref_users table:

# \d pref_users
                   Table "public.pref_users"
   Column   |            Type             |     Modifiers      
------------+-----------------------------+--------------------
 id         | character varying(32)       | not null
 first_name | character varying(64)       | not null
 last_name  | character varying(64)       | 
 login      | timestamp without time zone | default now()
 last_ip    | inet                        | 
 (... more columns skipped...)

And another table holds around 500 ids of users which are not allowed to play anymore:

# \d pref_ban2
                 Table "public.pref_ban2"
   Column   |            Type             |   Modifiers   
------------+-----------------------------+---------------
 id         | character varying(32)       | not null
 first_name | character varying(64)       | 
 last_name  | character varying(64)       | 
 city       | character varying(64)       | 
 last_ip    | inet                        | 
 reason     | character varying(128)      | 
 created    | timestamp without time zone | default now()
Indexes:
    "pref_ban2_pkey" PRIMARY KEY, btree (id)

In a PHP script I am trying to display all 60000 users from pref_users in a jQuery-dataTable. And I would like to mark the banned users (the users found in pref_ban2).

Which means I need a column named ban for each record in my query holding true or false.

So I am trying a left outer join query:

# select                          
       b.id,  -- how to make this column a boolean?
       u.id, 
       u.first_name, 
       u.last_name, 
       u.city,
       u.last_ip,
       to_char(u.login, 'DD.MM.YYYY') as day
from pref_users u left outer join pref_ban2 b on u.id=b.id
limit 10;
 id |    id    | first_name  | last_name |    city     |     last_ip     |    day     
----+----------+-------------+-----------+-------------+-----------------+------------
    | DE1      | Alex        |           | Bochum      | 2.206.0.224     | 21.11.2014
    | DE100032 | Княжна Мэри |           | London      | 151.50.61.131   | 01.02.2014
    | DE10011  | Aлександр Ш |           | Симферополь | 37.57.108.13    | 01.01.2014
    | DE10016  | Semen10     |           | usa         | 69.123.171.15   | 25.06.2014
    | DE10018  | Горловка    |           | Горловка    | 178.216.97.214  | 25.09.2011
    | DE10019  | -Дмитрий-   |           | пермь       | 5.140.81.95     | 21.11.2014
    | DE10047  | Василий     |           | Cумы        | 95.132.42.185   | 25.07.2014
    | DE10054  | Maedhros    |           | Чикаго      | 207.246.176.110 | 26.06.2014
    | DE10062  | ssergw      |           | москва      | 46.188.125.206  | 12.09.2014
    | DE10086  | Вадим       |           | Тула        | 109.111.26.176  | 26.02.2012
(10 rows)

As you can see the b.id column above is empty - because these 10 users aren't banned.

How to get a false value in that column instead of a String?

And I am not after some coalesceor case expression, but am looking for "the proper" way to do such a query.

Answer

Jordan Parmer picture Jordan Parmer · Nov 21, 2014

A CASE or COALESCE statement with an outer join IS the proper way to do this.

select
  CASE 
    WHEN b.id IS NULL THEN true
    ELSE false
  END AS banned,                          
  u.id, 
  u.first_name, 
  u.last_name, 
  u.city,
  u.last_ip,
  to_char(u.login, 'DD.MM.YYYY') as day
from pref_users u 
left outer join pref_ban2 b 
  on u.id=b.id
limit 10;