PostgreSQL
PostgreSQL

PostgreSQL is quite popular, open source object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.

You can download PostgreSQL from https://www.postgresql.org/

When you install it you will get
  • postgres: PostgreSQL Server
  • psql: PostgreSQL Client (Command Shell)
  • PgAdmin (WebClient)

Here is a good tutorial for PostgreSQL http://www.postgresqltutorial.com/

Starting and Stopping the Server

In Windows you can choose whether PostgreSQL starts manually or automatically with the commands below. Choose the start type you want; these are alternatives.
Note

  1. You should replace postgresql-x64-12 with the version, you are using
  2. You have to execute below commands as the administrator
  • Change PostgreSQL to manual start (start it when needed with net start below)
    sc.exe config postgresql-x64-12 start= demand
  • Set the service start type to automatic
    sc.exe config postgresql-x64-12 start= auto
You can start/stop the server with below commands. Note: The service name may differ from the example; replace postgresql-x64-12 with the actual name of your PostgreSQL service.
  • net start postgresql-x64-12
  • net stop postgresql-x64-12
psql Commands

The following command connects to a database under a specific user. After pressing Enter, PostgreSQL will ask for the password.

psql -d database -U user

To connect to a database on another host, add the -h option:

psql -h host -d database -U user

Once connected, you can use the following backslash commands inside the psql shell:

Command Description
\c dbname [username]Switch connection to a different database
\lList all databases
\dtList tables in the current schema
\dt schema.*List tables in a specific schema
\dnList all schemas
\d tablenameDescribe a table (columns, types, constraints)
\diList indexes
\dvList views
\dfList functions
\duList roles and users
\timingToggle display of query execution time
\i filenameExecute SQL commands from a file
\o filenameSend query output to a file
\eOpen the last query in a text editor
\gRe-execute the previous command
\?Show help for psql backslash commands
\h [command]Show help for SQL commands (e.g. \h SELECT)
\qQuit psql
psql Console Commands Animation

The following animation demonstrates basic PostgreSQL commands in psql using illustrative output. Database names, owners, locale settings and output formatting depend on your installation. This example assumes a connection as the postgres superuser with permission to create databases.

Click "Start Demo" to begin the interactive psql console demonstration

Key takeaways from the demonstration:

  • \l: Lists databases on the server
  • CREATE DATABASE testdb;: Creates a new database
  • \c testdb: Connects to the new database; the prompt changes to testdb=#
  • CREATE TABLE: Creates a table in the connected database
  • \dt: Lists tables visible in the current search path
  • \d users: Shows columns, data types and constraints of the users table

SQL statements end with a semicolon. The psql backslash commands shown here (\l, \c, \dt and \d) do not need one.

Differences from MySQL

If you are familiar with MySQL, PostgreSQL will feel familiar in many ways. However, there are important differences worth knowing before you start.

Data Types

PostgreSQL has a richer type system than MySQL. The table below highlights the most common differences:

MySQLPostgreSQLNotes
INT AUTO_INCREMENTSERIAL / INT GENERATED ALWAYS AS IDENTITYSee the next section
TINYINT(1)BOOLEANPostgreSQL has a true boolean type; use TRUE / FALSE
VARCHAR(n)VARCHAR(n) or TEXTTEXT has no length limit and is fully equivalent in performance
DATETIMETIMESTAMP WITHOUT TIME ZONE (or TIMESTAMP)Stores date and time without automatic time zone conversion
TIMESTAMPTIMESTAMP WITH TIME ZONE (or TIMESTAMPTZ)Similar time zone behavior: values are stored as UTC and displayed in the session time zone. Supported date ranges differ.
JSONJSON / JSONBJSONB stores data in binary form and supports indexing; prefer JSONB
—UUIDSee the next section
—ARRAYColumns can store arrays of any type, e.g. INTEGER[]
Auto-increment: SERIAL and IDENTITY

MySQL uses AUTO_INCREMENT to create auto-incrementing columns. PostgreSQL has two approaches:

SERIAL (traditional, still widely used):

CREATE TABLE person (
  id   SERIAL PRIMARY KEY,
  name VARCHAR(100)
);

GENERATED AS IDENTITY (SQL standard, introduced in PostgreSQL 10) comes in two variants:

