values. Since a query searching for a common value (one that
accounts for more than a few percent of all the table rows) will not
use the index anyway, there is no point in keeping those rows in the
index at all. This reduces the size of the index, which will speed
up those queries that do use the index. It will also speed up many table
update operations because the index does not need to be
updated in all cases. <xref linkend="indexes-partial-ex1"/> shows a
possible application of this idea.
</para>
<example id="indexes-partial-ex1">
<title>Setting up a Partial Index to Exclude Common Values</title>
<para>
Suppose you are storing web server access logs in a database.
Most accesses originate from the IP address range of your organization but
some are from elsewhere (say, employees on dial-up connections).
If your searches by IP are primarily for outside accesses,
you probably do not need to index the IP range that corresponds to your
organization's subnet.
</para>
<para>
Assume a table like this:
<programlisting>
CREATE TABLE access_log (
url varchar,
client_ip inet,
...
);
</programlisting>
</para>
<para>
To create a partial index that suits our example, use a command
such as this:
<programlisting>
CREATE INDEX access_log_client_ip_ix ON access_log (client_ip)
WHERE NOT (client_ip > inet '192.168.100.0' AND
client_ip < inet '192.168.100.255');
</programlisting>
</para>
<para>
A typical query that can use this index would be:
<programlisting>
SELECT *
FROM access_log
WHERE url = '/index.html' AND client_ip = inet '212.78.10.32';
</programlisting>
Here the query's IP address is covered by the partial index. The
following query cannot use the partial index, as it uses an IP address
that is excluded from the index:
<programlisting>
SELECT *
FROM access_log
WHERE url = '/index.html' AND client_ip = inet '192.168.100.23';
</programlisting>
</para>
<para>
Observe that this kind of partial index requires that the common
values be predetermined, so such partial indexes are best used for
data distributions that do not change. Such indexes can be recreated
occasionally to adjust for new data distributions, but this adds
maintenance effort.
</para>
</example>
<para>
Another possible use for a partial index is to exclude values from the
index that the
typical query workload is not interested in; this is shown in <xref
linkend="indexes-partial-ex2"/>. This results in the same
advantages as listed above, but it prevents the
<quote>uninteresting</quote> values from being accessed via that
index, even if an index scan might be profitable in that
case. Obviously, setting up partial indexes for this kind of
scenario will require a lot of care and experimentation.
</para>
<example id="indexes-partial-ex2">
<title>Setting up a Partial Index to Exclude Uninteresting Values</title>
<para>
If you have a table that contains both billed and unbilled orders,
where the unbilled orders take up a small fraction of the total
table and yet those are the most-accessed rows, you can improve
performance by creating an index on just the unbilled rows. The
command to create the index would look like this:
<programlisting>
CREATE INDEX orders_unbilled_index ON orders (order_nr)
WHERE billed is not true;
</programlisting>
</para>
<para>
A possible query to use this index would be:
<programlisting>
SELECT * FROM orders WHERE billed is not true AND order_nr < 10000;
</programlisting>
However, the index can also be used in queries that do not involve
<structfield>order_nr</structfield> at all, e.g.:
<programlisting>
SELECT * FROM orders WHERE billed is not true AND amount > 5000.00;
</programlisting>
This is not as efficient as a partial index on the
<structfield>amount</structfield> column would