● SQL Server 2017+ · All Editions Including Express

Undo is a query,
not a restore.

SQL RecycleBin captures every DELETE and UPDATE on your protected tables into a page-compressed JSON store. When someone forgets the WHERE clause, query the lost rows as real typed columns and undo the transaction with one command—no backup restore, no $2,000 log-reader tool, and zero downtime.

EXEC rb.Enable @Table = 'dbo.Orders'; -- then EXEC rb.UndoLast @Table = 'dbo.Orders';
ALTER TABLE Safe Schema-resilient JSON storage
IDENTITY_INSERT Preserves original PK IDs automatically
PolyForm 1.0.0 Free for personal & noncommercial use
dbo.rbDemo_Orders
CAPTURE ENABLED · 4 ROWS INTACT

          
LIVE TABLE STATE 4 rows
INTERNAL STORE: rb.ChangeLog (DATA_COMPRESSION = PAGE) 0 captured
ChangeId Op TxId RowBefore (JSON Snapshot) ChangedBy State
// The SQL Server Recovery Gap

SQL Server never got Oracle’s Flashback Query.

Oracle DBAs have had row-level Flashback Query for two decades. When someone runs an accidental DELETE or UPDATE on a live SQL Server database, your standard options are slow, lossy, or prohibitively expensive.

01 · THE RESTORE PROBLEM

Point-in-time restore is slow and lossy

Rolling an entire database back to 14:02 means losing every legitimate transaction that happened after 14:02—or spending hours restoring a multi-hundred-gigabyte backup to a sidecar server and writing custom IDENTITY_INSERT merge scripts under pressure.

02 · THE TOOLING PROBLEM

Log-reader forensics cost $2,000+ per seat

Commercial transaction-log readers can parse LDF files to reconstruct lost rows, but only if the transaction log hasn't truncated in SIMPLE recovery mode—and at a licensing cost most teams only approve after a painful outage.

03 · THE EDITION & SCHEMA PROBLEM

Express is left out & rigid triggers break

Hand-rolled shadow audit tables break the moment a developer runs ALTER TABLE, while enterprise audit features skip SQL Server Express. SQL RecycleBin stores row snapshots as compressed JSON and projects them dynamically against sys.columns.

Recovery Capability Point-in-Time Backup Restore 3rd-Party Log Forensics Temporal Tables SQL RecycleBin (rb.*)
Mean Time to Recover (1,000 rows) 30–120+ minutes 15–45 minutes Manual T-SQL merge < 5 seconds (1 command)
Preserves Newer Concurrent Transactions No (unless restored side-by-side) Yes Yes Yes (surgical per-TxId undo)
Works in SIMPLE Recovery Mode No (full/diff checkpoints only) Fails once log truncates Yes Yes (persistent table store)
Zero Friction with ALTER TABLE N/A N/A Restricts certain DDL changes Yes (JSON storage + dynamic OPENJSON)
Captures ChangedBy, AppName, HostName No Partial (SID only) No built-in session metadata Yes (built into every ChangeLog row)
Automatic IDENTITY_INSERT Handling Manual scripting Manual script export Manual scripting Automatic inside rb.UndoTransaction
SQL Server Express (2017+) Support Yes Often restricted Yes 100% Supported
// Under the Hood

How SQL RecycleBin Works

Three commands for day-to-day operations. Zero external agents, zero background daemons, and zero schema mirroring—everything runs natively inside T-SQL on SQL Server 2017+.

01

Enable capture, once per table

rb.Enable inspects primary key metadata and attaches a lightweight AFTER UPDATE, DELETE trigger (rb_trg_TableName) to your table.

EXEC rb.Enable @Table = N'dbo.Orders';
02

Automatic JSON snapshot on change

When a DELETE or UPDATE executes, the trigger records CURRENT_TRANSACTION_ID(), primary key JSON, full RowBefore / RowAfter JSON, login, host, and app name.

