schema_migration.sql in SQL
The table rebuild: the only way to add a constraint SQLite cannot ALTER in.
-- Adding a NOT NULL column and a CHECK to a live table. SQLite cannot
-- ALTER either one in, so the table gets rebuilt around the data.
PRAGMA foreign_keys = OFF;
BEGIN;
CREATE TABLE payments_new (
id INTEGER PRIMARY KEY,
order_id INTEGER REFERENCES orders(id) ON DELETE SET NULL,
amount REAL NOT NULL CHECK (amount > 0),
currency TEXT NOT NULL CHECK (LENGTH(currency) = 3),
method TEXT NOT NULL DEFAULT 'card'
CHECK (method IN ('card', 'transfer', 'credit')),
paid_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO payments_new (id, order_id, amount, currency, method, paid_at)
SELECT
id,
order_id,
amount,
UPPER(COALESCE(NULLIF(TRIM(currency), ''), 'USD')),
CASE
WHEN method IN ('card', 'transfer', 'credit') THEN method
ELSE 'card'
END,
COALESCE(paid_at, CURRENT_TIMESTAMP)
FROM payments
WHERE amount > 0;
DROP TABLE payments;
ALTER TABLE payments_new RENAME TO payments;
CREATE INDEX payments_by_order
ON payments (order_id);
CREATE INDEX payments_by_day
ON payments (paid_at);
PRAGMA foreign_key_check;
COMMIT;
PRAGMA foreign_keys = ON;
SELECT
method,
currency,
COUNT(*) AS payments,
ROUND(SUM(amount), 2) AS collected
FROM payments
GROUP BY method, currency
ORDER BY collected DESC;
How it works
- Foreign keys go off outside the transaction, then back on at the end.
- Build the new table, copy the rows, drop the old one, rename into place.
foreign_key_checkbeforeCOMMITis what makes this safe to run.
Keywords and builtins used here
ALTERASBEGINBYCASECHECKCOALESCECOMMITCOUNTCREATECURRENT_TIMESTAMPDEFAULTDELETEDESCDROPELSEENDFROMGROUPININDEXINSERTINTEGERINTOKEYLENGTHNOTNULLNULLIFOFFONORDERPRIMARYREALREFERENCESRENAMESELECTSETSUMTABLETEXTTHENTOTRIMUPPERWHENWHEREmethod
The run, in numbers
- Lines
- 55
- Characters to type
- 1210
- Tokens
- 227
- Three-star pace
- 100 tpm
At the three-star pace of 100 tokens a minute, this run takes about 136 seconds.
Step 1 of 2 in Encore, step 15 of 16 in Schema & constraints.