-- Strict: manual inserts are rejected; the value is always generated
CREATE TABLE person (
  id   INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name VARCHAR(100)
);

-- Flexible: a generated value is used unless you supply one explicitly
CREATE TABLE person (
  id   INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  name VARCHAR(100)
);

Use GENERATED ALWAYS when the identity column should never be set manually (safer for surrogate keys). Use GENERATED BY DEFAULT when you occasionally need to insert an explicit value, for example when migrating data from another database.

UUID as a Primary Key

A UUID (Universally Unique Identifier) is a 128-bit value represented as a 32-character hex string, for example a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11. PostgreSQL has a built-in UUID type that stores it efficiently in 16 bytes.

UUID is an alternative to integer primary keys. The main reasons to use it:

  • UUIDs can be generated anywhere — in the application, on a client device, or in multiple databases — with a negligible risk of collision.
  • Random UUIDs (UUIDv4) do not reveal how many rows exist or in what order they were created.
  • Easier to merge data from multiple databases or distribute writes across servers

The downside is that UUID primary keys are larger (16 bytes vs 4 bytes for INT) and because they are random, inserting many rows can fragment indexes and slow down range scans compared to sequential integers.

In PostgreSQL 13+ you can generate a UUID with the built-in function gen_random_uuid() — no extension needed:

CREATE TABLE order_item (
  id         UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
  order_id   UUID        NOT NULL,
  product    VARCHAR(100),
  quantity   INT
);

-- UUID is generated automatically on insert
INSERT INTO order_item (order_id, product, quantity)
VALUES ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', 'Laptop', 1);

-- You can also supply your own UUID explicitly
INSERT INTO order_item (id, order_id, product, quantity)
VALUES (gen_random_uuid(), gen_random_uuid(), 'Mouse', 2);

In earlier versions, enable the pgcrypto extension first:

CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- then use gen_random_uuid() in the same way

As a rule of thumb: use IDENTITY (integer) for most tables, and consider UUID when you need globally unique IDs across systems or want to hide row counts from users.

Sequences

Under the hood, SERIAL and IDENTITY both use a sequence — a database object that generates a series of unique numbers. You can also create and use sequences directly:

CREATE SEQUENCE order_seq START 1000 INCREMENT 10;

SELECT nextval('order_seq');      -- returns the next value
SELECT currval('order_seq');      -- returns the current value
SELECT setval('order_seq', 500);  -- set the current value manually

Sequences are independent of tables, so the same sequence can serve multiple tables or be reset without affecting existing data.

DDL Transactions

In PostgreSQL, DDL statements (CREATE TABLE, DROP TABLE, ALTER TABLE, etc.) can be included inside a transaction and rolled back if something goes wrong.

BEGIN;

CREATE TABLE test_table (id SERIAL PRIMARY KEY, name TEXT);
INSERT INTO test_table (name) VALUES ('Alice');

ROLLBACK;  -- both the table and the row are gone

In MySQL, DDL statements cause an implicit commit and cannot be rolled back. PostgreSQL DDL transactions make schema migrations significantly safer.

RETURNING Clause

PostgreSQL supports a RETURNING clause on INSERT, UPDATE, and DELETE statements. It returns the affected rows immediately, removing the need for a separate SELECT.

-- Get the generated id after insert
INSERT INTO person (name) VALUES ('Alice') RETURNING id;

-- Get the updated row after update
UPDATE person SET name = 'Bob' WHERE id = 1 RETURNING id, name;

-- Get the deleted row
DELETE FROM person WHERE id = 1 RETURNING *;

MySQL has no equivalent; you must use LAST_INSERT_ID() or run a separate query.

ILIKE — case-insensitive LIKE

In MySQL, LIKE is case-insensitive by default for most collations. In PostgreSQL, LIKE is case-sensitive. Use ILIKE for case-insensitive matching:

-- case-sensitive: matches 'alice', not 'Alice'
SELECT * FROM person WHERE name LIKE 'ali%';

-- case-insensitive: matches 'alice', 'Alice', 'ALICE'
SELECT * FROM person WHERE name ILIKE 'ali%';
Case Sensitivity of Identifiers