DELETE dbo.Orders -- forgot WHERE! -- Captured in rb.ChangeLog (PAGE compressed)
03

Query typed columns, then Undo

Inspect lost rows projected back into real SQL data types via OPENJSON, then unwind the last transaction, a specific @TxId, or everything since @Since.

EXEC rb.Deleted @Table = N'dbo.Orders'; EXEC rb.UndoLast @Table = N'dbo.Orders';
INTERNAL EXECUTION PIPELINE (install.sql) Survives restarts, failovers & ALTER TABLE
STAGE 1 · CAPTURE

Trigger & Bypass Check

Checks SESSION_CONTEXT(N'rb_bypass') first. Pairs deleted and inserted pseudo-tables on Primary Key ordinals and serializes rows with FOR JSON PATH, INCLUDE_NULL_VALUES.

STAGE 2 · STORAGE

Page-Compressed Store

Persists to rb.ChangeLog with DATA_COMPRESSION = PAGE and targeted indexes on (SchemaName, TableName, Operation, ChangeId DESC) and (TxId).

STAGE 3 · PROJECTION

rb.internal_GetTableMeta

Queries live sys.columns & sys.types at read time to build a typed OPENJSON(...) WITH (...) clause—excluding computed and rowversion columns automatically.

STAGE 4 · SURGICAL UNDO

Transactional Reversal

Wraps undo in rb.BypassOn, toggles IDENTITY_INSERT ON if needed, skips PK collisions via WHERE NOT EXISTS, reverts updated columns, and marks Restored = 1.

// Real-World Recovery Scenarios

Case Studies: From Incident to Sub-10-Second Undo

See how engineering, DBA, and operations teams use SQL RecycleBin across E-Commerce, Multi-Tenant SaaS on SQL Express, Financial ETL pipelines, and Healthcare databases to turn potential outages into non-events.

CASE STUDY #01 E-Commerce & Order Fulfillment SQL Server 2022 Standard Command: rb.UndoTransaction

The Partial SSMS Selection: Recovering 1,842 Settled Black Friday Orders Without Halting Checkout

1,842 rows Restored with original PKs
3.8 sec Total time to undo
0 lost Subsequent live orders

During peak Black Friday settlement triage, an on-call engineer wrote DELETE dbo.Orders WHERE Status = N'Paid' AND Customer LIKE N'QA_SMOKE_%'; in SSMS to remove three smoke-test rows. When highlighting the query with the mouse, the AND Customer LIKE... line was clipped out of the selection before hitting F5. Instantly, 1,842 paid customer orders vanished while 60+ new orders per minute continued streaming into dbo.Orders.

Taking the 480 GB production database offline for a point-in-time restore would have halted Black Friday checkout for 40+ minutes and wiped out hundreds of newer orders placed seconds after the mistake.

The DBA queried EXEC rb.Deleted @Table = N'dbo.Orders', @Top = 2000;, confirmed all 1,842 rows shared TxId = 9481022 from HostName = 'OPS-JUMPBOX-02', and executed EXEC rb.UndoTransaction @Table = N'dbo.Orders', @TxId = 9481022;. SQL RecycleBin toggled IDENTITY_INSERT dbo.Orders ON, re-inserted all 1,842 rows with their exact original OrderId keys, and left every newer checkout completely untouched.

-- 1. What the engineer highlighted by accident in SSMS:
DELETE dbo.Orders WHERE Status = N'Paid';
--   AND Customer LIKE N'QA_SMOKE_%';  <-- left unselected!
-- (1842 rows affected)

-- 2. Inspect deleted rows & isolate the exact Transaction ID:
EXEC rb.Deleted @Table = N'dbo.Orders', @Top = 5;
-- Returns ChangeId, ChangedAt, ChangedBy='sa_triage',
-- plus typed OrderId, Customer, Amount, Status, CreatedAt

