Про размер индекса -- верно, частичный индекс экономит место, если NULL-ов ожидается много. Но при этом надо понимать, что будут последствия для оптимайзера (всё же это индекс и он может пригодиться не только как ограничение целостности).
Какие такие последствия?
Берём две таблички
m=# \d a
Table "public.a"
Column | Type | Modifiers
--------+---------+-----------
num | integer |
txt | text |
Indexes:
"a_txt_key" UNIQUE, btree (txt)
m=# \d b
Table "public.b"
Column | Type | Modifiers
--------+---------+-----------
num | integer |
txt | text |
Indexes:
"u_b" UNIQUE, btree (txt) WHERE txt IS NOT NULL
Заполняем данными
m=# insert into a select 1, random()::text from generate_series(1, 10000);
INSERT 0 10000
m=# insert into a select 1, NULL from generate_series(1, 1000);
INSERT 0 1000
m=# insert into b select 1, random()::text from generate_series(1, 10000);
INSERT 0 10000
m=# insert into b select 1, NULL from generate_series(1, 1000);
INSERT 0 1000
Ну и финальное, перед сном)
m=# explain select * from a order by txt limit 10;
QUERY PLAN
-------------------------------------------------------------------------------
Limit (cost=0.00..0.84 rows=10 width=36)
-> Index Scan using a_txt_key on a (cost=0.00..711.56 rows=8487 width=36)
(2 rows)
m=# explain select * from b order by txt limit 10;
QUERY PLAN
--------------------------------------------------------------------
Limit (cost=416.71..416.73 rows=10 width=36)
-> Sort (cost=416.71..444.21 rows=11000 width=36)
Sort Key: txt
-> Seq Scan on b (cost=0.00..179.00 rows=11000 width=36)
(4 rows)