Skip to content
Back to skills

Sql Data Retention Purge

ASecurity

Designs and audits SQL Server data retention and purge policies: temporal table SYSTEM_VERSIONING with retention period, soft-delete with retention date columns, scheduled or partition-switch-based purge jobs, compliance archiving (GDPR right-to-erasure, right-to-be-forgotten), and PII column scrubbing before archive. Ensures purge jobs respect multitenancy isolation.

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 6, 2026
developmentgosql

Security analysis

A100/100

Scanned September 6, 2026

npx -y skills add AgenticPawan/FullStack-Pilot --skill sql-data-retention-purge --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Sql Data Retention Purge?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Sql Data Retention Purge
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/agenticpawan-sql-data-retention-purge/badge)](https://www.skillsdirectory.com/skills/agenticpawan-sql-data-retention-purge)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: sql-data-retention-purge
description: "Designs and audits SQL Server data retention and purge policies: temporal table SYSTEM_VERSIONING with retention period, soft-delete with retention date columns, scheduled or partition-switch-based purge jobs, compliance archiving (GDPR right-to-erasure, right-to-be-forgotten), and PII column scrubbing before archive. Ensures purge jobs respect multitenancy isolation."
when_to_use: data retention, purge, gdpr erasure, soft delete purge, temporal table cleanup, partition switch delete, pii scrubbing, right to erasure, archive, data lifecycle, retention policy, cleanup job, scheduled delete
---

## Standard IDs

| ID | Severity | What it checks |
|----|----------|----------------|
| RET-001 | P0 | PII columns hard-deleted without scrubbing — GDPR right-to-erasure violated |
| RET-002 | P1 | Table flagged for retention has no `RetainUntil` / `DeletedAt` column or temporal history |
| RET-003 | P1 | Purge job or stored procedure deletes rows without a `TenantId` / `OrganisationId` filter |
| RET-004 | P2 | Batch delete runs as single transaction on >10k rows — risks log growth and blocking |
| RET-005 | P2 | Temporal `SYSTEM_VERSIONING` ON but no `HISTORY_RETENTION_PERIOD` set |

---

## Check A — Retention column pattern (RET-002)

Every table that holds user-generated or personally identifiable data must have either:

**Option 1 — Soft-delete with explicit retention date**

```sql
-- Migration adds RetainUntil
ALTER TABLE dbo.CustomerNotes
    ADD RetainUntil DATE NOT NULL
        CONSTRAINT DF_CustomerNotes_RetainUntil DEFAULT DATEADD(YEAR, 7, GETUTCDATE());
```

**Option 2 — Temporal table with retention policy**

```sql
ALTER TABLE dbo.CustomerNotes
    ADD PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo);

ALTER TABLE dbo.CustomerNotes
    SET (SYSTEM_VERSIONING = ON (
        HISTORY_TABLE = dbo.CustomerNotesHistory,
        HISTORY_RETENTION_PERIOD = 7 YEARS
    ));
```

---

## Check B — Purge job with tenant isolation (RET-003 / RET-004)

### BAD — No tenant filter, single large transaction

```sql
DELETE FROM dbo.CustomerNotes
WHERE RetainUntil < GETUTCDATE();
```

### GOOD — Batched with tenant scope

```sql
DECLARE @BatchSize INT = 5000, @Deleted INT = 1;
WHILE @Deleted > 0
BEGIN
    DELETE TOP (@BatchSize) FROM dbo.CustomerNotes
    WHERE TenantId = @TenantId
      AND RetainUntil < GETUTCDATE();
    SET @Deleted = @@ROWCOUNT;
END
```

---

## Check C — GDPR right-to-erasure (RET-001)

Before deleting a user record, scrub PII columns to a tombstone value rather than relying
on CASCADE DELETE alone. Archive non-PII audit data before scrubbing.

```sql
-- Step 1: archive non-PII audit trail
INSERT INTO dbo.DeletedUserAudit (UserId, TenantId, DeletedAt)
SELECT UserId, TenantId, GETUTCDATE() FROM dbo.Users WHERE UserId = @UserId;

-- Step 2: scrub PII in place
UPDATE dbo.Users
SET Email        = 'deleted@example.invalid',
    DisplayName  = 'Deleted User',
    PhoneNumber  = NULL,
    DateOfBirth  = NULL
WHERE UserId = @UserId AND TenantId = @TenantId;

-- Step 3: mark soft-deleted
UPDATE dbo.Users SET DeletedAt = GETUTCDATE() WHERE UserId = @UserId;
```

---

## Check D — Partition-switch purge for large tables

For tables with >1 M rows per retention cycle, use partition switching instead of batched
deletes to avoid log amplification:

1. Create a staging table with identical schema and constraints.
2. Switch the expired partition out: `ALTER TABLE dbo.Events SWITCH PARTITION @P TO dbo.EventsStaging PARTITION @P`.
3. Truncate or archive the staging table.
4. Requires: partition function aligned to `RetainUntil` date range.

---

## EF Core integration

Map retention columns in the entity configuration:

```csharp
entity.Property(e => e.RetainUntil)
      .HasDefaultValueSql("DATEADD(YEAR, 7, GETUTCDATE())");

// Global query filter excludes hard-expired rows from all queries
modelBuilder.Entity<CustomerNote>()
    .HasQueryFilter(n => n.RetainUntil > DateTime.UtcNow || n.RetainUntil == null);
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.