SELECT DISTINCT TxId, ChangedBy, HostName, COUNT(*) AS DeletedRows
FROM rb.ChangeLog
WHERE SchemaName = N'dbo' AND TableName = N'Orders' AND Restored = 0
GROUP BY TxId, ChangedBy, HostName;
-- TxId: 9481022 | ChangedBy: sa_triage | HostName: OPS-JUMPBOX-02 | Rows: 1842

-- 3. Surgically undo ONLY TxId 9481022 while live checkouts continue:
EXEC rb.UndoTransaction @Table = N'dbo.Orders', @TxId = 9481022;
-- rb: restored 1842 deleted row(s), reverted 0 update(s) on [dbo].[Orders].
// Stored in rb.ChangeLog (Page Compressed)
{
  "ChangeId": 41092,
  "SchemaName": "dbo",
  "TableName": "Orders",
  "Operation": "D",
  "TxId": 9481022,
  "KeyValues": "{\"OrderId\":10481}",
  "RowBefore": "{\"OrderId\":10481,\"Customer\":\"Nordic Logistics AB\",\"Amount\":18400.00,\"Status\":\"Paid\",\"CreatedAt\":\"2026-11-27T14:02:11\"}",
  "RowAfter": null,
  "ChangedBy": "sa_triage",
  "AppName": "Microsoft SQL Server Management Studio - Query",
  "HostName": "OPS-JUMPBOX-02",
  "ChangedAt": "2026-11-27T14:04:02.118Z",
  "Restored": 1
}
-- Timeline Comparison:
14:04:02.118  Accidental DELETE removes 1,842 rows (OrderId 8600..10484)
14:04:03.000  Live customers place 14 new orders (OrderId 10485..10498)
14:04:05.910  EXEC rb.UndoTransaction @Table='dbo.Orders', @TxId=9481022

✓ Original OrderIds 8600..10484 re-inserted via automatic IDENTITY_INSERT ON
✓ Newer OrderIds 10485..10498 preserved without interruption
✓ PK collision guard (WHERE NOT EXISTS) prevented any duplicate key errors
CASE STUDY #02 B2B Multi-Tenant SaaS SQL Server 2019 Express Command: rb.Updated + rb.Restore

Surviving Both an ALTER TABLE Migration and a Mass-Zeroing Billing Update on SQL Server Express

4,120 tenants Pricing tiers reverted
11.2 sec Point-in-time table rollback
SQL Express No Enterprise license needed

A fast-growing B2B SaaS platform running on SQL Server 2019 Express deployed a release that first ran ALTER TABLE dbo.Subscriptions ADD GracePeriodDays INT NULL; and then executed a post-deploy cleanup script intended to zero out expired sandbox trials. Due to a faulty predicate in the migration runner, the script executed UPDATE dbo.Subscriptions SET MonthlyRate = 0, BillingTier = N'Free'; across all 4,120 active paying tenants.

SQL Server Express lacks Enterprise audit suites, and traditional column-mapped audit triggers fail or miss columns when ALTER TABLE modifies the schema immediately prior to an incident.

Because SQL RecycleBin serializes rows with FOR JSON PATH and inspects live sys.columns metadata dynamically via rb.internal_GetTableMeta, the preceding ALTER TABLE didn't break capture or recovery. The team verified the before/after JSON in rb.Updated and ran EXEC rb.Restore @Table = N'dbo.Subscriptions', @Since = '2026-09-14T14:20:00'; to revert every tenant's exact custom pricing tier.

-- 1. Release script added a column, then ran a buggy mass UPDATE:
ALTER TABLE dbo.Subscriptions ADD GracePeriodDays INT NULL;
UPDATE dbo.Subscriptions SET MonthlyRate = 0, BillingTier = N'Free';
-- (4120 rows affected)

-- 2. Inspect the before and after values captured during the update:
EXEC rb.Updated
    @Table = N'dbo.Subscriptions',
    @Top   = 20,
    @Since = '2026-09-14T14:20:00';