In PostgreSQL, unquoted identifiers (table names, column names) are automatically folded to lowercase. Quoting an identifier preserves its case but makes it case-sensitive.

In the example below, ... stands for omitted column definitions.

CREATE TABLE Person (...);  -- table name stored as: person

SELECT * FROM person;    -- works
SELECT * FROM Person;    -- works (folded to lowercase)
SELECT * FROM "Person";  -- ERROR: relation "Person" does not exist

The safe rule: always use lowercase identifiers and avoid quoting them. If you migrate a MySQL schema with mixed-case names, rename them to lowercase in PostgreSQL.

Connecting from Node.js

The most common PostgreSQL client for Node.js is node-postgres (pg). Install it with:

npm install pg

Connect using a connection string and a Pool, which manages multiple simultaneous connections efficiently:

const { Pool } = require('pg');

const pool = new Pool({
  connectionString: 'postgresql://libuser:libpass@localhost:5432/library',
});

async function getBooks() {
  const res = await pool.query('SELECT * FROM book WHERE author = $1', ['Tolkien']);
  return res.rows;
}

Note that PostgreSQL uses $1, $2, ... as parameter placeholders, unlike MySQL which uses ?.

When connecting to a remote server, enable SSL by adding an ssl option. In most cloud environments the server certificate is signed by a trusted CA, so you only need:

const pool = new Pool({
  connectionString: 'postgresql://libuser:libpass@db.example.com:5432/library',
  ssl: { rejectUnauthorized: true },
});

If you are connecting to a server with a self-signed certificate (common in development), set rejectUnauthorized: false — but never use this setting in production.

Procedures

A stored procedure is a named routine stored in the database. Use CREATE OR REPLACE PROCEDURE to create it or update its body, and CALL to execute it. The examples below use PL/pgSQL, PostgreSQL's procedural language, which supports variables, conditions, and error handling.

Run the following examples in order in a practice database, using psql or a graphical client.

Creating and Calling a Procedure

First, create a table for the examples:

CREATE TABLE person (
    id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

This procedure takes a name as an input parameter and inserts a new row:

CREATE OR REPLACE PROCEDURE add_person(IN p_name VARCHAR(100))
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO person (name) VALUES (p_name);
END;
$$;

CALL add_person('Alice');
CALL add_person('Bob');

SELECT * FROM person ORDER BY id;
-- With a newly created table: 1 | Alice, 2 | Bob
  • IN marks an input parameter; it is also the default parameter mode. The prefix p_ helps distinguish parameter names from column names.
  • LANGUAGE plpgsql specifies the language used for the procedure body.
  • $$ encloses the body, so single quotes inside it do not need to be doubled. No MySQL-style DELIMITER command is needed.
  • BEGIN ... END; groups the statements in the procedure body; it does not start a transaction.
Parameters and Error Handling

This procedure uses two input parameters to update a person's name. PL/pgSQL sets FOUND after the update; if no row matched, the procedure raises an error.

CREATE OR REPLACE PROCEDURE rename_person(
    IN p_id INTEGER,
    IN p_name VARCHAR(100)
)
LANGUAGE plpgsql
AS $$
BEGIN
    UPDATE person
    SET name = p_name
    WHERE id = p_id;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'No person with id %', p_id;
    END IF;
END;
$$;

CALL rename_person(1, 'Alice Smith');
SELECT * FROM person WHERE id = 1;
-- 1 | Alice Smith

-- Run separately to try the error case:
CALL rename_person(-1, 'Unknown');
-- ERROR: No person with id -1

A procedure is called with CALL, rather than used as an expression in a SELECT. Use a function when you need a routine that returns a value for use in a query.

Removing a Procedure

Include the input parameter types when identifying the procedure to remove. You can clean up the examples with:

DROP PROCEDURE add_person(VARCHAR);
DROP PROCEDURE rename_person(INTEGER, VARCHAR);
DROP TABLE person;

See the PostgreSQL documentation for CREATE PROCEDURE and CALL.

Schemas in PostgreSQL

In PostgreSQL, a schema is a logical namespace that holds database objects such as tables, views, functions, and indexes. It allows multiple users or applications to organize and isolate data within the same database.

In PostgreSQL, schemas are useful when you need to separate objects for different applications, isolate tenants in a shared database, or keep development, testing, and production structures organized. They also help manage user-specific namespaces and group related objects for easier security and maintenance.

In many cases, creating a separate schema is not necessary. For small projects, single-user databases, or applications with only a few tables, the default public schema is often sufficient. If you don’t need to isolate data, manage multiple environments in the same database, or apply schema-level permissions, using the default schema keeps the database simpler and easier to maintain.

Default Schema: public

Every PostgreSQL database contains a default schema called public. If no schema is explicitly specified when creating or querying objects, PostgreSQL uses this schema by default.

Creating a Schema

You can create your own schema with the following SQL command:

CREATE SCHEMA libschema;
Using a Schema

When creating objects inside a specific schema, you can prefix the schema name:

CREATE TABLE libschema.book (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255),
  author VARCHAR(255),
  isbn VARCHAR(255)
);
Setting the Search Path

