In SQL each column of a database table has a data type. These are types like NUMBER, VARCHAR(size) / TEXT, TIMESTAMP. What you perhaps don't know yet is that in PostgreSQL you can use tables as types. What does this mean? Let's say we have a table:

 CREATE TABLE person (   firstname TEXT,   lastname  TEXT,   email     TEXT ); 

Now you can use this table as a type for columns in other tables:

 CREATE TABLE article (   id       SERIAL,   content  TEXT,   author   person,   reviewer person ); 

Instead of repeating the three columns of a person twice, e.g. author_firstname, author_lastname, author_email, and reviewer_firstname, reviewer_lastname, reviewer_email, the person table defined before acts as a type. Of course, the usual way in SQL is to give each person an ID and reference persons via these IDs from other tables. But sometimes you do not want this reference semantics. In this example you might want to fix the values of author and reviewer for articles in time and not retroactively update them automatically if a person changes their last name or email address later.

How to access the columns of these types? For INSERT the syntax is as follows:

 INSERT INTO article (content, author, reviewer)   VALUES ('...',     ('Jane', 'Doe', 'jane.doe@example.com'),     ('John', 'Roe', 'jroe@example.com')   );  

Or with explicit names of the sub-columns:

 INSERT INTO article (content,     author.firstname,     author.lastname,     author.email,     reviewer.firstname,     reviewer.lastname,     reviewer.email)   VALUES ('...',     'Jane', 'Doe', 'jane.doe@example.com',     'John', 'Roe', 'jroe@example.com'   );  

In a SELECT query individual values can be accessed with the following syntax:

 SELECT   content,   (author).lastname,   (reviewer).lastname FROM article; 

Of course, tables that uses other tables as data types for their columns can be used as data types again.

One last thing worth mentioning is that these nested definitions can be mapped nicely to JSON:

 SELECT jsonb_pretty(to_jsonb(article)) FROM article; 
 {   "id": 1,   "content": "...",   "author": {     "email": "jane.doe@example.com",     "firstname": "Jane",     "lastname": "Doe"   },   "reviewer": {     "email": "jroe@example.com",     "firstname": "John",     "lastname": "Roe"   } }