-- 3. Roll the table's captured changes back to before the deployment:
EXEC rb.Restore
    @Table = N'dbo.Subscriptions',
    @Since = '2026-09-14T14:20:00';
-- rb: restored 0 deleted row(s), reverted 4120 update(s) on [dbo].[Subscriptions].
// Output from EXEC rb.Updated @Table = N'dbo.Subscriptions'
KeyValues : {"SubId": 501}
ChangedBy : "deploy_runner"
AppName   : "DbUp-Migration-Cli"

RowBefore : {
  "SubId": 501,
  "TenantName": "Aether BioLabs",
  "MonthlyRate": 1499.00,
  "BillingTier": "Enterprise",
  "GracePeriodDays": null
}

RowAfter  : {
  "SubId": 501,
  "TenantName": "Aether BioLabs",
  "MonthlyRate": 0.00,
  "BillingTier": "Free",
  "GracePeriodDays": null
}
-- How rb.internal_GetTableMeta handles ALTER TABLE automatically:
1. Trigger uses `SELECT d.* FOR JSON PATH, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES`
   so newly added columns are automatically serialized without recreating triggers.
2. During `rb.UndoTransaction`, `rb.internal_GetTableMeta` queries `sys.columns`
   dynamically and generates:
   UPDATE t SET t.[TenantName] = src.[TenantName],
                t.[MonthlyRate] = src.[MonthlyRate],
                t.[BillingTier] = src.[BillingTier],
                t.[GracePeriodDays] = src.[GracePeriodDays]
   FROM [dbo].[Subscriptions] AS t
   JOIN OPENJSON(@rows) WITH (...) AS src ON t.[SubId] = src.[SubId];
CASE STUDY #03 Wealth Management & Financial Ledger PowerShell Module: SqlRecycleBin Cmdlets: Invoke-SqlRecycleBinBypass + Restore-SqlRecycleBinPointInTime

Bypassing 2.4M Nightly ETL Archival Rows While Reversing a Multi-Step Portfolio Rebalance Error

0% overhead During nightly ETL via Bypass
2 stacked TXs Unwound newest-first
42.8 MB 14-day PAGE-compressed store

A wealth management firm needed protection on dbo.ClientAllocations during business hours, where analysts run ad-hoc portfolio adjustments, but also ran a nightly PowerShell ETL pipeline that purged and reloaded 2.4 million historical staging rows. One afternoon, an analyst ran a bad DELETE followed immediately by a bad UPDATE in an attempt to "fix" the first mistake—compounding the damage across two separate transactions.

Calling rb.BypassOn in one Invoke-Sqlcmd call and the ETL script in a second call fails because Invoke-Sqlcmd opens a new SQL connection per invocation, losing SESSION_CONTEXT(N'rb_bypass').

Using the official SqlRecycleBin PowerShell module, the team wrapped their nightly ETL in Invoke-SqlRecycleBinBypass (which batches rb.BypassOn, the workload, and rb.BypassOff on a single connection). When the afternoon double-accident occurred, the ops engineer ran Restore-SqlRecycleBinPointInTime, which automatically iterated captured transactions in reverse chronological order (ORDER BY LastChange DESC)—first reverting the bad UPDATE, then re-inserting the deleted allocations.

# 1. Inspect deleted and updated rows directly as PowerShell objects:
Get-SqlRecycleBinDeletedRow `
    -ServerInstance 'SQL-PROD-01' `
    -Database 'WealthLedger' `
    -Table 'dbo.ClientAllocations' -Top 50

Get-SqlRecycleBinUpdatedRow `
    -ServerInstance 'SQL-PROD-01' `
    -Database 'WealthLedger' `
    -Table 'dbo.ClientAllocations' -Top 50

# 2. Unwind both the bad UPDATE and bad DELETE (newest transaction first):
Restore-SqlRecycleBinPointInTime `
    -ServerInstance 'SQL-PROD-01' `
    -Database 'WealthLedger' `
    -Table 'dbo.ClientAllocations' `
    -Since ([datetime]'2026-10-12T16:15:00Z')
