Column Naming Conventionsο
Danger
π¨ CRITICAL: This is the #1 cause of errors when using MatrixOne with SQLAlchemy!
Always use lowercase with underscores (snake_case) for column names!
The Problemο
MatrixOne does not support SQL standard double-quoted identifiers in queries. This creates compatibility issues with SQLAlchemy ORM when using camelCase or PascalCase column names.
What Happens with CamelCaseο
When you define columns with mixed case names:
from matrixone.orm import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
userId = Column(Integer, primary_key=True) # β CamelCase
userName = Column(String(50)) # β CamelCase
emailAddress = Column(String(100)) # β CamelCase
Step 1: CREATE TABLE β Works
SQLAlchemy generates (using MySQL backticks):
CREATE TABLE users (
`userId` INTEGER NOT NULL AUTO_INCREMENT,
`userName` VARCHAR(50),
`emailAddress` VARCHAR(100),
PRIMARY KEY (`userId`)
)
β This works! MatrixOne supports backticks.
Step 2: INSERT β Works
INSERT INTO users (userId, userName, emailAddress)
VALUES (1, 'Alice', 'alice@example.com')
β This works! No quotes needed for INSERT.
Step 3: SELECT β FAILS!
SQLAlchemy generates (using SQL standard double quotes):
SELECT users."userId" AS userId,
users."userName" AS userName,
users."emailAddress" AS emailAddress
FROM users
β SQL Syntax Error! MatrixOne doesnβt support double quotes for identifiers.
Error message:
(pymysql.err.ProgrammingError) (1064, 'SQL parser error: You have an error in your SQL
syntax; check the manual that corresponds to your MatrixOne server version for the right
syntax to use. syntax error at line 1 column XX near ""userId"...')
The Solution: snake_caseο
Use lowercase with underscores for all column names:
class User(Base):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True) # β
snake_case
user_name = Column(String(50)) # β
snake_case
email_address = Column(String(100)) # β
snake_case
created_at = Column(DateTime) # β
snake_case
is_active = Column(Boolean) # β
snake_case
All operations work perfectly:
-- CREATE TABLE (no quotes needed)
CREATE TABLE users (
user_id INTEGER NOT NULL AUTO_INCREMENT,
user_name VARCHAR(50),
email_address VARCHAR(100),
created_at DATETIME,
is_active BOOLEAN,
PRIMARY KEY (user_id)
)
-- INSERT (no quotes needed)
INSERT INTO users (user_id, user_name, email_address)
VALUES (1, 'Alice', 'alice@example.com')
-- SELECT (no quotes needed)
SELECT users.user_id AS user_id,
users.user_name AS user_name,
users.email_address AS email_address
FROM users
β All queries succeed!
Complete Exampleο
Hereβs a complete working example following best practices:
from matrixone import Client
from matrixone.orm import declarative_base
from sqlalchemy import Column, Integer, String, DECIMAL, DateTime, Boolean
from sqlalchemy.dialects.mysql import JSON
from datetime import datetime
Base = declarative_base()
# β
Perfect: All column names use snake_case
class Product(Base):
__tablename__ = 'products'
# Primary key
product_id = Column(Integer, primary_key=True, autoincrement=True)
# Basic info
product_name = Column(String(200), nullable=False)
product_code = Column(String(50), unique=True)
# Pricing
unit_price = Column(DECIMAL(10, 2))
sale_price = Column(DECIMAL(10, 2))
# Categorization
category_name = Column(String(100))
subcategory_name = Column(String(100))
# Status
is_active = Column(Boolean, default=True)
is_featured = Column(Boolean, default=False)
# Metadata (JSON field)
product_metadata = Column(JSON)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, onupdate=datetime.utcnow)
# Connect and create table
client = Client()
client.connect(database='demo')
client.create_table(Product)
# Insert data - direct dict with JSON auto-serialization
client.insert(Product, {
'product_name': 'Laptop',
'product_code': 'LAP-001',
'unit_price': 999.99,
'category_name': 'Electronics',
'is_active': True,
'product_metadata': {'brand': 'Dell', 'warranty': '2 years'} # β
Dict auto-serializes
})
# Query data - all operations work perfectly
results = client.query(
Product.product_id,
Product.product_name,
Product.unit_price,
Product.product_metadata
).filter(Product.is_active == True).execute()
for row in results.rows:
print(f"{row[0]}: {row[1]} - ${row[2]}")
Naming Rules Summaryο
Style |
Status |
Examples |
|---|---|---|
snake_case (lowercase + underscores) |
β Use this! |
|
camelCase |
β Donβt use |
|
PascalCase |
β Donβt use |
|
lowercase (no separator) |
β οΈ OK but not recommended |
|
UPPERCASE |
β Donβt use |
|
Additional Naming Tipsο
Table Names: Also use snake_case for consistency
__tablename__ = 'user_accounts' # β Good __tablename__ = 'UserAccounts' # β Avoid __tablename__ = 'userAccounts' # β Avoid
Avoid SQL Reserved Words
Even with snake_case, avoid these problematic names:
select,from,where,order,groupuser,table,column,databasekey,index,create,drop
If you must use them, add a prefix/suffix:
# β Problematic order = Column(Integer) user = Column(String(50)) # β Better order_id = Column(Integer) user_name = Column(String(50))
Boolean Columns: Use
is_prefixis_active = Column(Boolean) # β Clear intent is_deleted = Column(Boolean) # β Clear intent active = Column(Boolean) # β οΈ Less clear
Timestamp Columns: Use
_ator_datesuffixcreated_at = Column(DateTime) # β Standard convention updated_at = Column(DateTime) # β Standard convention deleted_at = Column(DateTime) # β Soft delete pattern birth_date = Column(Date) # β Clear for dates
Foreign Key Columns: Use
_idsuffixuser_id = Column(Integer, ForeignKey('users.id')) # β Clear FK category_id = Column(Integer, ForeignKey('categories.id')) # β Clear FK parent_id = Column(Integer, ForeignKey('products.id')) # β Self-reference
Technical Backgroundο
Why does MatrixOne have this limitation?
MatrixOne is based on MySQL, which traditionally uses:
Backticks
`identifier`for quoted identifiers (MySQL-specific)Double quotes
"identifier"only in ANSI_QUOTES mode (disabled by default)
MatrixOne currently:
β Supports backticks for DDL (CREATE TABLE)
β Does not support double quotes for DML/queries (SELECT, UPDATE, DELETE)
SQLAlchemy behavior:
SQLAlchemyβs MySQL dialect uses:
Backticks for CREATE TABLE (compatible with MatrixOne)
Double quotes for SELECT/DML when preserving case (incompatible with MatrixOne)
Solution: Use snake_case to avoid any quoting entirely!
Comparison with Other Databasesο
Database |
Backticks ` |
Double Quotes β |
Recommended Style |
|---|---|---|---|
PostgreSQL |
β Not supported |
β Standard (SQL-92) |
snake_case |
MySQL |
β Standard |
β οΈ Needs ANSI_QUOTES |
snake_case |
MatrixOne |
β DDL only |
β Not supported |
snake_case only! |
SQLite |
β Supported |
β Supported |
snake_case (best practice) |
See Alsoο
ORM Usage Guide - Complete ORM usage guide
Quick Start - Quick start with correct naming
Examples - All examples use snake_case