How 2 Soft Delete

HomePage.subtitle

HomePage.meta.avatar-alt
HomePage.meta.author
HomePage.meta.read-time

HomePage.guide.context

HomePage.guide.hard-delete.title

HomePage.guide.hard-delete.p1

HomePage.guide.hard-delete.hint-how-work

Hard Delete

Exemplo de hard delete em uma tabela de usuários

Step

Selecione alguma linha

id
name
email
11Alice Silvaalice@company.com
22Roberto Carlosroberto@company.com
33Julia Robertsjulia@company.com

SQL Console

readonly
[2026-03-31 23:16:51]db.public>SELECT * FROM users
[2026-03-31 23:16:51]db.public>3 rows retrieved in 1301 ms (execution: 11 ms, fetching: 1290 ms)

HomePage.guide.hard-delete.p2

HomePage.guide.soft-delete.title

HomePage.guide.soft-delete.p1

HomePage.guide.logical-delete.title

HomePage.guide.logical-delete.p1

HomePage.guide.logical-delete.hint-how-work

Logical Delete

Exemplo de soft delete em uma tabela de usuários

Step

Selecione alguma linha

id
name
email
archived_at
11Alice Silvaalice@company.comnull
22Roberto Carlosroberto@company.comnull
33Julia Robertsjulia@company.comnull

SQL Console

readonly
[2026-03-31 23:16:51]db.public>SELECT * FROM users
[2026-03-31 23:16:51]db.public>3 rows retrieved in 1301 ms (execution: 11 ms, fetching: 1290 ms)

HomePage.guide.logical-delete.hint-how-implement-p1

How to implement Logical Delete

readonly
ALTER TABLE users
ADD COLUMN archived_at TIMESTAMPTZ DEFAULT NULL;

HomePage.guide.logical-delete.hint-how-implement-p2

HomePage.guide.logical-delete.challenge-p1

HomePage.guide.logical-delete.hint-challenge

The Zombie Table

Exemplo do problema de tabela poluída com soft delete

Step

Faça um select para exibir todos os usuários ativos

id
name
email
archived_at
11Alice Silvaalice@company.com2026-02-19T10:22:32.123Z
22Roberto Carlosroberto@company.comnull
33Julia Robertsjulia@company.com2026-02-05T17:51:12.332Z
1-3 of 1000000

SQL Console

readonly
[2026-03-31 23:16:51]db.public>SELECT * FROM users
[2026-03-31 23:16:51]db.public>1000000 rows retrieved in 12092 ms (execution: 1092 ms, fetching: 11000 ms)

HomePage.guide.logical-delete.challenge-p2

HomePage.guide.shadow-table.title

HomePage.guide.shadow-table.p1

HomePage.guide.shadow-table.hint-how-work

Shadow Table Pattern

Exemplo do padrão shadow table para recuperação de dados

Step

Selecione alguma linha

id
name
email
11Alice Silvaalice@company.com
22Roberto Carlosroberto@company.com
33Julia Robertsjulia@company.com
id
table_name
record_id
data
archived_at
No rows selected

SQL Console

readonly
[2026-03-31 23:16:51]db.public>SELECT * FROM users
[2026-03-31 23:16:51]db.public>3 rows retrieved in 1301 ms (execution: 11 ms, fetching: 1290 ms)

HomePage.guide.shadow-table.hint-how-implement-p1

How to implement Shadow Table

readonly
CREATE OR REPLACE FUNCTION fn_shadow_archive()
RETURNS TRIGGER AS $$
DECLARE
v_cause_table TEXT;
v_cause_id_text TEXT;
BEGIN
v_cause_table := current_setting('app.current_deleter_table', true);
v_cause_id_text := current_setting('app.current_deleter_id', true);

IF (v_cause_table IS NULL OR v_cause_table = '') THEN
v_cause_table := TG_TABLE_NAME;
v_cause_id_text := OLD.id::text;

PERFORM set_config('app.current_deleter_table', v_cause_table, true);
PERFORM set_config('app.current_deleter_id', v_cause_id_text, true);
END IF;

INSERT INTO archives (
table_name,
record_id,
data,
caused_by_table,
caused_by_id
)
VALUES (
TG_TABLE_NAME,
jsonb_build_object('id', OLD.id),
to_jsonb(OLD),
v_cause_table,
jsonb_build_object('id', v_cause_id_text)
);

RETURN OLD;
END;
$$ LANGUAGE plpgsql;

HomePage.guide.shadow-table.hint-how-implement-p2

HomePage.guide.shadow-table.hint-triggers

How to create Shadow Table Triggers

