SQL is considered a strongly typed language. This means that any piece of data represented by PostgreSQL has an associated data type, even if it is not plainly obvious. A data value's type both defines and constrains the kinds of operations which may be performed it.
Not only is every piece of data associated with a type, but types play a large part in the construction of tables. As stated in the section called Introduction to Relational Databases, tables are made up of one or more columns. These columns must, in addition to having a name, have a specific data type.
![]() | Extending Data Types |
|---|---|
While PostgreSQL provides a wide variety of built-in data types, you also have the option to add new data types to PostgreSQL using the CREATE TYPE command. See Chapter 5 for more information on this extensibility. |
Table 3-9 lists the data types officially supported by PostgreSQL, as well as any PostgreSQL recognized aliases (alternative names which are identical in connotation). There are many other internal (meaning they are no longer intended for normal use) or deprecated (outdated, and discouraged) data types available which are unlisted.
Additionally, while most of the data types implemented in PostgreSQL are directly derived from SQL standards, there are some actively maintained data types which are non-standard (such as the geometric and spacial types). Whether or not you are able to find equivalent types on other SQL-capable database management systems is therefore highly subjective.
Table 3-9. PostgreSQL Supported Data Types
Category | Data type | Description | Standardization |
|---|---|---|---|
Boolean and Binary Types | boolean, bool | A single true or false value. | SQL99 |
bit(n) | An n-length bit string (exactly n binary bits). | SQL92 | |
bit varying(n), varbit(n) | A variable n-length bit string (up to n binary bits) | SQL92 | |
Character Types | character (n), char(n) | A fixed n-length character string. | SQL89 |
character varying(n), varchar(n) | A variable length character string of up to n characters. | SQL92 | |
text | Variable length character string, of unlimited length. | PostgreSQL-specific | |
Numeric Types | smallint, int2 | A signed two-byte integer. | SQL89 |
integer, int, int4 | A signed, fixed-precision four-byte number. | SQL92 | |
bigint, int8 | A signed 8-byte integer, up to 18 digits in length. | PostgreSQL-specific | |
real, float4 | A single precision floating-point number. | SQL89 | |
double precision, float8, float | An 8-byte floating-point number. | SQL89 | |
numeric(p,s), decimal(p, s) | An exact numeric type with arbitrary precision p, and scale s. | SQL99 | |
money | A fixed precision, US-style currency. | PostgreSQL-specific, deprecated. | |
serial | An auto-incrementing four-byte integer. | PostgreSQL-specific | |
Date and Time Types | date | The calendar date (day, month and year). | SQL92 |
time | The time of day. | SQL92 | |
time with time zone | The time of day, including time zone information. | SQL92 | |
timestamp [with time zone] | Both the date and time. | SQL92 | |
interval | An arbitrarily specified length of time. | SQL92 | |
Geometric Types | box | A rectangular box in a two-dimensional plane. | PostgreSQL-specific |
line | infinite line in a 2D plane | PostgreSQL-specific | |
lseg | finite line segment in a 2D plane | PostgreSQL-specific | |
circle | a circle with center and radius | PostgreSQL-specific | |
path | Open and closed geometric paths in a two-dimensional plane. | PostgreSQL-specific | |
point | geometric point in a 2D plane | PostgreSQL-specific | |
polygon | closed geometric path in a 2D plane | PostgreSQL-specific | |
Network Types | cidr | An IP network specification | PostgreSQL-specific |
inet | A network IP address, with optional subnet bits | PostgreSQL-specific | |
macaddr | A MAC address (e.g., an Ethernet card's hardware address) | PostgreSQL-specific | |
System Types | oid | An object (row) identifier | PostgreSQL-specific |
xid | A transaction identifier | PostgreSQL-specific |
Remaining true to theme, the following sections on data types will describe in further detail each of the most widely used and practical types. This book will not go into detail on the non-standard and/or more esoteric types, such as the geometric, network and bitwise types.
These sections include information on valid usage, storage considerations, input and output formats and general syntactic conventions. Before we go much further on specific data types, however, a couple of topics worth discussing first are type coercion and the NULL keyword.
PostgreSQL supports three separate conventions for type coercion (also called type casting, or explicit type casting). Type coercion is a somewhat ugly looking term which refers to a PostgreSQL method for changing a value from one data type to another. In the middle of a SQL statement, this has the net effect of explicitly creating a constant of an arbitrary type.
Any one of the following three methods can be used in order to cast the value contained within a string constant to another type:
type 'value'
'value'::type
CAST ('value' AS type)
The value in this case represents the content that you are wishing to modify the data type of, and type represents the type that you wish to coerce, or cast, the data into. Bear in mind that not every data type can be coerced into every other data type. For example, there is no meaningful way to convert the character data 'abcd' into a binary bit type. Invalid casting will result in an error from PostgreSQL.
In addition to these type casting conventions, there are some functions which can be called to achieve essentially the same effect as an explicit cast of any of the previously mentioned forms. These often bear the name of the type itself (such as the timestamp() function), though others are named more specifically (such as bitfromint4()). Example 3-15 shows an example of such a function, converting the integer 1000 to a character string of type text representing the characters '1000'.
Example 3-15. Using Type Conversion Functions
booktown=# SELECT text(1000) booktown-# AS explicit_text; explicit_text --------------- 1000 (1 row)
![]() | Casting Non-String Constants |
|---|---|
Because of conflicting semantics recognized by PostgreSQL's parser, the type coercion format of type 'value' can only be used to specify the data type of a single value (e.g., a string constant bound by single quotes). In contrast, the other available methods of type coercion ('value'::type, CAST('value' AS type), and type conversion functions, where applicable) can be used to to specify the type of arbitrary expressions. This is partially because attempting to follow a data type with a grouped expression (e.g., in parentheses) will cause PostgreSQL to expect a function with the name of the provided data type (which will often cause an error), while each of the other methods are syntactically valid upon grouped expressions. booktown=# SELECT 1 + integer ('1' || '2') AS add_one_to_twelve;
ERROR: Function 'integer(text)' does not exist
Unable to identify a function that satisfies the given argument types
You may need to add explicit typecasts
booktown=# SELECT 1 + ('1' || '2')::integer AS add_one_to_twelve;
add_one_to_twelve
-------------------
13
(1 row)
booktown=# SELECT 1 + CAST('1' || '2' AS integer) AS add_on_to_twelve;
add_on_to_twelve
------------------
13
(1 row) |
The SQL keyword NULL is a very special exception to the rules that we have introduced regarding columns and discrete data types. While a column may only have one data type, any column (excluding those explicitly defined as NOT NULL) may contain the NULL value.
NULL can be thought of as a meta-value: a value which represents a lack of a value, which will never be equivalent to a non-NULL value. In practice, this can cause some confusion if the nature of NULL is not fully understood. This is demonstrated in Example 3-16.
Example 3-16. Observing NULL Values
booktown=# SELECT id, title FROM books; id | title ------+--------------------- 7808 | The Shining 156 | The Tell-Tale Heart 4513 | Dune 100 | 101 | (5 rows) booktown=# SELECT id, title FROM books WHERE title = ''; id | title -----+------- 100 | (1 row) booktown=# SELECT id, title FROM books WHERE title IS NULL; id | title -----+------- 101 | (1 row)
Example 3-16 shows a set of five books in the books table. The first SELECT query shows that there appear to be two books which have been inserted without titles. Upon successive querying, however, it becomes clear that while neither have visible titles, one of the books has an empty value for its title (id 100), while the other has a NULL value.
This is an important distinction, as the rules for SQL statements upon the NULL value are quite different from those applied to empty values. This is an especially important point in reference to joins, which are discussed in Chapter 4.
NULL is often used in places where a value is optional. It can be a convenient way of omitting data without having to resort to strange or arbitrary conventions, such as storing negative values in an integer field to represent omitted data. While your system requirements may change over time, the connotation of NULL is always NULL.
Example 3-17 demonstrates a more likely use of NULL in a table called editions, which relates a book's ISBN number to its publication date.
Example 3-17. Using NULL Values
booktown=# SELECT isbn, publication FROM editions;
isbn | publication
------------+-------------
039480001X | 1957-03-01
0394800753 | 1949-03-01
0385121679 |
(3 rows)
booktown=# SELECT isbn, publication FROM editions WHERE publication IS NULL;
isbn | publication
------------+-------------
0385121679 |
(1 row)NULL might be used in this manner in order to represent books with editions which are not yet published, or for books whose publication date was unknown when entered into the database. It could be misleading to supply some arbitrarily illogical date for a book fitting either of these criteria, and in both cases, NULL makes sense as a solution.
A boolean value is a simple data type, as it can only represent values of true, or false. PostgreSQL supports the SQL99-defined boolean type, with a PostgreSQL-specific alias of bool.
A lack of a value at all in a boolean column is, as documented in the section called NULL Values, called NULL, which is not the same as false.
Table 3-10 shows the valid constant values for a true or false state that are recognized by PostgreSQL. Which convention you choose to employ is dependent solely on your own preference, and are interpretted identically by the server.
Table 3-10. Supported True or False Constants
True | False |
|---|---|
true | false |
't' | 'f' |
'true' | 'false' |
'y' | 'n' |
'yes' | 'no' |
'1' | '0' |
Example 3-18 creates a simple table named daily_inventory which logs what books are stock and which are not, correlating an isbn number with a boolean value. Once created, the table is populated with data via a series of INSERT statements involving a string constant (the ISBN number), and a variety of valid boolean constants.
Example 3-18. Simple Boolean Table
booktown=# CREATE TABLE daily_inventory (isbn text, in_stock boolean);
CREATE
booktown=# INSERT INTO daily_inventory VALUES ('0385121679', true);
INSERT 3390926 1
booktown=# INSERT INTO daily_inventory VALUES ('039480001X', 't');
INSERT 3390927 1
booktown=# INSERT INTO daily_inventory VALUES ('044100590X', 'true');
INSERT 3390928 1
booktown=# INSERT INTO daily_inventory VALUES ('0451198492', false);
INSERT 3390929 1
booktown=# INSERT INTO daily_inventory VALUES ('0394900014', '0');
INSERT 3390930 1
booktown=# INSERT INTO daily_inventory VALUES ('0441172717', '1');
INSERT 3390931 1
booktown=# INSERT INTO daily_inventory VALUES ('0451160916');
INSERT 3390932 1Now that the table has been populated with records, a SELECT query may be issued to easily check which books are in stock, as shown in Example 3-19.
Example 3-19. Checking Boolean Values
booktown=# SELECT * FROM daily_inventory WHERE in_stock = 'yes';
isbn | in_stock
------------+----------
0385121679 | t
039480001X | t
044100590X | t
0441172717 | t
(4 rows)With a boolean column you have the ability to imply a true value by referencing the column name without any kind of operator or modifying keyword. This can lead to more intuitive looking queries for well-designed tables, as shown in Example 3-20.
Example 3-20. Implying Boolean 'true'
booktown=# SELECT * FROM daily_inventory WHERE in_stock;
isbn | in_stock
------------+----------
0385121679 | t
039480001X | t
044100590X | t
0441172717 | t
(4 rows)Although the second query does not specify 'true' or 'false', it implicitly looks for a value of 'true' by omitting a comparison operator.
Similarly, if you want to search for false values, you may either compare the named column's value against any of the valid boolean constants in Table 3-10, or you may use the SQL keyword NOT just before the column name. Each method is demonstrated in Example 3-21.
Example 3-21. Checking for 'false' Boolean Values
booktown=# SELECT * FROM daily_inventory WHERE in_stock = 'no';
isbn | in_stock
------------+----------
0451198492 | f
0394900014 | f
(2 rows)
booktown=# SELECT * FROM daily_inventory WHERE NOT in_stock;
isbn | in_stock
------------+----------
0451198492 | f
0394900014 | f
(2 rows)In this way, you can see how SQL was designed with human readability in mind. By naming your tables and columns in well-designed terms, a SQL query can read almost as plainly as an English sentence.
![]() | Caution with Boolean Constants |
|---|---|
If you decide to use the constants listed in Table 3-10, every value (except for true and false) must be enclosed within single quotes. Failure to do so will result in a server error. |
For the more programming-oriented readers, it may be of interest that you can use the inequality (!=) operator to compare the value of a boolean field against any of the values in Table 3-10 (e.g., WHERE in_stock != 't').
As such, the following three syntactic variations are each equivalent:
SELECT * FROM daily_inventory WHERE NOT in_stock; SELECT * FROM daily_inventory WHERE in_stock = 'no'; SELECT * FROM daily_inventory WHERE in_stock != 't';
You may have noticed that while seven rows were inserted into the table, only six rows were returned between the books found in stock, and those found out of stock. This is due to the last insertion in Example 3-18 not supplying a value at all for the in_stock column, leaving the record for the book with ISBN 0451160916 with a NULL value in the in_stock column.
As stated previously, NULL will not register as either true or false. As such, you may use the SQL phrase IS NULL to check for rows with NULL values.
booktown=# SELECT * FROM daily_inventory WHERE in_stock IS NULL;
isbn | in_stock
------------+----------
0451160916 |
(1 row)Since IS NULL is a general SQL phrase, you can use the same WHERE clause in an UPDATE statement to correct any accidental NULL values, as shown in Example 3-22.
Character types are required any time that you wish to reference character data, such as blocks of ascii text. They are commonly used for storing names, addresses, and so on.
SQL provides two character types called character, and character varying. In addition to these, a general text type is supported by PostgreSQL, which does not require an explicitly declared upper limit on the size of the field. This means that using the text type does not require you to specify the largest length that you want a value in that column to be as the standard character types do. Columns of type text are automatically re-sized according to the data you put in them, and they may re-size without boundaries (discounting, of course, the 1GB limit for a single field).
Table 3-11. Character Types
Type | Storage | Description |
|---|---|---|
character(n), char(n) | (4+n) bytes | A fixed-length character string, padded up to n characters |
character varying(n), varchar(n) | (4+n) bytes | A variable-length character string with a limit of n characters |
text | (4+n) bytes | A variable, unlimited-length character string |
The n in the section called Character Types represents an arbitrarily specified number of characters. This number is specified for a column when a table is created.
![]() | Text Portability |
|---|---|
Although the text data type is not part of the ANSI/ISO SQL standards, many other Relational Database Management Systems (RDBMS) provide this functionality, including Sybase and MS SQL Server. |
PostgreSQL's numeric types are used to represent both whole integers and decimal floating point values. From a general perspective, PostgreSQL's supported numeric types consist of:
Two, four, and eight byte integers
Eight byte floating point numbers
Fixed precision decimals
PostgreSQL has support for special types which fall under the family of numeric types, including the deprecated money type, and the special serial construct.
Table 3-12. Numeric Types Overview
Data type | Storage | Range |
|---|---|---|
bigint, int8 | 8 bytes | Whole integer values, -9223372036854775807 to +9223372036854775807 |
double precision, float8, float | 8 bytes | 15 significant digits, unlimited size (with limited precision) |
integer, int, int4 | 4 bytes | Whole integer values, -2147483648 to +2147483647 |
numeric(p,s), decimal (p,s) | Variable | Up to p digits (including to the right of the decimal), with a maximum of stotal digits |
real, float4 | 4 bytes | 6 significant digits, unlimited size (with limited precision) |
smallint, int2 | 2 bytes | Whole integers, -32768 to +32767 |
money | 4 bytes | Decimal values with a scale of 2 digits to the right of the decimal, -21474836.48 to +21474836.47 |
serial | 4 bytes | Whole integers, 0 to 2147483647 |
As shown in Table 3-12, several numeric data types have aliases, which are functionally identical in meaning to their associated data type. If you're not careful, however, they may not behave as you expect them to. For example, the default data type for the float alias is double precision, though you might have been expecting a real, or float4 type. The difference may seem trivial, but storage and precision conditions can be vital in designing table requirements.
Remember that to explicitly change such a type to real, you may use either the SQL type notation, or PostgreSQL-style type casts, as shown in Example 3-23
Example 3-23. Casting double precision to real
booktown=# SELECT REAL '3.1415' AS real_cast,
booktown-# CAST('3.1415' AS real) AS real_cast,
booktown-# '3.1415'::real AS real_cast;
real_cast | real_cast | real_cast
-----------+-----------+-----------
3.1415 | 3.1415 | 3.1415
(1 row)![]() | bigint/int8 Compatibility |
|---|---|
The bigint type may not be available on your system, as it depends upon support for eight-byte integers within the compiler used when installing and compiling PostgreSQL. If you are unsure, check with your compiler's vendor for details on its multi-byte integer support. |
The numeric (also known as decimal) type is a specially designed numeric data type which can represent arbitrarily large and precise values, within a fixed definition. When you create a table with a column of type numeric, you may specify in parentheses two values: the precision, and the scale.
The precision is the maximum number of sequential digits that the numeric value may hold (including digits to the right of the decimal point), while the scale describes how many of those digits of precision are to be to the right of the decimal point. If left unspecified, the precision will default to 30, and the scale will default to 6.
Table 3-13. Default Numeric Type
Attribute | Value |
|---|---|
Precision | 30 |
Scale | 6 |
Maximum | 999999999999999999999999.999999 |
Minimum | -999999999999999999999999.999999 |
Unlike the floating point data types, however, the numeric type will cause an overflow error if digits are specified outside of its precision range. You may somewhat safely specify non-zero digits which are smaller than the scale of the numeric definition, but only provided that the entirety of the numeric input fits within the maximum precision defined (though the value may be rounded).
For example, in a numeric(11,6) column, you may safely insert the value 9.9999999 with two digits too many to the right (though the value is rounded up to 10.000000). However, an attempt to insert the value 99999.99999999 will fail, as shown in Example 3-24.
This problem can be explicitly avoided by using the trunc() numeric truncating function, which is illustrated in Example 3-24. Used consistently, it also can keep unwanted rounding from happening by forcing an appropriate scale on the data before attempting to insert.
Example 3-24. Avoiding Overflow Errors
booktown=# INSERT INTO numbers VALUES (9.99999999);
INSERT 3390697 1
booktown=# SELECT * FROM numbers;
number
--------------
10.000000
(1 row)
booktown=# INSERT INTO numbers VALUES (99999.99999999);
ERROR: overflow on numeric ABS(value) >= 10^5 for field with precision 11 scale 6
booktown=# INSERT INTO numbers VALUES (trunc(99999.99999999, 6));
INSERT 3390698 1
booktown=# SELECT * FROM numbers;
number
--------------
10.000000
99999.999999
(2 rows)
booktown=# INSERT INTO numbers VALUES (trunc(9.99999999, 6));
INSERT 3390699 1
booktown=# SELECT * FROM numbers;
number
--------------
10.000000
99999.999999
9.999999
(3 rows)The money type is an automatically translated numeric type that formats to and from US-style currency notation, and plain numeric values. As of the writing of this book, the money type is deprecated, and is discouraged from being actively used. It is only presented here as it is still a functional data type, and may be in use on existing PostgreSQL systems.
The suggested alternative to the money type is the numeric type, with a scale of 2 to represent coin values, and a precision large enough to store the largest necessary monetary value (including two digits for the coin precision). Formatting similar to that of the money type can be achieved with the to_char function, as shown in Example 3-25. This example demonstrates the text concatentation operator covered in the section called Character and Text Operators in Chapter 4, and the ltrim() text formatting function, described in Chapter 4.
Example 3-25. A numeric Alternative to money
booktown=# INSERT INTO money_example VALUES ('$12.24', 12.24);
INSERT 3391095 1
booktown=# SELECT * FROM money_example;
money_cash | numeric_cash
------------+--------------
$12.24 | 12.24
(1 row)
booktown=# SELECT money_cash,
booktown-# '$' || ltrim(to_char(numeric_cash, '9999.99'))
booktown-# AS numeric_cashified
booktown-# FROM money_example;
money_cash | numeric_cashified
------------+-------------------
$12.24 | $12.24
(1 row)The serial type is non-standard, but useful, shortcut which allows you to easily create unique identifier columns within tables. It literally combines the functionality of a 4-byte integer data type, an index and a sequence.
In fact, as of the writing of this book, the two methods illustrated in Example 3-26 are functionally identical.
Example 3-26. Using the serial Data Type
booktown=# -- First, implicitly create the sequence
booktown=# -- and index via the serial type.
booktown=#
booktown=# CREATE TABLE auto_identified (id serial);
NOTICE: CREATE TABLE will create implicit sequence 'auto_identified_id_seq' for SERIAL column 'auto_identified.id'
NOTICE: CREATE TABLE/UNIQUE will create implicit index 'auto_identified_id_key' for table 'auto_identified'
CREATE
booktown=# -- Remove the table, and clean up the sequence behind it.
booktown=#
booktown=# DROP TABLE auto_identified;
DROP
booktown=# DROP SEQUENCE auto_identified_id_seq;
DROP
booktown=# -- Re-create the serial type's parts manually.
booktown=#
booktown=# CREATE SEQUENCE auto_identified_id_seq;
CREATE
booktown=# CREATE TABLE auto_identified
booktown-# (id integer UNIQUE DEFAULT nextval('auto_identified_id_seq'));
NOTICE: CREATE TABLE/UNIQUE will create implicit index 'auto_identified_id_key' for table 'auto_identified'
CREATE![]() | Caution with Implicit Sequences |
|---|---|
Upon dropping a table, the implicit sequence created for the serial types are not automatically dropped. You must clean up after these types of sequences if you destroy a table which had a serial column, as shown in Example 3-26, with the DROP SEQUENCE command. |
See Chapter 5 for more information on using sequences.
Date and time types are a convenient way to store date and time related data in a uniform SQL data structure, without having to worry about the conventions involved with storage (e.g., if you were to try to store such information in a character data type). PostgreSQL uses Julian dates for all date and time calculations. By fixing the length of a year at about 365.24 days, these Julian dates can correctly predict and calculate any date after 4713BC, as well as far into the future.
PostgreSQL supports all of the SQL92-defined date and time types, shown in Table 3-14, with some PostgreSQL-specific variations. Perhaps most notable of these variations is the extended flexibility pertaining to time zones.
Table 3-14. Date and Time Types
Name | Storage | Description | Range |
|---|---|---|---|
date | 4 bytes | A calendar date (year, month, and day) | 4713 BC to 32767 AD |
time | 4 bytes | The time of day only, without time zone information | 00:00:00.00 to 23:59:59.99 |
time with time zone | 4 bytes | The time of day only, including a time zone | 00:00:00.00+12 to 23:59:59.99-12 |
timestamp | 8 bytes | Both the calendar date and time, with time zone information | 1903 AD to 2037 AD |
interval | 12 bytes | A general time span interval | -1780000000 years to 17800000 years |
To ensure compatibility with earlier versions of PostgreSQL, the developers have continued to provide the older datetime and timespan data types. The datetime type is now equivalent to timestamp, while the timespan is now equivalent to the interval types.
Other date/time data types include abstime and reltime, which are lower precision types. However, these types are internal to PostgreSQL, and any or all of these types may disappear in a future release. It is advised therefore to design new applications with the SQL-compliant data types in mind, and to convert older applications from any of these data types as soon as is possible.
Date input can be accepted by PostgreSQL in many common formats, including the ISO-8601 format, the traditional SQL format, the original PostgreSQL format, and more. Table 3-15 lists several of these date formats.
These formats are relevant to the date and the timestamp data types.
Table 3-15. Valid Date Formats
Format Example | Description |
|---|---|
July 1, 2001 | Format inspecific: named month, day and year |
Sunday July 1, 2001 | Format inspecific: named day, named month, day and year |
July 15, 01 BC | Format inspecific: named month, day and year before the Common Era |
2001-07-01 | Standard ISO-8601 format: numeric year, month and day |
20010715 | ISO-8601: formatted numerically as complete year, month, day |
010715 | ISO-8601: formatted numerically as 2-digit year, month, day |
7/01/2001 | Non-European (US) format: numeric month, day and year |
1/7/2001 | European format: numeric day, month and year |
2001.182 | Numeric format, with complete year, and sequential day of the year |
When specifying a named month in a date value to PostgreSQL, you may either type the complete month name, or choose from a set of defined abbreviations for each month. These abbreviations are listed in Table 3-16.
Table 3-16. Month Abbreviations
Month | Abbreviation |
|---|---|
January | Jan |
February | Feb |
March | Mar |
April | Apr |
May | May |
June | Jun |
July | Jul |
August | Aug |
September | Sep, Sept |
October | Oct |
November | Nov |
December | Dec |
Similarly, Table 3-17 lists PostgreSQL-recognized abbreviations for weekday names.
Table 3-17. Day of the Week Abbreviations
Day | Abbreviation |
|---|---|
Sunday | Sun |
Monday | Mon |
Tuesday | Tue, Tues |
Wednesday | Wed, Weds |
Thursday | Thu, Thur, Thurs |
Friday | Fri |
Saturday | Sat |
Despite the wide variety of ways in which PostgreSQL can interpret date values, the values are always stored uniformally, and will be returned in a consistent format. As such, you have a variety of methods available to you to customize the default behavior with which date and time values are returned to you.
![]() | On Formatting |
|---|---|
While date values can always be formatted after being selected via several formatting functions (e.g., to_char()), it is more efficient to configure your defaults as close to the most commonly used conventions as you can before having to resort to manual type conversion and text formatting. |
To set the general date/time output format, the SET command can be applied to the run-time variable DATESTYLE. This variable may be set to one of four available general styles shown in Table 3-18.
Table 3-18. Date Output Formats
General Format | Description | Example |
|---|---|---|
ISO | ISO-8601 standard | 2001-06-25 12:24:00-07 |
SQL | Traditional SQL style | 06/25/2001 12:24:00.00 PDT |
Postgres | Original PostgreSQL style | Mon 25 Jun 12:24:00 2001 PDT |
German | Regional style for Germany | 25.06.2001 12:24:00.00 PDT |
As an example, you can use the following SQL statement to set the date style to SQL:
booktown=# SET DATESTYLE TO SQL; SET VARIABLE
If you perform a SELECT current_timestamp query after setting this variable, PostgreSQL should return the current time using the ISO format as instructed:
booktown=# SELECT current_timestamp;
timestamp
----------------------------
08/10/2001 13:25:55.00 PDT
(1 row)In addition to these general formats, PostgreSQL's date output format has two other variants which further describe how to display the date, shown in Table 3-19: European and non-European (US). These determine whether the format is month followed by day, or vice versa. This variation can be applied on top of the previous four general formats with the same syntax to SET DATESTYLE, and will not modify your chosen format except for the arrangement of the month and day.
Table 3-19. Extended Date Output Formats
Month/Day Format | Description | Example |
|---|---|---|
European | day/month/year | 12/07/2001 17:34:50.00 MET |
US, or Non-European | month/day/year | 07/12/2001 17:34:50.0 PST |
Furthermore, you may set both the general format and day/month convention by supplying both variables to the SET command, comma delimited. The order of these variables is not important to the SET command as long as the variables are not mutually exclusive (e.g., SQL and ISO), as shown in Example 3-27.
Example 3-27. Setting Date Formats
booktown=# SET DATESTYLE TO ISO,US; SET VARIABLE booktown=# SHOW DATESTYLE; NOTICE: DateStyle is ISO with US (NonEuropean) conventions SHOW VARIABLE booktown=# SET DATESTYLE TO NONEUROPEAN, GERMAN; SET VARIABLE booktown=# SHOW DATESTYLE; NOTICE: DateStyle is German with European conventions SHOW VARIABLE
If you do not specify a month/day format, a reasonable default will usually be chosen (e.g., European is the default for the German regional format).
While this is a convenient way to set the output format, it is important to notice that this is a run-time variable, which means that it exists only for the lifespan of your connected session to PostgreSQL when configured with the SET DATESTYLE command. There are two other ways to configure this format.
You may change the PGDATESTYLE environment variable used by the backend directly on postmaster's start-up (e.g., adding a Bash export PGDATESTYLE="FORMAT" variable setting to the postgres user's .bash_profile file). This has the effect of being global to all date and time formatting performed by PostgreSQL.
You may change the PGDATESTYLE environment variable used by a client application (assuming it was written with the libpq library) on its session start-up, if you wish the client rather than the server to configure the output. For example, setting the PGDATESTYLE variable at a Bash prompt with the export command before starting psql sets the format such that psql will recognize it, and automatically configure itself for that style of output.
Time values, like date values, may be entered in to a table in a number of ways. Commonly used formats are listed in Table 3-20. These apply to values of type time, and time with time zone.
Table 3-20. Valid Time Formats
Format Example | Description |
|---|---|
01:24 | ISO-8601, detailed to minutes |
01:24 AM | Equivalent to 01:24 (the AM attached is for readability only, and does not affect the value) |
01:24 PM | Equivalent to 13:24 (the hour must be less-than or equal to 12 to use PM) |
13:24 | 24-hour time, equivalent to 01:24 PM |
01:24:11 | ISO-8601, detailed to seconds |
01:24:11.112 | ISO-8601, detailed to microseconds |
012411 | ISO-8601, detailed to seconds, formatted numerically |
In addition to these formats, PostgreSQL allows for further description of a time value which is defined as time with time zone by supporting extra time zone parameters following the time value. The supported formats are illustrated in Table 3-21.
Table 3-21. Valid Time Zone Formats
Format Example | Description |
|---|---|
01:24:11-7 | ISO-8601, 7 hours behind GMT |
01:24:11-07:00 | ISO-8601, 7 hours, zero minutes behind GMT |
01:24:11-0700 | ISO-8601, 7 hours, zero minutes behind GMT |
01:24:11 PST | ISO-8601, Pacific Standard Time (7 hours behind GMT) |
The time with time zone data type is mainly supported by PostgreSQL to adhere to existing SQL standards, and for portability with other database management systems. If you need to work with time zones, it is recommended that you use the timestamp data type discussed in the section called Timestamps. This is primarily because of the fact that, due to daylight savings, time zones cannot always be meaningfully interpretted without an associated date.
Internally, PostgreSQL keeps track of all time zone information as a numeric offset of GMT (Greenwich Mean Time), which is also known as UTC (Universal Coordinated Time). By default, PostgreSQL's time display will use the time zone that your server's operating system is configured for. If you wish the time value to operate under a different time zone, there are four ways in which you can modify the output.
This variable is found by the backend server as the default time zone when the postmaster starts up. It can be set, for example, in the postgres user's .bash_profile file with a Bash export TZ='zone' command.
This variable can be set by a client written with the libpq library (such as psql).
This SQL command sets the time zone for the session to zone (e.g., SET TIMEZONE TO UTC
This SQL92 keyword can be used to specify zone as a text time zone (e.g., PST) or as an interval (e.g., interval('-07:00')). This clause may be applied in the middle of a SQL statement following a value which contains a timestamp (e.g., SELECT my_timestamp AT TIME ZONE 'PST'.
![]() | Time Zone Caveats |
|---|---|
If an invalid time zone is specified, then most systems will default to the GMT time zone. Additionally, if the compiler option USE_AUSTRALIAN_RULES was set when PostgreSQL was built, the EST time zone will refer to Australian Eastern Standard Time( with an offset of +10:00 hours from GMT) rather than US Eastern Standard Time. |
The PostgreSQL timestamp combines the functionality of the PostgreSQL date and time types into a single data type. The syntax of a timestamp value consists of a valid date format, followed by at least one whitespace character, and a valid time format. It can be followed optionally by a time zone value, if specified.
Combinations of all date and time formats listed in Table 3-15 and Table 3-20 are each supported in this fashion. Table 3-22 illustrates some examples of valid timestamp input.
Table 3-22. Some Valid Timestamp Formats
Format Example | Description |
|---|---|
1980-06-25 11:11-7 | ISO-8601 date format, with time detailed to minutes, and PST time zone |
25/06/1980 12:24:11.112 | European date format, with time detailed to microseconds |
06/25/1980 23:11 | US date format, with time detailed to minutes in 24-hour time |
25.06.1980 23:11:12 PM | German regional date format, with time detailed to seconds, and PM attached |
![]() | Time Zones and Timestamps |
|---|---|
While PostgreSQL supports the syntax of creating a column or value with the type timestamp without time zone, as of PostgreSQL 7.1.2 the resultant data type still contains a time zone. |
The SQL92 standard specifies a data typed called an interval, which represents a fixed span of time. By itself, an interval represents only a quantity of time, and does not begin or end at any set date or time. These intervals can be useful when applied to date and time values to calculate a new date or time, either by subtracting or adding the quantity. They can also be handy for quickly determining the precise interval between two date or time values. This can be achieved by subtracting date values, time values or timestamps from one another.
The two syntax variations in Table 3-23 can specify an interval within PostgreSQL:
Table 3-23. Interval Formats
Format | Description |
|---|---|
qty unit [ago] | A single quantity and unit of time, with an optional ago quantity inversion term |
[qty1 unit1, qty2 unit2, ... ] [ago] | Several quantities, each with a discrete unit of time, with an optional ago quantity inversion term |
Specifies the quantity of your interval, which may be any whole integer, or floating point number in the case of microseconds. The literal meaning of this number is qualified by the subsequent unit.
Qualifies the qty provided. The unit may be any one of the following keywords: second, minute, hour, day, week, month, year, decade, century, millennium. It can also be an abbreviation or plurals of the previously mentioned units.
The optional ago term of the interval determines whether or not you are describing a period of time before or after an associated time. You can think of it as a negative sign for date and time types.
Example 3-28. Interpretting Interval Formats
booktown=# SELECT date('1980-06-25');
date
------------
1980-06-25
(1 row)
booktown=# SELECT interval('21 years 8 days');
interval
-----------------
21 years 8 days
(1 row)
booktown=# SELECT date('1980-06-25') + interval('21 years 8 days')
booktown-# AS spanned_date;
spanned_date
------------------------
2001-07-03 00:00:00-07
(1 row)
booktown=# SELECT date('1980-06-25') - interval('21 years 8 days ago')
booktown-# AS twice_inverted_interval_date;
twice_inverted_interval_date
------------------------------
2001-07-03 00:00:00-07
(1 row)Example 3-28 shows functional syntax for date and interval values being meaningfully combined. You can see that subtracting an inverted time interval (e.g., one with the term ago) is functionally identical to adding a normal interval. This can be thought of as similar to the effect of adding negative numbers to integer values.
PostgreSQL provides three built-in functions for retreiving the current time, date, and timestamp. These are aptly named current_date, current_time, and current_timestamp.
In addition to these functions, there are many special constants which are supported by PostgreSQL. These constants represent common date/time values, such as now, tomorrow, and yesterday. The predefined date and time constants supported by PostgreSQL are listed in Table 3-24.
Table 3-24. Date and Time Constants
Constant | Description |
|---|---|
current | The current transaction time, deferred |
epoch | 1970-01-01 00:00:00+00 (UNIX's "Birthday") |
infinity | An abstract constant later than all other valid dates and times |
-infinity | An abstract constant earlier than all other valid dates and times |
now | The current transaction timestamp |
today | Midnight, on the current day |
tomorrow | Midnight, on the day after the current day |
yesterday | Midnight on the day before the current day |
The now and current timestamp constants may seem to be identical, looking solely at their names. They are, however, very different in terms of storing them in a table. The now constant is translated into the timestamp of the relevant transaction time (e.g., the time of insertion of an INSERT statement). In contrast, the current constant, as it is a deferred identifier, will actually appear as the phrase 'current' in the database if inserted. From there, it can be translated (e.g., via the to_char() function) to the timestamp associated with the transaction time of any query which requests that value. In other words, current will always tell you the "current" time when queried, regardless of when it was stored to the table.
Therefore, you generally always want to use now when storing a transaction timestamp in a table, or even the current_timestamp function, which is equivalent to the output of now. Example 3-29 shows how this could be a potentially disastrous SQL design issue if not properly understood. It shows a pair of INSERT statements; one which uses now, another which uses current.
Example 3-29. Comparing now to current
booktown=# INSERT INTO shipments (customer_id, isbn, ship_date)
booktown-# VALUES (1, '039480001X', 'current');
INSERT 3391221 1
booktown=# INSERT INTO shipments (customer_id, isbn, ship_date)
booktown-# VALUES (2, '0394800753', 'now');
INSERT 3391222 1
booktown=# SELECT isbn, ship_date FROM shipments;
isbn | ship_date
------------+------------------------
039480001X | current
0394800753 | 2001-08-10 18:17:49-07
(2 rows)
booktown=# SELECT isbn,
booktown-# to_char(ship_date, 'YYYY-MM-DD HH24:MI:SS')
booktown-# AS value
booktown-# FROM shipments;
isbn | value
------------+---------------------
039480001X | 2001-08-10 18:21:22
0394800753 | 2001-08-10 18:17:49
(2 rows)
booktown=# SELECT isbn, to_char(ship_date, 'YYYY-MM-DD HH24:MI:SS') AS value
booktown-# FROM shipments;
isbn | value
------------+---------------------
039480001X | 2001-08-10 18:22:35
0394800753 | 2001-08-10 18:17:49
(2 rows)Geometric types in PostgreSQL represent two dimensional spatial objects. These types are not standard SQL data types, and will not be discussed in depth in this book. Table 3-25 gives a brief overview of each of the available geometric types.
Table 3-25. Geometric Types
Type Name | Storage | Description | Example |
|---|---|---|---|
point | 16 bytes | A dimensionless object with no properties except for its location, where x and y is a floating point number. | (x, y) |
line | 32 bytes | Infinite line. The x and y are the end points of the line segment. | ((x1, y1),(x2, y2)) |
lseg | 32 bytes | Finite line segment. The x and y are the end points of the line segment | ((x1, y1),(x2, y2)) |
box | 32 bytes | Rectangular box. The points specified are the opposite corners of the box. | ((x1, y1),(x2, y2)) |
path | 4+32n bytes | Closed path (similar to polygon), a connected set of points | ((x1, y1),... ) |
path | 4+32n bytes | Open path, a connected set of points | [(x1, y1),...] |
polygon | 4+32n bytes | Polygon (similar to closed path), end points of the line segments that makes up the boundary of the polygon | ((x1, y1),...] |
circle | 24 bytes | The point (x,y) is the center while y is the radius of the circle | <(x, y),r> |
The original relational model specifies that the values represented by columns within a table be an atomic piece of data, object-relational database systems such as PostgreSQL allow non-atomic values to be used in queries through data structures called arrays.
An array is a collection of data values referenced through a single identifier. This array may be a collection of values of a built-in data type or a user-defined data type, but every value in the array must be of the same type. Arrays can be accessed from a table through subscript notation via square brackets (e.g., my_array[0]), and used as an array constant via curly braces within single quotes (e.g., '{value_one,value_two,value_three}').
When defining an array, the syntax allows for the array to be defined either as fixed-length or variable-length, however as of PostgreSQL 7.1.2, the fixed-length size restriction is not enforced. This means that you may treat the array as having a fixed number of elements at all times, but it can still be dynamically sized depending on the context in which it is treated. For example, it is perfectly acceptable for a single column defined as an array to contain three values in one record, four values in another, and no values in a third.
Additionally, arrays may be defined as being multi-dimensional, meaning that each element of the array may actually represent another array, rather than an atomic value. Values which are selected from a multi-dimensional array will consist of nested curly braces in order to show an array within an array.
booktown=# SELECT editions FROM my_notes WHERE title='The Cat in the Hat';
editions
---------------------------------------------------------------
{{"039480001X","1st Ed, Hard Cover"},{"0394900014","1st Ed"}}
(1 row)In order to actually insert array values into a table column, you of course need a way to refer to several values as an array in a SQL statement. The formal syntax of an array constant is a grouping of values, separated by delimiters (commas, for built-in data types), constrained by curly braces ({}), which are in turn constrained by single quotes.
'{value1 , value2 , [...] }' The values in this syntax can be any valid PostgreSQL data type. As the entire array is constrained by single quotes, the use of single quotes within an array value must be escaped, just as they must be within a string constant. The use of commas to delimit the values, however, poses an interesting problem pertaining to the use of character strings which contain commas themselves, as the commas will be interpreted as delimiters if not within single-quotes. However, as just mentioned, the singles quotes constrain the array, not the array's values.
PostgreSQL's method of handling this is to use double-quotes to quote string constants where single-quotes would ordinarily be used outside of an array context.
'{"value1" , "value 2, which contains a comma" }'It's vital to remember that arrays require the single quotes surrounding the curly braces in order to be interpretted correctly by PostgreSQL. You can think of array constants as being akin to a special type of string constant, which is interpretted as an array based on where it is used (e.g., when used to add records to a target column which is of an array data type). This is because, unless used in an array context, a constant of the this format will be interpretted by PostgreSQL as a normal string constant (as it is bound by single quotes) which just happens to include curly braces.