217 lines
5.5 KiB
Markdown
217 lines
5.5 KiB
Markdown
# MySQL Rules
|
|
|
|
Guidelines for designing schemas specific to MySQL / MariaDB.
|
|
|
|
---
|
|
|
|
## Data Types
|
|
|
|
| Use case | Type | Notes |
|
|
|----------|------|-------|
|
|
| Primary key | `BIGINT UNSIGNED AUTO_INCREMENT` | For large tables |
|
|
| Primary key (small) | `INT UNSIGNED AUTO_INCREMENT` | Tables < 2 billion rows |
|
|
| Foreign key | Match with PK type | |
|
|
| Money/Price | `DECIMAL(18,2)` | **Do NOT use FLOAT/DOUBLE** |
|
|
| Quantity | `INT` or `DECIMAL(18,4)` | |
|
|
| Boolean | `TINYINT(1)` or `BOOLEAN` | |
|
|
| Short text | `VARCHAR(n)` | n ≤ 255 for indexing |
|
|
| Long text | `TEXT` | Cannot index directly |
|
|
| JSON | `JSON` | MySQL 5.7+ |
|
|
| Timestamp | `DATETIME` or `TIMESTAMP` | |
|
|
| Date | `DATE` | |
|
|
| UUID | `CHAR(36)` or `BINARY(16)` | |
|
|
|
|
### TIMESTAMP vs DATETIME
|
|
- `TIMESTAMP`: Auto-converts timezone, range 1970-2038
|
|
- `DATETIME`: No timezone conversion, range 1000-9999
|
|
|
|
---
|
|
|
|
## Comments (NO metadata tables needed)
|
|
|
|
MySQL supports comments directly in DDL:
|
|
|
|
### Table comment
|
|
```sql
|
|
CREATE TABLE orders (
|
|
...
|
|
) COMMENT='Table storing customer orders';
|
|
```
|
|
|
|
### Column comment
|
|
```sql
|
|
CREATE TABLE orders (
|
|
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT
|
|
COMMENT 'Primary key, auto increment',
|
|
order_number VARCHAR(50) NOT NULL
|
|
COMMENT 'Unique order code, format: ORD-YYYYMMDD-XXXXX',
|
|
status VARCHAR(32) NOT NULL DEFAULT 'pending'
|
|
COMMENT 'Status: pending|confirmed|shipped|cancelled',
|
|
total_amount DECIMAL(18,2) NOT NULL
|
|
COMMENT 'Total value including tax'
|
|
);
|
|
```
|
|
|
|
### Query comments
|
|
```sql
|
|
-- Table comment
|
|
SELECT TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES
|
|
WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'orders';
|
|
|
|
-- Column comments
|
|
SELECT COLUMN_NAME, COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS
|
|
WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'orders';
|
|
```
|
|
|
|
---
|
|
|
|
## Index Types
|
|
|
|
### B-Tree (default)
|
|
```sql
|
|
CREATE INDEX idx_orders_status ON orders(status);
|
|
CREATE INDEX idx_orders_user_status_created ON orders(user_id, status, created_at DESC);
|
|
CREATE UNIQUE INDEX idx_orders_number ON orders(order_number);
|
|
```
|
|
|
|
### FULLTEXT (text search)
|
|
```sql
|
|
CREATE FULLTEXT INDEX idx_products_search ON products(name, description);
|
|
|
|
-- Query
|
|
SELECT * FROM products
|
|
WHERE MATCH(name, description) AGAINST('gaming laptop' IN NATURAL LANGUAGE MODE);
|
|
```
|
|
|
|
### Notes
|
|
- **Partial index**: Not natively supported in MySQL
|
|
- Index prefix for TEXT: `CREATE INDEX idx ON table(col(255))`
|
|
|
|
---
|
|
|
|
## Auto Increment
|
|
|
|
```sql
|
|
CREATE TABLE users (
|
|
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT
|
|
);
|
|
|
|
-- Set starting value
|
|
ALTER TABLE users AUTO_INCREMENT = 1000;
|
|
|
|
-- Get last inserted ID
|
|
SELECT LAST_INSERT_ID();
|
|
```
|
|
|
|
---
|
|
|
|
## Auto update timestamp
|
|
|
|
```sql
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
```
|
|
|
|
---
|
|
|
|
## ENUM vs VARCHAR
|
|
|
|
### Use ENUM when values are fixed and rarely change
|
|
```sql
|
|
status ENUM('pending', 'confirmed', 'shipped', 'cancelled') NOT NULL DEFAULT 'pending'
|
|
```
|
|
|
|
### Use VARCHAR + CHECK when flexibility needed
|
|
```sql
|
|
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
|
CONSTRAINT chk_orders_status CHECK (status IN ('pending', 'confirmed', 'shipped'))
|
|
```
|
|
|
|
---
|
|
|
|
## Foreign Keys
|
|
|
|
```sql
|
|
CREATE TABLE order_items (
|
|
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
|
order_id BIGINT UNSIGNED NOT NULL,
|
|
product_id BIGINT UNSIGNED NOT NULL,
|
|
|
|
CONSTRAINT fk_orderitems_order
|
|
FOREIGN KEY (order_id) REFERENCES orders(id)
|
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
|
|
|
CONSTRAINT fk_orderitems_product
|
|
FOREIGN KEY (product_id) REFERENCES products(id)
|
|
ON DELETE RESTRICT
|
|
);
|
|
|
|
-- IMPORTANT: Index for FK
|
|
CREATE INDEX idx_orderitems_order ON order_items(order_id);
|
|
CREATE INDEX idx_orderitems_product ON order_items(product_id);
|
|
```
|
|
|
|
**Note:** FK only works with **InnoDB** engine.
|
|
|
|
---
|
|
|
|
## Partitioning
|
|
|
|
### Range partitioning (by date)
|
|
```sql
|
|
CREATE TABLE fact_orders (
|
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
order_date DATE NOT NULL,
|
|
...
|
|
PRIMARY KEY (id, order_date)
|
|
) PARTITION BY RANGE (YEAR(order_date)) (
|
|
PARTITION p2023 VALUES LESS THAN (2024),
|
|
PARTITION p2024 VALUES LESS THAN (2025),
|
|
PARTITION pmax VALUES LESS THAN MAXVALUE
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Example DDL
|
|
|
|
```sql
|
|
CREATE TABLE orders (
|
|
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT
|
|
COMMENT 'Primary key',
|
|
order_number VARCHAR(50) NOT NULL
|
|
COMMENT 'Unique order code',
|
|
user_id BIGINT UNSIGNED NOT NULL
|
|
COMMENT 'FK to users.id',
|
|
status ENUM('pending', 'confirmed', 'shipped', 'cancelled', 'completed')
|
|
NOT NULL DEFAULT 'pending'
|
|
COMMENT 'Order status',
|
|
subtotal DECIMAL(18,2) NOT NULL DEFAULT 0,
|
|
discount_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
|
|
total_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
UNIQUE KEY uk_orders_number (order_number),
|
|
KEY idx_orders_user (user_id),
|
|
KEY idx_orders_status (status),
|
|
KEY idx_orders_user_status_created (user_id, status, created_at DESC),
|
|
|
|
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT
|
|
) ENGINE=InnoDB
|
|
DEFAULT CHARSET=utf8mb4
|
|
COLLATE=utf8mb4_unicode_ci
|
|
COMMENT='Table storing order information';
|
|
```
|
|
|
|
---
|
|
|
|
## Checklist
|
|
|
|
- [ ] Engine: `InnoDB` (required for FK)
|
|
- [ ] Charset: `utf8mb4` + `utf8mb4_unicode_ci`
|
|
- [ ] Comments for tables and columns
|
|
- [ ] All FKs have indexes
|
|
- [ ] Use `DECIMAL` for money, not `FLOAT`
|
|
- [ ] `ON UPDATE CURRENT_TIMESTAMP` for `updated_at`
|