# Nightly Scheduled Job: Skip trigger capture for 2.4M-row maintenance
# Wraps rb.BypassOn + Script + rb.BypassOff inside ONE connection batch:
Invoke-SqlRecycleBinBypass `
    -ServerInstance 'SQL-PROD-01' `
    -Database 'WealthLedger' `
    -InputFile 'C:\Jobs\NightlyPortfolioRollup.sql'

# Purge captured history older than 14 days in 50,000-row batches:
Clear-SqlRecycleBinHistory `
    -ServerInstance 'SQL-PROD-01' `
    -Database 'WealthLedger' `
    -KeepDays 14 -Confirm:$false
PS> Get-SqlRecycleBinStatus -ServerInstance 'SQL-PROD-01' -Database 'WealthLedger'

SchemaName TableName          EnabledAt           Captured PendingRestore StoreSizeMB
---------- ---------          ---------           -------- -------------- -----------
dbo        ClientAllocations  2026-08-01 09:00:00    14820              0        42.8
dbo        RebalanceOrders    2026-08-01 09:00:00     6310              0        42.8
CASE STUDY #04 Healthcare Clinic Scheduling SQL Server 2019 Standard Forensics + Parent/Child FK Recovery

Pinpointing a Rogue Desktop Client & Restoring Parent-Child Tables Across Foreign Keys

< 30 sec Time to identify workstation
310 + 485 Parent & child rows restored
0 FK errors Parent-first restore workflow

At 08:45 AM, front-desk staff at a regional multi-specialty clinic noticed 310 upcoming patient appointments (dbo.Appointments) and 485 linked clinical intake notes (dbo.AppointmentNotes) had disappeared. Because six different internal tools and desktop applications connected to the database, the IT team had no idea what triggered the deletion or whether it was still happening.

Restoring child rows in dbo.AppointmentNotes before restoring parent rows in dbo.Appointments fails due to FK_AppointmentNotes_Appointments—and parent rows must retain their exact original ApptId identity values for the child foreign keys to match.

Running EXEC rb.Deleted @Table = N'dbo.Appointments' immediately revealed AppName = 'ClinicScheduler-WinForms-v1.8' and HostName = 'RECEPTION-PC-07', where a stuck macro key had purged the day's queue. The DBA isolated that workstation, then ran EXEC rb.UndoLast @Table = N'dbo.Appointments' first (which restored all 310 parent rows with original ApptId identity keys), followed immediately by EXEC rb.UndoLast @Table = N'dbo.AppointmentNotes'.

-- 1. Identify WHO, WHICH APP, and WHICH WORKSTATION deleted the rows:
SELECT TOP (5)
    ChangeId, TxId, ChangedAt, ChangedBy, AppName, HostName, KeyValues
FROM rb.ChangeLog
WHERE TableName = N'Appointments' AND Operation = 'D' AND Restored = 0
ORDER BY ChangeId DESC;

-- 2. Restore PARENT table first (automatically preserves ApptId IDENTITY PKs):
EXEC rb.UndoLast @Table = N'dbo.Appointments';
-- rb: restored 310 deleted row(s), reverted 0 update(s) on [dbo].[Appointments].

-- 3. Restore CHILD table second (FK_AppointmentNotes_Appointments succeeds):
EXEC rb.UndoLast @Table = N'dbo.AppointmentNotes';
-- rb: restored 485 deleted row(s), reverted 0 update(s) on [dbo].[AppointmentNotes].
// Built-in Forensic Columns Captured on Every Trigger Execution:
ChangedBy : "CLINIC\svc_desk"          -- via ORIGINAL_LOGIN()
AppName   : "ClinicScheduler-WinForms-v1.8" -- via APP_NAME()
HostName  : "RECEPTION-PC-07"          -- via HOST_NAME()
ChangedAt : "2026-10-29 08:44:51.902"  -- via SYSUTCDATETIME()
TxId      : 11804921                   -- via CURRENT_TRANSACTION_ID()