To make a schema the default for object lookup, you can set the search path in your session:

SET search_path TO libschema;

After setting this, you can reference tables and other objects without specifying the schema name.

Benefits of Schemas
  • Organize database objects more clearly
  • Support multi-tenancy within a single database
  • Control access and permissions at schema level
  • Avoid naming conflicts between applications
Listing Schemas

In psql, you can list all schemas using:

\dn
Managing Users and Schema Privileges

The default administrator is postgres. You have to first login with that user and then you can create new users and give privileges to them.

Example: Create a new user libuser and give privileges to all tables in database library to that user.

CREATE ROLE libuser WITH LOGIN PASSWORD 'libpass';
And then you can give permissions to that user, like this
  1. Set the default database with the command
    \c library
    
  2. Add privileges to public schema with the command
    grant ALL ON SCHEMA public to libuser;
    
  3. Add privileges to all tables with the command
    GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO libuser;
    
  4. Create the schema named libschema and assign libuser as its owner
    CREATE SCHEMA libschema AUTHORIZATION libuser;
    

Database Backup and Restore

pg_dump works mostly the same way as mysqldump. So you can start it from the command line and backup a database like this

pg_dump -U user database > db.sql
Then you can restore the database with this command
psql -U user -d database -f db.sql
Note: In Windows pg_dump and psql are located in ProgramFiles\PostgreSQL\...\bin
In order to make it work from any folder, you should add that path to your environment variables

Graphical Clients

You can use graphical clients to manage PostgreSQL databases and run SQL queries:

  • pgAdmin is a graphical administration tool specifically for PostgreSQL.
  • DBeaver is a graphical database client that supports PostgreSQL as well as other database systems, including MySQL and SQL Server.
Exercise: Schemas and User Permissions

Login to your PostgreSQL as administrator (=postgres) and execute below commands

CREATE DATABASE test;
\c test;

CREATE ROLE user1 WITH LOGIN PASSWORD 'user1';
CREATE ROLE user2 WITH LOGIN PASSWORD 'user2';

CREATE SCHEMA schema1;
CREATE SCHEMA schema2;

CREATE TABLE schema1.table1(
	idTable1 INT primary key,
	name CHAR(50)
);

CREATE TABLE schema2.table2(
	idTable2 INT primary key,
	name CHAR(50)
);

insert into schema1.table1 VALUES (1,'Jim');
insert into schema2.table2 VALUES (1,'Lisa');

GRANT USAGE ON SCHEMA schema1 to user1;
GRANT USAGE ON SCHEMA schema2 to user2;

GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA schema1 to user1;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA schema2 to user2;
Then login as a user1 (psql -U user1 -d test) and execute below commands. Some of them does not work. Try to figure out why.
\dt
SELECT * FROM table1;
SELECT * FROM schema1.table1;
SELECT * FROM schema2.table2;

set search_path to schema1;
\dt
SELECT * FROM table1;
Then you can delete the database and user1 and user2.
user1 and user2 should disconnect first

as a administrator
\c test
DROP TABLE schema1.table1;
DROP SCHEMA schema1;
DROP USER user1;

DROP TABLE schema2.table2;
DROP SCHEMA schema2;
DROP USER user2;

\c postgres
DROP DATABASE test;



Toggle Menu