readonly
CREATE TRIGGER trg_archive_users BEFORE DELETE ON users FOR EACH ROW EXECUTE FUNCTION fn_shadow_archive();
CREATE TRIGGER trg_archive_establishments BEFORE DELETE ON establishments FOR EACH ROW EXECUTE FUNCTION fn_shadow_archive();
CREATE TRIGGER trg_archive_schedulings BEFORE DELETE ON schedulings FOR EACH ROW EXECUTE FUNCTION fn_shadow_archive();

HomePage.guide.shadow-table.challenge-p1

HomePage.guide.shadow-table.hint-challenge

Restauring Data

Exemplo de restauração de dados arquivados

id
table_name
record_id
data
archived_at
caused_by_table
caused_by_id
12a8f5d9c-5f74-4d0f-8f61-1f45e6d0a101establishments{"id":"est-500"}{"id":"est-500","name":"Barbearia Central","owner_id":"user-owner-1","created_at":"2026-01-10T10:00:00.000Z"}2026-02-23T10:00:00.000Zestablishments{"id":"est-500"}
29d0f7b6a-2f43-4b23-a6df-3a4db44cf102schedulings{"user_id":"user-200","establishment_id":"est-500","scheduled_at":"2026-03-01T09:00:00.000Z"}{"user_id":"user-200","establishment_id":"est-500","scheduled_at":"2026-03-01T09:00:00.000Z","service":"Corte","status":"CONFIRMED"}2026-02-23T10:00:00.050Zestablishments{"id":"est-500"}
37bb2a10e-61b4-46e9-9f83-58d39f2aa103schedulings{"user_id":"user-201","establishment_id":"est-500","scheduled_at":"2026-03-01T10:00:00.000Z"}{"user_id":"user-201","establishment_id":"est-500","scheduled_at":"2026-03-01T10:00:00.000Z","service":"Barba","status":"PENDING"}2026-02-23T10:00:00.080Zestablishments{"id":"est-500"}
445d98f8a-8cf9-4f35-8f69-c9ec3f5b4104users{"id":"user-300"}{"id":"user-300","name":"Fernanda Lima","email":"fernanda@email.com","role":"CUSTOMER","created_at":"2026-01-15T12:00:00.000Z"}2026-02-23T11:00:00.000Zusers{"id":"user-300"}
5c0f57e2b-2d9f-4d8c-8f2e-98f703a6b105schedulings{"user_id":"user-300","establishment_id":"est-800","scheduled_at":"2026-03-05T14:00:00.000Z"}{"user_id":"user-300","establishment_id":"est-800","scheduled_at":"2026-03-05T14:00:00.000Z","service":"Manicure","status":"CONFIRMED"}2026-02-23T11:00:00.040Zusers{"id":"user-300"}
6ad3bcae9-7c31-4ceb-a20d-e4f952107106schedulings{"user_id":"user-300","establishment_id":"est-801","scheduled_at":"2026-03-06T16:00:00.000Z"}{"user_id":"user-300","establishment_id":"est-801","scheduled_at":"2026-03-06T16:00:00.000Z","service":"Pedicure","status":"PENDING"}2026-02-23T11:00:00.070Zusers{"id":"user-300"}
7f6a1db8e-34a9-4d3d-b7f3-27a8c1de7107schedulings{"user_id":"user-555","establishment_id":"est-999","scheduled_at":"2026-03-10T18:00:00.000Z"}{"user_id":"user-555","establishment_id":"est-999","scheduled_at":"2026-03-10T18:00:00.000Z","service":"Massagem","status":"CANCELLED_BY_USER"}2026-02-23T12:00:00.000Zschedulings{"user_id":"user-555","establishment_id":"est-999","scheduled_at":"2026-03-10T18:00:00.000Z"}
1-7 of 142

Mateus (mateus@gmail.com) acionou o suporte informando que apagou por engano o estabelecimento 'Barbearia X' e precisa recuperar o registro.

A cliente Fernanda Lima solicitou a recuperação da conta removida acidentalmente durante uma limpeza manual de dados.

O suporte recebeu pedido para restaurar um agendamento cancelado por engano, mantendo o histórico para auditoria.

SQL Console

readonly
[2026-03-31 23:16:51]db.public>SELECT * FROM archived LIMIT 7;
[2026-03-31 23:16:51]db.public>7 rows retrieved in 1432 ms (execution: 12 ms, fetching: 1420 ms)

HomePage.guide.conclusion.title

HomePage.guide.conclusion.p1

HomePage.guide.conclusion.item-1

HomePage.guide.conclusion.item-2

HomePage.guide.conclusion.item-3