Skip to main content

Tables, views, and data

Tables are the core objects that store business data in PostgreSQL. When designing tables, consider data types, primary keys, constraints, indexes, permission policies, and future migration costs.

Table design

For each business table, define at least the following items:

ItemRecommendation
Primary keyUse bigint generated by default as identity or uuid. Avoid business fields as primary keys
Required fieldsSet not null for fields that must exist
DefaultsSet defaults for created time, status, owner, and similar fields
Unique constraintsAdd unique constraints for emails, external order IDs, and other unique business keys
Ownership fieldFor multi-user tables, keep a user_id field for RLS policies
create table public.todos (
id bigint generated by default as identity primary key,
user_id varchar(64) not null default auth.uid(),
title text not null,
done boolean not null default false,
priority int not null default 0,
created_at timestamptz not null default now()
);

Common field types

TypeUse case
textTitles, descriptions, nicknames
booleanFlags such as done or enabled
int / bigintCounts, sort values, identity primary keys
numericMoney and exact decimals
timestamptzTime with time zone, recommended for created and updated times
jsonbFlexible attributes, configs, third-party callback payloads
uuidDistributed IDs and public IDs
enumSmall and stable fixed values, such as order status or workflow stage

For array fields, see Arrays. For fixed value sets, see Enum types. For flexible data, see JSON and unstructured data.

Constraints

Constraints keep data valid at the database layer:

  • primary key: uniquely identifies a row.
  • not null: prevents empty values.
  • unique: prevents duplicate values.
  • check: requires values to satisfy an expression.
  • foreign key: enforces relationships between tables.
alter table public.todos
add constraint todos_priority_check check (priority between 0 and 5);

For foreign keys, see Foreign key configuration.

Views

Views are useful for wrapping common queries or exposing a reduced set of fields.

create view public.active_todos as
select id, title, priority, created_at
from public.todos
where done = false;

If the underlying table has RLS enabled, check the view security mode. PostgreSQL 15 and later can use security_invoker = true so the view runs with the caller's permissions.

create view public.active_todos
with (security_invoker = true) as
select id, title, priority, created_at
from public.todos
where done = false;

For details, see Basic permissions.

Data management

During development, you can maintain small amounts of data through DMC or the console. For bulk imports, complex updates, and production fixes, use SQL scripts and keep execution records.

insert into public.todos (title, priority)
values
('Prepare launch checklist', 3),
('Add database indexes', 2);

Business code can read and write data through SDKs, HTTP APIs, or server-side direct connections. Configure RLS before allowing frontend access to user data.

Schema changes

Production schema changes should be backward compatible where possible. A common flow is to add fields first, release code gradually, and remove old fields later.

Avoid long-running lock operations during peak traffic. For large tables, test index creation, bulk updates, and type conversions in a staging environment first.