// Note: If both Parent and Child were deleted inside the same explicit
// transaction or cascade, they share the same CURRENT_TRANSACTION_ID()!
-- Why Parent-Before-Child Restore Works Cleanly:
1. `rb.UndoTransaction` detects `is_identity = 1` on `dbo.Appointments.ApptId`.
2. It dynamically wraps the `INSERT` in:
   `SET IDENTITY_INSERT [dbo].[Appointments] ON; ... SET IDENTITY_INSERT [dbo].[Appointments] OFF;`
3. Because all 310 parent rows come back with their exact original `ApptId` values
   (e.g. 301..610), restoring `dbo.AppointmentNotes` immediately afterward satisfies
   every foreign key reference without disabling or dropping constraints.
// Complete Command Reference

T-SQL Stored Procedures & PowerShell Module

Use the rb.* stored procedures directly in SSMS / Azure Data Studio, or install the official SqlRecycleBin module from the PowerShell Gallery for pipeline automation.

Schema: rb.* · Default Retention: 14 days (rb.Config)
Stored Procedure Parameters What It Does
EXEC rb.Enable @Table SYSNAME Starts capturing DELETE and UPDATE on a table (e.g., 'dbo.Orders'). Requires a Primary Key. Generates trigger rb_trg_<Table>.
EXEC rb.Disable @Table SYSNAME Drops the capture trigger and removes the table from rb.EnabledTables (existing history in rb.ChangeLog is retained).
EXEC rb.Deleted @Table SYSNAME, @Top INT = 10, @Since DATETIME2(3) = NULL Projects recently deleted rows (`Restored = 0`) from JSON back into real typed columns matching the table's current schema.
EXEC rb.Updated @Table SYSNAME, @Top INT = 10, @Since DATETIME2(3) = NULL Returns recent updates (`Restored = 0`) with KeyValues, RowBefore JSON, and RowAfter JSON side-by-side.
EXEC rb.UndoLast @Table SYSNAME Finds the most recent un-restored TxId on the table and calls rb.UndoTransaction to reverse it.
EXEC rb.UndoTransaction @Table SYSNAME, @TxId BIGINT Re-inserts deleted rows (handling IDENTITY_INSERT and skipping PK collisions) and reverts updated rows for a specific @TxId.
EXEC rb.Restore @Table SYSNAME, @Since DATETIME2(3) Rolls the table's captured changes back to @Since by undoing every captured transaction newest-first (ORDER BY LastChange DESC).
EXEC rb.BypassOn / rb.BypassOff None Sets SESSION_CONTEXT(N'rb_bypass') to 1 or 0 so ETL and bulk maintenance jobs in the current session skip capture.
EXEC rb.Cleanup @KeepDays INT = NULL Purges history older than @KeepDays (defaults to 14 from rb.Config) in 50,000-row batches to avoid log growth.
EXEC rb.Status None Returns enabled tables, total captured change counts, pending restore counts, and rb.ChangeLog store size in MB.
PowerShell Cmdlet Wraps What It Does
Install-SqlRecycleBin install.sql Installs the rb schema, tables, and stored procedures on a target database. Idempotent and safe to re-run.
Uninstall-SqlRecycleBin uninstall.sql Removes SQL RecycleBin and drops all rb objects. Permanently deletes captured history (supports -Force).
Enable-SqlRecycleBinCapture rb.Enable Starts capturing DELETE and UPDATE on a table (requires a primary key).
Disable-SqlRecycleBinCapture rb.Disable Stops capturing on a table.
Get-SqlRecycleBinDeletedRow rb.Deleted Returns recently deleted rows as typed PowerShell objects into the pipeline.
Get-SqlRecycleBinUpdatedRow rb.Updated Returns recent updates with before/after JSON payloads.
Restore-SqlRecycleBinLast rb.UndoLast Undoes the most recent captured transaction on a table.
Restore-SqlRecycleBinTransaction rb.UndoTransaction Undoes a specific transaction by -TxId.
Restore-SqlRecycleBinPointInTime rb.Restore Undoes every captured change on a table since a given -Since timestamp.
Invoke-SqlRecycleBinBypass rb.BypassOn / Off Runs a -Query or -InputFile script with capture bypassed inside a single shared connection batch.
Clear-SqlRecycleBinHistory rb.Cleanup Purges history beyond a retention window (-KeepDays).
Get-SqlRecycleBinStatus rb.Status Returns enabled tables, capture counts, and change-log store size in MB.
Invoke-SqlRecycleBinDemo demo.sql Runs the full accident-to-recovery smoke test against a scratch database.

