Author: FRANCIS NDUNGU
Last Updated: Thu, Jun 22, 2023A range is a sequential series of data. In a PostgreSQL database server, you can store a range of values using a mathematical interval notation that implements square brackets and parenthesis to define lower and upper bounds of all possible data elements. For instance, in a loan processing application, you can use the range datatype to store the following data:
The minimum and maximum allowed principal for each loan product (For example, you can represent a loan with a minimum allowed principal of $5,000 and a maximum principal of $100,000 as [5000, 100000]
).
The fluctuating annual interest rate of loans. This depends on the credit score of borrowers and the economy. (For example, the [8.5, 18.6]
value represents a loan with an annual interest rate that varies between 8.5% and 18.6%
).
The differing loan term (or number of installments) depending on the customers' capability. (For example, if a customer can pay a loan from 6 to 36 months, you can use a value of [6, 36]
to denote the information).
Without the PostgreSQL range datatype, you need two different columns to store the lower and upper bounds of all the above scenarios. This guide explains how to implement and use the PostgreSQL range data type, run common operators, and use the different functions on a Ubuntu 20.04 server.
Before you begin, be sure to:
The PostgreSQL server provides different range subtypes you can use to meet your needs. To check the availability of these subtypes in your database server, follow the steps below:
Log in to PostgreSQL as the user postgres
.
$ sudo -u postgres psql
Enter the correct user password and press enter to continue.
Enter the following command to retrieve available range subtypes.
postgres=# SELECT
oid,
typname
FROM pg_type
WHERE typname ~ 'range';
Output:
oid | typname
-------+------------
3904 | int4range
3926 | int8range
3906 | numrange
3908 | tsrange
3910 | tstzrange
3912 | daterange
...
Each of the PostgreSQL range subtypes represents:
int4range
: A range of 4 bytes integer
.
int8range
: A range of 8 bytes integer
.
numrange
: A range of numeric
values.
tsrange
: A range of timestamps data without timezones.
tstzrange
: A range of timestamps data with timezones.
daterange
: A range of dates.
After understanding the different PostgreSQL range subtypes, create a sample database.
The PostgreSQL range subtypes explained in the previous section are useful. This section describes how to use most of these range subtypes with real-life examples as explained below.
Create a sample_company
database.
postgres=# CREATE DATABASE sample_company;
Switch to the new sample_company
database.
postgres=# \c sample_company;
Create a sample loans_products
table.
sample_company=# CREATE TABLE loan_products (
product_id SERIAL PRIMARY KEY,
product_name VARCHAR(50),
principal NUMRANGE,
apr NUMRANGE,
no_of_installments INT4RANGE,
validity TSRANGE
);
This table stores information about the available loan products in sample bank applications. The application_id
column uniquely identifies each record in the table. The SERIAL
keyword instructs PostgreSQL to create auto-incremented records to avoid typing the product_ids
manually.
The product_name
column identifies the various loan products in the bank. The principal
and apr
(annual interest rate) columns use the NUMRANGE
data type, this datatype allows you to store the lower and upper bound limits of the allowed principal and interest for each loan.
The no_of_installments
column must contain a range of integers that define the repayment period of a loan in months. Then, the validity
column stores a range of timestamps to check whether a loan is available when customers submit their applications.
Before populating data to the table, examine the mathematical interval notation below. A square bracket denotes an inclusive bound that includes the boundary points in the range. A round parenthesis represents an exclusive bound that excludes the boundary points from the range.
[
Starts a range and includes the starting value in the range.
]
Ends a range and includes the end value in the range.
(
Start a range and excludes the starting value from the range.
)
Ends a range and excludes the end value from the range.
For example:
[a,b] - Includes all values between a
and b
in the range.
(a,b] - Includes all values ââbetween a
and b
, excludes a
, and includes b
in the range.
[a,b) - Includes all values between a
and b
, excludes b
but includes a
in the range.
(a,b) - Includes all values between a
and b
and excludes both a
and b
from the range.
Populate the loan_products
table as below.
sample_company=# INSERT INTO loan_products (product_name, principal, apr, no_of_installments, validity) VALUES ('EMERGENCY LOAN', '[1000,5000]', '[8,12]', '[3,12]', '[2023-01-01 00:00, 2023-12-31 23:59]');
INSERT INTO loan_products (product_name, principal, apr, no_of_installments, validity) VALUES ('DEVELOPMENT LOAN', '[3000,10000]', '[5,10]', '[12,36]', '[2023-01-01 00:00, 2023-05-31 23:59]');
INSERT INTO loan_products (product_name, principal, apr, no_of_installments, validity) VALUES ('STUDENT LOAN', '[500,1000]', '[4,7]', '[1,3]', '[2023-01-01 00:00, 2023-12-31 23:59]');
Output:
...
INSERT 0 1
Query the loan_products
table data.
sample_company=# SELECT
product_id,
product_name,
principal,
apr,
no_of_installments,
validity
FROM loan_products;
Output:
product_id | product_name | principal | apr | no_of_installments | validity
------------+------------------+--------------+--------+--------------------+-----------------------------------------------
1 | EMERGENCY LOAN | [1000,5000] | [8,12] | [3,13) | ["2023-01-01 00:00:00","2023-12-31 23:59:00"]
2 | DEVELOPMENT LOAN | [3000,10000] | [5,10] | [12,37) | ["2023-01-01 00:00:00","2023-05-31 23:59:00"]
3 | STUDENT LOAN | [500,1000] | [4,7] | [1,4) | ["2023-01-01 00:00:00","2023-12-31 23:59:00"]
(3 rows)
With the sample database in place, proceed to learn how you can use basic PostgreSQL range operators and functions.
You can achieve the full power of the PostgreSQL range datatype using inbuilt operators and functions. Below are the most common operators and functions.
@>
Checks if a range contains another range or element.
lower()
returns the lower bound of a given range.
upper()
returns the upper bound of a given range.
To test the above operator and functions, run the following examples:
Select all loan products that allow a customer to borrow $500 using the @>
operator.
sample_company=# SELECT
product_id,
product_name,
principal
FROM loan_products
WHERE principal @> '500'::NUMERIC;
Output:
product_id | product_name | principal
------------+--------------+------------
3 | STUDENT LOAN | [500,1000]
(1 row)
List the minimum and maximum principal allowed for each loan product using the lower()
and upper()
functions.
sample_company=# SELECT
product_id,
product_name,
lower(principal) as min_principal,
upper(principal) as max_principal
FROM loan_products;
Output:
product_id | product_name | min_principal | max_principal
------------+------------------+---------------+---------------
1 | EMERGENCY LOAN | 1000 | 5000
2 | DEVELOPMENT LOAN | 3000 | 10000
3 | STUDENT LOAN | 500 | 1000
(3 rows)
Select all loan products available on the first day of June 2023 using the @>
operator.
sample_company=# SELECT
product_id,
product_name,
principal,
validity
FROM loan_products
WHERE validity @> '2023-06-01'::timestamp;
Output:
product_id | product_name | principal | validity
------------+----------------+-------------+-----------------------------------------------
1 | EMERGENCY LOAN | [1000,5000] | ["2023-01-01 00:00:00","2023-12-31 23:59:00"]
3 | STUDENT LOAN | [500,1000] | ["2023-01-01 00:00:00","2023-12-31 23:59:00"]
(2 rows)
The examples in this guide only use a few records. For better query speed as your database grows, add a Generalized Search Tree-based Index (GIST) on the range columns. For instance, to create a principal
column index, run the following command.
sample_company=# CREATE INDEX principal_idx ON loan_products USING GIST (principal);
Output:
CREATE INDEX
In this guide, you implemented the PostgreSQL range data type on a Ubuntu 20.04 server. You have created a sample table, populated the range columns using a mathematical interval notation, and run queries to test PostgreSQL range operators and functions.
To leverage PostgreSQL functions, please visit the following resources.