Architectural Note: Why Invoke-SqlRecycleBinBypass Exists as Its Own Cmdlet

rb.BypassOn and rb.BypassOff toggle a SESSION_CONTEXT(N'rb_bypass') flag scoped to a single SQL connection. Because Invoke-Sqlcmd opens a fresh connection per call, running a separate bypass cmdlet before your bulk job would not share a session. Invoke-SqlRecycleBinBypass wraps EXEC rb.BypassOn;, your SQL batch, and EXEC rb.BypassOff; into a single connection so bulk ETL bypass is guaranteed to work.

// Engineering Transparency

Honest Limitations (v1)

SQL RecycleBin is a fast-recovery safety net, not a replacement for database backups. We want you to know the exact technical boundaries before an incident—not during one.

⚠️ TRUNCATE, DROP & Minimally-Logged Bulk Ops

TRUNCATE TABLE, DROP TABLE, and minimally-logged BULK INSERT operations without FIRE_TRIGGERS do not fire DML triggers and are not captured in v1. Keep your regular backups intact.

🔑 Primary Key Required

Protected tables must have a primary key. Primary keys are how AFTER UPDATE pairs deleted and inserted rows and how rb.UndoTransaction targets specific rows on restore.

🔄 Updating Primary Key Values

If an UPDATE statement mutates the primary key column itself, before/after pairing on the PK is broken for that row (though the full RowBefore JSON image is still captured in rb.ChangeLog).

⚡ Trigger Write Overhead & LOB Sizing

Trigger capture adds synchronous write work. Measure on your workload, exclude ultra-hot telemetry tables, or wrap bulk jobs in rb.BypassOn. Large NVARCHAR(MAX) / VARBINARY(MAX) columns increase store size—tune rb.Cleanup @KeepDays accordingly.

// Fair-Code Licensing & Roadmap

Pricing & Commercial Licensing

The complete product is source-available and free for personal, educational, academic, nonprofit, and noncommercial use under the PolyForm Noncommercial License 1.0.0. For-profit companies running it in production require a commercial license.

Free — Noncommercial
$0
PolyForm Noncommercial License 1.0.0
  • ✓ Complete rb.* T-SQL capture & surgical undo engine
  • ✓ Official SqlRecycleBin PowerShell module (13 cmdlets)
  • ✓ All SQL Server 2017+ editions, including SQL Server Express
  • ✓ Personal, educational, research, nonprofit & government use
  • – Not licensed for commercial / for-profit company use
Download on GitHub →
COMMERCIAL & PRO ROADMAP
Pro — Commercial License
Contact Us
Required for any for-profit / commercial production use
  • ✓ Commercial Production License for your organization
  • ✓ CDC-Based Capture Engine (Roadmap): Zero trigger overhead on the write path
  • ✓ Blazor Web Dashboard (Roadmap): Visual JSON diff viewer & one-click restore with preview
  • ✓ App-Level Identity Forensics: SESSION_CONTEXT tracking behind shared connection pools
  • ✓ Enterprise Controls: Multi-DB monitoring, mass-delete alerts, RBAC on restore & TRUNCATE protection
Contact qaiser@92logics.com →