Comparing CREATE TABLE Syntax: MySQL vs. SQL Server

A comprehensive technical analysis of table creation syntax, data types, constraints, and advanced features between two leading database management systems

Understanding CREATE TABLE Fundamentals

The CREATE TABLE statement is a foundational SQL command that defines the structure of a database table, including column definitions, data types, constraints, indexes, and storage parameters. While the core purpose remains consistent across database systems, MySQL and SQL Server implement this command with significant syntactical and functional differences.

These differences reflect each system's architectural design, optimization strategies, and historical development. Understanding these nuances is essential for database administrators, developers working in multi-database environments, and organizations planning database migrations. For comprehensive documentation on SQL CREATE TABLE syntax across all major database systems, the SQL CREATE TABLE reference guide provides detailed specifications and examples.

Critical Differences Overview

  • Syntax Structure: Fundamental differences in statement organization, schema references, and option placement
  • Data Type Systems: Variations in native data types, size limitations, and precision handling
  • Auto-Increment Implementation: AUTO_INCREMENT vs. IDENTITY with different configuration options
  • Constraint Definition: Different approaches to naming, creating, and managing table and column constraints
  • Storage Configuration: System-specific options for physical storage, partitioning, and performance optimization
  • Default Values: Different syntax and capabilities for specifying default values and expressions

MySQL CREATE TABLE Syntax

MySQL's CREATE TABLE implementation is characterized by its flexibility with storage engines, character sets, and collations. The syntax allows for detailed configuration of how data is stored, indexed, and managed at both the table and column levels.

CREATE TABLE [IF NOT EXISTS] [database_name.]table_name (
    column_name1 data_type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] [UNIQUE [KEY]] [PRIMARY KEY] [COMMENT 'string'],
    column_name2 data_type [column_attributes],
    ...,
    [CONSTRAINT [constraint_name]] PRIMARY KEY (column_name, ...),
    [CONSTRAINT [constraint_name]] UNIQUE [INDEX|KEY] [index_name] (column_name, ...),
    [CONSTRAINT [constraint_name]] FOREIGN KEY [index_name] (column_name, ...) REFERENCES table_name (column_name, ...) [ON DELETE reference_option] [ON UPDATE reference_option],
    [CONSTRAINT [constraint_name]] CHECK (expr)
) [table_options];

[table_options]:
    [ENGINE = engine_name]
    [AUTO_INCREMENT = value]
    [AVG_ROW_LENGTH = value]
    [CHARACTER SET = charset_name]
    [COLLATE = collation_name]
    [COMMENT = 'string']
    [COMPRESSION = {'ZLIB'|'LZ4'|'NONE'}]
    [CONNECTION = 'connect_string']
    [DATA DIRECTORY = 'absolute path to directory']
    [INDEX DIRECTORY = 'absolute path to directory']
    [DELAY_KEY_WRITE = {0 | 1}]
    [ENCRYPTION = {'Y' | 'N'}]
    [INSERT_METHOD = { NO | FIRST | LAST }]
    [KEY_BLOCK_SIZE = value]
    [MAX_ROWS = value]
    [MIN_ROWS = value]
    [PACK_KEYS = {0 | 1 | DEFAULT}]
    [PASSWORD = 'string']
    [ROW_FORMAT = {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT}]
    [STATS_AUTO_RECALC = {DEFAULT|0|1}]
    [STATS_PERSISTENT = {DEFAULT|0|1}]
    [STATS_SAMPLE_PAGES = value]
    [TABLESPACE tablespace_name [STORAGE {DISK|MEMORY}]]
    [UNION = (table_name[,table_name]...)]

Comprehensive Example: Creating a Products Table in MySQL

CREATE TABLE IF NOT EXISTS products (
    product_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    product_name VARCHAR(255) NOT NULL,
    product_code VARCHAR(50) NOT NULL,
    category_id INT UNSIGNED NOT NULL,
    supplier_id INT UNSIGNED,
    price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
    cost DECIMAL(10,2),
    stock_quantity INT NOT NULL DEFAULT 0,
    reorder_level INT DEFAULT 5,
    weight FLOAT(8,2),
    dimensions VARCHAR(50),
    short_description VARCHAR(255),
    long_description TEXT,
    is_featured BOOLEAN DEFAULT FALSE,
    is_active BOOLEAN DEFAULT TRUE,
    date_added TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    PRIMARY KEY (product_id),
    UNIQUE KEY idx_product_code (product_code),
    INDEX idx_category (category_id),
    INDEX idx_supplier (supplier_id),
    INDEX idx_product_name (product_name(50)),
    FULLTEXT INDEX idx_product_search (product_name, short_description),
    
    CONSTRAINT fk_product_category FOREIGN KEY (category_id) 
        REFERENCES categories (category_id) 
        ON UPDATE CASCADE 
        ON DELETE RESTRICT,
        
    CONSTRAINT fk_product_supplier FOREIGN KEY (supplier_id) 
        REFERENCES suppliers (supplier_id) 
        ON UPDATE CASCADE 
        ON DELETE SET NULL,
        
    CONSTRAINT chk_price CHECK (price >= 0),
    CONSTRAINT chk_stock CHECK (stock_quantity >= 0)
) 
ENGINE=InnoDB 
AUTO_INCREMENT=1001 
DEFAULT CHARSET=utf8mb4 
COLLATE=utf8mb4_unicode_ci
ROW_FORMAT=DYNAMIC
COMMENT='Product catalog with inventory information'
MAX_ROWS=1000000;

MySQL Data Types in Detail

Numeric Data Types

TypeStorageRangeUsage
TINYINT1 byte-128 to 127 or 0 to 255 (unsigned)Small integer values, flags
INT4 bytes-2^31 to 2^31-1 or 0 to 2^32-1 (unsigned)Standard integer values, IDs
DECIMAL(p,s)VariableDepends on precision (p) and scale (s)Exact numeric values (money)
FLOAT4 bytes±1.175494351E-38 to ±3.402823466E+38Approximate numeric values

String Data Types

TypeStorageMax SizeUsage
CHAR(n)n bytes255 charactersFixed-length strings
VARCHAR(n)Length + 1-2 bytes65,535 bytesVariable-length strings
TEXTLength + 2 bytes65,535 bytesLong text strings
LONGTEXTLength + 4 bytes4GBVery long text strings

Date and Time Data Types

TypeStorageRangeFormat
DATE3 bytes1000-01-01 to 9999-12-31YYYY-MM-DD
TIME3 bytes-838:59:59 to 838:59:59HH:MM:SS
DATETIME8 bytes1000-01-01 00:00:00 to 9999-12-31 23:59:59YYYY-MM-DD HH:MM:SS
TIMESTAMP4 bytes1970-01-01 00:00:01 to 2038-01-19 03:14:07YYYY-MM-DD HH:MM:SS

Key MySQL Features

AUTO_INCREMENT Implementation

MySQL's AUTO_INCREMENT attribute creates automatically incrementing column values, typically for primary keys. Unlike SQL Server's IDENTITY, AUTO_INCREMENT can be modified at the table level.

-- Setting the initial AUTO_INCREMENT value CREATE TABLE products ( product_id INT AUTO_INCREMENT PRIMARY KEY, product_name VARCHAR(100) ) AUTO_INCREMENT = 1000; -- Modifying the AUTO_INCREMENT value ALTER TABLE products AUTO_INCREMENT = 2000;

Storage Engines

MySQL allows specifying different storage engines for tables, each with unique characteristics for performance, transaction support, and data integrity.

EngineTransactionsXASavepointsForeign Keys
InnoDBYesYesYesYes
MyISAMNoNoNoNo
MEMORYNoNoNoNo
ARCHIVENoNoNoNo

Character Sets and Collations

MySQL provides extensive support for character sets and collations at the database, table, and column levels.

-- Table with specific character set and collation CREATE TABLE multilingual_content ( content_id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, content_en TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, content_fr TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_french_ci, content_de TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_german2_ci ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Advanced Indexing Options

MySQL supports various index types including B-tree, hash, fulltext, and spatial indexes, with options for partial indexing of string columns.

CREATE TABLE articles ( article_id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, content TEXT NOT NULL, author VARCHAR(100), published_date DATE, -- Regular index INDEX idx_author (author), -- Composite index INDEX idx_author_date (author, published_date), -- Partial index (only indexes first 50 characters) INDEX idx_title (title(50)), -- Fulltext index for search FULLTEXT INDEX idx_content (title, content) );

ON UPDATE CURRENT_TIMESTAMP

MySQL provides automatic timestamp updating for tracking row modifications, a feature not directly available in SQL Server.

CREATE TABLE audit_records ( record_id INT AUTO_INCREMENT PRIMARY KEY, record_data VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );

MySQL's CREATE TABLE syntax offers extensive configuration options that make it highly adaptable to different application requirements. For a comprehensive reference on all MySQL CREATE TABLE options, syntax variations, and best practices, visit the MySQL CREATE TABLE documentation.

MySQL 8.0+ Enhancements

Recent MySQL versions have introduced several important enhancements to the CREATE TABLE syntax:

  • Support for CHECK constraints (previously ignored)
  • Improved JSON data type with functions and indexing
  • Invisible indexes for testing index removal impact
  • Enhanced data dictionary for improved metadata management

SQL Server CREATE TABLE Syntax

SQL Server's CREATE TABLE syntax reflects its enterprise-oriented architecture with features for filegroups, partitioning, and advanced constraint management. The syntax emphasizes explicit schema qualification and named constraints.

CREATE TABLE [ database_name . [ schema_name ] . | schema_name . ] table_name 
(
    column_name1 data_type [ NULL | NOT NULL ] 
        [ CONSTRAINT constraint_name DEFAULT constant_expression ] 
        [ IDENTITY [ ( seed , increment ) ] ]
        [ ROWGUIDCOL ] 
        [ CONSTRAINT constraint_name { PRIMARY KEY | UNIQUE } [ CLUSTERED | NONCLUSTERED ] ]
        [ CONSTRAINT constraint_name CHECK ( logical_expression ) ]
        [ CONSTRAINT constraint_name FOREIGN KEY REFERENCES referenced_table_name ( referenced_column_name ) 
            [ ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] 
            [ ON UPDATE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] ]
        [ COLLATE collation_name ]
        [ SPARSE ]
        [ MASKED WITH ( FUNCTION = 'mask_function' ) ]
        [ ENCRYPTED WITH ( COLUMN_ENCRYPTION_KEY = key_name, ENCRYPTION_TYPE = { DETERMINISTIC | RANDOMIZED }, 
            ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256' ) ]
        [ GENERATED ALWAYS AS { ROW | TRANSACTION_ID | SEQUENCE_NUMBER } [ START | END ] [ HIDDEN ] ]
        [ GENERATED ALWAYS AS ( computed_column_expression ) [ PERSISTED [ NOT NULL ] ] ]
    [ ,...n ]

    [ CONSTRAINT constraint_name PRIMARY KEY [ CLUSTERED | NONCLUSTERED ] 
        ( column [ ASC | DESC ] [ ,...n ] ) 
        [ WITH ( <index_option> [ ,...n ] ) ]
    ]

    [ CONSTRAINT constraint_name UNIQUE [ CLUSTERED | NONCLUSTERED ] 
        ( column [ ASC | DESC ] [ ,...n ] ) 
        [ WITH ( <index_option> [ ,...n ] ) ]
    ]

    [ CONSTRAINT constraint_name FOREIGN KEY ( column [ ,...n ] ) 
        REFERENCES referenced_table_name ( referenced_column_name [ ,...n ] ) 
        [ ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] 
        [ ON UPDATE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] 
    ]

    [ CONSTRAINT constraint_name CHECK ( logical_expression ) ]

    [ PERIOD FOR SYSTEM_TIME ( system_start_time_column_name, system_end_time_column_name ) ]
)
[ WITH ( <table_option> [ ,...n ] ) ]
[ ON { partition_scheme_name ( partition_column_name ) | filegroup | "default" } ]
[ TEXTIMAGE_ON { filegroup | "default" } ]
[ FILESTREAM_ON { partition_scheme_name | filegroup | "default" } ]
[ PARTITION ( column_name RANGE [ LEFT | RIGHT ] FOR VALUES ( [ boundary_value [ ,...n ] ] ) ) ];

Comprehensive Example: Creating a Products Table in SQL Server

CREATE TABLE Production.Products (
    ProductID INT NOT NULL IDENTITY(1001, 1),
    ProductName NVARCHAR(255) NOT NULL,
    ProductCode NVARCHAR(50) NOT NULL,
    CategoryID INT NOT NULL,
    SupplierID INT NULL,
    Price DECIMAL(10, 2) NOT NULL CONSTRAINT DF_Products_Price DEFAULT 0.00,
    Cost DECIMAL(10, 2) NULL,
    StockQuantity INT NOT NULL CONSTRAINT DF_Products_StockQuantity DEFAULT 0,
    ReorderLevel INT NULL CONSTRAINT DF_Products_ReorderLevel DEFAULT 5,
    Weight FLOAT NULL,
    Dimensions NVARCHAR(50) NULL,
    ShortDescription NVARCHAR(255) NULL,
    LongDescription NVARCHAR(MAX) NULL,
    IsFeatured BIT CONSTRAINT DF_Products_IsFeatured DEFAULT 0,
    IsActive BIT CONSTRAINT DF_Products_IsActive DEFAULT 1,
    DateAdded DATETIME2 CONSTRAINT DF_Products_DateAdded DEFAULT GETDATE(),
    LastUpdated DATETIME2 NULL,
    
    CONSTRAINT PK_Products PRIMARY KEY CLUSTERED (ProductID),
    CONSTRAINT UQ_Products_ProductCode UNIQUE NONCLUSTERED (ProductCode),
    
    CONSTRAINT FK_Products_Categories FOREIGN KEY (CategoryID) 
        REFERENCES Production.Categories (CategoryID) 
        ON UPDATE CASCADE 
        ON DELETE RESTRICT,
        
    CONSTRAINT FK_Products_Suppliers FOREIGN KEY (SupplierID) 
        REFERENCES Production.Suppliers (SupplierID) 
        ON UPDATE CASCADE 
        ON DELETE SET NULL,
        
    CONSTRAINT CK_Products_Price CHECK (Price >= 0),
    CONSTRAINT CK_Products_StockQuantity CHECK (StockQuantity >= 0)
)
WITH (
    DATA_COMPRESSION = PAGE,
    DURABILITY = SCHEMA_AND_DATA
)
ON ProductsFileGroup
TEXTIMAGE_ON LargeDataFileGroup;

-- Create indexes
CREATE NONCLUSTERED INDEX IX_Products_CategoryID ON Production.Products (CategoryID);
CREATE NONCLUSTERED INDEX IX_Products_SupplierID ON Production.Products (SupplierID) WHERE SupplierID IS NOT NULL;
CREATE NONCLUSTERED INDEX IX_Products_ProductName ON Production.Products (ProductName);

-- Create a full-text index
CREATE FULLTEXT INDEX ON Production.Products (ProductName, ShortDescription) 
KEY INDEX PK_Products
ON ProductCatalogFullTextIndex
WITH CHANGE_TRACKING AUTO;

SQL Server Data Types in Detail

Numeric Data Types

TypeStorageRangeUsage
TINYINT1 byte0 to 255Small integer values
INT4 bytes-2^31 to 2^31-1Standard integer values, IDs
DECIMAL(p,s)5-17 bytes-10^38+1 to 10^38-1Exact numeric values (money)
FLOAT4 or 8 bytes-1.79E+308 to 1.79E+308Approximate numeric values

String Data Types

TypeStorageMax SizeUsage
CHAR(n)n bytes8,000 charactersFixed-length non-Unicode strings
VARCHAR(n)Actual length + 2 bytes8,000 characters or MAX (2GB)Variable-length non-Unicode strings
NCHAR(n)2×n bytes4,000 charactersFixed-length Unicode strings
NVARCHAR(n)2×actual length + 2 bytes4,000 characters or MAX (2GB)Variable-length Unicode strings

Date and Time Data Types

TypeStorageRangePrecision
DATE3 bytes0001-01-01 to 9999-12-311 day
TIME3-5 bytes00:00:00.0000000 to 23:59:59.9999999100ns
DATETIME26-8 bytes0001-01-01 to 9999-12-31100ns
DATETIMEOFFSET8-10 bytes0001-01-01 to 9999-12-31 (with time zone)100ns

Key SQL Server Features

IDENTITY Implementation

SQL Server uses the IDENTITY property for auto-incrementing columns, with explicit seed and increment values.

-- Creating a table with IDENTITY column CREATE TABLE Products ( ProductID INT IDENTITY(1000, 1) PRIMARY KEY, ProductName NVARCHAR(100) NOT NULL ); -- You cannot directly set IDENTITY values in INSERT statements -- To insert explicit values, you must use: SET IDENTITY_INSERT Products ON; INSERT INTO Products (ProductID, ProductName) VALUES (1500, 'Custom Product'); SET IDENTITY_INSERT Products OFF;

Schema Qualification

SQL Server organizes objects within schemas, providing an additional namespace level for better organization and security management.

-- Creating a schema CREATE SCHEMA Sales; GO -- Creating a table in the schema CREATE TABLE Sales.Customers ( CustomerID INT PRIMARY KEY, CustomerName NVARCHAR(100) ); -- Granting permissions at the schema level GRANT SELECT ON SCHEMA::Sales TO SalesRole;

Filegroups and Partitioning

SQL Server allows tables and indexes to be placed on specific filegroups for performance optimization and storage management.

-- Creating filegroups ALTER DATABASE SalesDB ADD FILEGROUP FG_Current; ALTER DATABASE SalesDB ADD FILEGROUP FG_Archive; -- Adding files to filegroups ALTER DATABASE SalesDB ADD FILE ( NAME = 'SalesData_Current', FILENAME = 'C:\Data\SalesData_Current.ndf', SIZE = 100MB ) TO FILEGROUP FG_Current; -- Creating a partitioned table CREATE PARTITION FUNCTION SalesByDatePF (DATETIME2) AS RANGE RIGHT FOR VALUES ('2023-01-01', '2024-01-01'); CREATE PARTITION SCHEME SalesByDatePS AS PARTITION SalesByDatePF TO (FG_Archive, FG_Current, FG_Current); CREATE TABLE Sales.Orders ( OrderID INT IDENTITY(1,1) PRIMARY KEY, OrderDate DATETIME2 NOT NULL, CustomerID INT NOT NULL, Amount DECIMAL(10,2) NOT NULL ) ON SalesByDatePS(OrderDate);

Computed Columns

SQL Server supports computed columns that can be either virtual (calculated on-the-fly) or persisted (stored physically in the table).

CREATE TABLE Products ( ProductID INT IDENTITY(1,1) PRIMARY KEY, Price DECIMAL(10,2) NOT NULL, DiscountRate DECIMAL(5,2) NOT NULL DEFAULT 0, -- Virtual computed column (calculated when queried) DiscountAmount AS (Price * (DiscountRate / 100)), -- Persisted computed column (stored in the table) FinalPrice AS (Price - (Price * (DiscountRate / 100))) PERSISTED ); -- You can index persisted computed columns CREATE INDEX IX_Products_FinalPrice ON Products(FinalPrice);

Temporal Tables

SQL Server supports system-versioned temporal tables that automatically track historical data changes.

CREATE TABLE Products ( ProductID INT IDENTITY(1,1) PRIMARY KEY, ProductName NVARCHAR(100) NOT NULL, Price DECIMAL(10,2) NOT NULL, ValidFrom DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL, ValidTo DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL, PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo) ) WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.ProductsHistory)); -- Query data at a specific point in time SELECT * FROM Products FOR SYSTEM_TIME AS OF '2023-01-15 12:00:00';

SQL Server's CREATE TABLE syntax provides enterprise-grade features for performance, security, and data management. For comprehensive documentation on SQL Server's CREATE TABLE syntax and all available options, refer to the SQL Server CREATE TABLE syntax guide.

SQL Server 2019+ Enhancements

Recent SQL Server versions have introduced several important enhancements to table creation:

  • Data classification and sensitivity labeling
  • Improved JSON support with optimized storage
  • UTF-8 character encoding support
  • Accelerated Database Recovery (ADR) for faster recovery

Advanced Features Comparison

Beyond basic table creation, MySQL and SQL Server offer advanced features that address specific database requirements. Understanding these differences is crucial for optimizing database design and performance.

Constraints and Data Integrity

FeatureMySQLSQL Server
Named ConstraintsOptionalCommon practice
CHECK ConstraintsSupported since MySQL 8.0.16Fully supported
Foreign Key ActionsCASCADE, SET NULL, RESTRICT, NO ACTIONCASCADE, SET NULL, SET DEFAULT, NO ACTION
Default ConstraintsInline with column definitionCan be named and added separately
Computed ColumnsGenerated columns (virtual or stored)Computed columns (virtual or persisted)

MySQL Example

CREATE TABLE orders ( order_id INT AUTO_INCREMENT PRIMARY KEY, customer_id INT NOT NULL, order_date DATE NOT NULL, total_amount DECIMAL(10,2) NOT NULL, CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ON DELETE RESTRICT ON UPDATE CASCADE, CONSTRAINT chk_total CHECK (total_amount > 0) );

SQL Server Example

CREATE TABLE Orders ( OrderID INT IDENTITY(1,1) PRIMARY KEY, CustomerID INT NOT NULL, OrderDate DATE NOT NULL CONSTRAINT DF_Orders_OrderDate DEFAULT GETDATE(), TotalAmount DECIMAL(10,2) NOT NULL, CONSTRAINT FK_Orders_Customers FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID) ON DELETE NO ACTION ON UPDATE CASCADE, CONSTRAINT CK_Orders_TotalAmount CHECK (TotalAmount > 0) );

Performance Optimization Features

FeatureMySQLSQL Server
Index TypesB-tree, Hash, Fulltext, SpatialB-tree, Columnstore, XML, Spatial, Hash, Memory-optimized
PartitioningRANGE, LIST, HASH, KEY partitioningRANGE partitioning with filegroups
CompressionRow and page compression (InnoDB)Row, page, and columnstore compression
Clustered IndexesPrimary key is always clusteredCan specify CLUSTERED or NONCLUSTERED
In-Memory TablesMEMORY storage engine (limited features)Memory-optimized tables with durability options

MySQL Partitioning Example

CREATE TABLE sales ( id INT NOT NULL, created_at DATE NOT NULL, customer_id INT NOT NULL, amount DECIMAL(10,2) NOT NULL, PRIMARY KEY(id, created_at) ) PARTITION BY RANGE (YEAR(created_at)) ( PARTITION p0 VALUES LESS THAN (2020), PARTITION p1 VALUES LESS THAN (2021), PARTITION p2 VALUES LESS THAN (2022), PARTITION p3 VALUES LESS THAN (2023), PARTITION p4 VALUES LESS THAN (2024), PARTITION p5 VALUES LESS THAN MAXVALUE );

SQL Server Partitioning Example

-- Create partition function CREATE PARTITION FUNCTION SalesDateRangePF (DATE) AS RANGE RIGHT FOR VALUES ( '2020-01-01', '2021-01-01', '2022-01-01', '2023-01-01', '2024-01-01' ); -- Create partition scheme CREATE PARTITION SCHEME SalesDateRangePS AS PARTITION SalesDateRangePF TO ( FG_Archive, FG_2020, FG_2021, FG_2022, FG_2023, FG_Current ); -- Create partitioned table CREATE TABLE Sales ( ID INT NOT NULL, CreatedAt DATE NOT NULL, CustomerID INT NOT NULL, Amount DECIMAL(10,2) NOT NULL, CONSTRAINT PK_Sales PRIMARY KEY (ID, CreatedAt) ) ON SalesDateRangePS(CreatedAt);

Storage and Organization

FeatureMySQLSQL Server
Storage EnginesMultiple engines (InnoDB, MyISAM, etc.)Single engine with different options
Schema OrganizationDatabases as schema containersSchemas within databases
File ManagementTablespaces with limited controlFilegroups with detailed control
LOB StorageInline with table dataCan be stored in separate filegroups
Temporary TablesCREATE TEMPORARY TABLECREATE TABLE #temp or @table_variable

MySQL Storage Engine Example

-- Transaction-safe table with foreign keys CREATE TABLE customers ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE ) ENGINE=InnoDB; -- Fast read-only table without transactions CREATE TABLE log_entries ( id INT AUTO_INCREMENT PRIMARY KEY, log_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, message TEXT NOT NULL ) ENGINE=MyISAM; -- In-memory table for temporary data CREATE TABLE cache_items ( cache_key VARCHAR(100) PRIMARY KEY, cache_value TEXT, expiry TIMESTAMP ) ENGINE=MEMORY;

SQL Server Filegroup Example

-- Create filegroups ALTER DATABASE SalesDB ADD FILEGROUP FG_Data; ALTER DATABASE SalesDB ADD FILEGROUP FG_Index; ALTER DATABASE SalesDB ADD FILEGROUP FG_Archive; ALTER DATABASE SalesDB ADD FILEGROUP FG_LOB; -- Add files to filegroups ALTER DATABASE SalesDB ADD FILE ( NAME = 'SalesData', FILENAME = 'C:\Data\SalesData.ndf', SIZE = 100MB ) TO FILEGROUP FG_Data; -- Create table with different storage locations CREATE TABLE Sales.Orders ( OrderID INT IDENTITY(1,1), OrderDate DATETIME2 NOT NULL, CustomerID INT NOT NULL, OrderDetails NVARCHAR(MAX), CONSTRAINT PK_Orders PRIMARY KEY (OrderID) ) ON FG_Data TEXTIMAGE_ON FG_LOB;

Special Data Types and Features

FeatureMySQLSQL Server
JSON SupportJSON data type with functionsNVARCHAR with JSON functions
Spatial DataSpatial data types and functionsGEOMETRY and GEOGRAPHY types
XML SupportLimited XML functionsXML data type with XQuery
Temporal DataNo built-in temporal tablesSystem-versioned temporal tables
Hierarchical DataNo specialized typeHIERARCHYID data type

MySQL JSON Example

CREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, attributes JSON, INDEX idx_attributes ((CAST(attributes->>'$.color' AS CHAR(20)))) ); -- Insert with JSON INSERT INTO products (name, attributes) VALUES ('Smartphone', '{"color": "black", "storage": 128, "features": ["5G", "waterproof"]}'); -- Query JSON data SELECT id, name, JSON_EXTRACT(attributes, '$.color') AS color, JSON_EXTRACT(attributes, '$.storage') AS storage FROM products WHERE JSON_CONTAINS(attributes, '"waterproof"', '$.features');

SQL Server Temporal Table Example

-- Create a system-versioned temporal table CREATE TABLE Products ( ProductID INT IDENTITY(1,1) PRIMARY KEY, ProductName NVARCHAR(100) NOT NULL, Price DECIMAL(10,2) NOT NULL, ValidFrom DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL, ValidTo DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL, PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo) ) WITH ( SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.ProductsHistory ) ); -- Query data as of a point in time SELECT ProductID, ProductName, Price FROM Products FOR SYSTEM_TIME AS OF '2023-01-15 12:00:00'; -- Query data changes over time SELECT ProductID, ProductName, Price, ValidFrom, ValidTo FROM Products FOR SYSTEM_TIME BETWEEN '2023-01-01' AND '2023-12-31';

Database Migration Considerations

Migrating database schemas between MySQL and SQL Server requires careful planning and understanding of the syntax differences. Here are key considerations when converting CREATE TABLE statements between these systems.

Data Type Mapping

MySQL TypeSQL Server EquivalentNotes
TINYINT(1)BITMySQL often uses TINYINT(1) for boolean values
VARCHARVARCHAR or NVARCHARUse NVARCHAR for Unicode support
TEXTVARCHAR(MAX) or NVARCHAR(MAX)SQL Server doesn't have TEXT type in modern usage
DATETIMEDATETIME2DATETIME2 has better precision and range
TIMESTAMPDATETIME2Behavior differences need special handling
UNSIGNED INTBIGINTSQL Server doesn't support unsigned integers

Auto-Increment Conversion

MySQL

CREATE TABLE products ( product_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL ) AUTO_INCREMENT=1001;

SQL Server

CREATE TABLE Products ( ProductID INT IDENTITY(1001, 1) PRIMARY KEY, Name NVARCHAR(100) NOT NULL );

Note: SQL Server's IDENTITY requires both seed and increment values, while MySQL's AUTO_INCREMENT only specifies the starting value.

Default Value Differences

MySQL

CREATE TABLE orders ( order_id INT AUTO_INCREMENT PRIMARY KEY, order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, status VARCHAR(20) DEFAULT 'pending' );

SQL Server

CREATE TABLE Orders ( OrderID INT IDENTITY(1,1) PRIMARY KEY, OrderDate DATETIME2 DEFAULT GETDATE(), LastUpdated DATETIME2 NULL, Status NVARCHAR(20) DEFAULT 'pending' ); -- Need trigger for auto-update CREATE TRIGGER trg_Orders_Update ON Orders AFTER UPDATE AS BEGIN UPDATE Orders SET LastUpdated = GETDATE() FROM Orders o INNER JOIN inserted i ON o.OrderID = i.OrderID; END;

Constraint Naming and Definition

MySQL

CREATE TABLE orders ( order_id INT AUTO_INCREMENT PRIMARY KEY, customer_id INT NOT NULL, amount DECIMAL(10,2) NOT NULL, INDEX idx_customer (customer_id), CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE RESTRICT );

SQL Server

CREATE TABLE Orders ( OrderID INT IDENTITY(1,1) PRIMARY KEY, CustomerID INT NOT NULL, Amount DECIMAL(10,2) NOT NULL, CONSTRAINT FK_Orders_Customers FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID) ON DELETE NO ACTION ); CREATE INDEX IX_Orders_CustomerID ON Orders(CustomerID);

Note: SQL Server typically uses explicit constraint names and often separates index creation from table creation, while MySQL commonly defines indexes inline with the table definition.

Migration Tools and Strategies

When migrating between MySQL and SQL Server, consider these approaches:

  • Use specialized migration tools like SQL Server Migration Assistant (SSMA) for MySQL, which can automatically convert schema and data
  • Script-based approach where you extract schema definitions, transform them with conversion rules, and apply them to the target database
  • ETL tools that can handle both schema conversion and data migration between different database systems
  • Database-specific features audit to identify features that don't have direct equivalents and require redesign

For comprehensive migration guidance, syntax conversion tools, and best practices, refer to the SQL database migration guide which provides detailed step-by-step instructions for converting between different database systems.

CREATE TABLE Best Practices

Regardless of which database system you're using, following these best practices will help you create well-structured, efficient, and maintainable tables that perform well and adapt to changing requirements.

Normalize Your Database Design

Follow normalization principles to reduce data redundancy and improve data integrity. Typically aim for Third Normal Form (3NF) for most operational databases, but know when to denormalize for performance.

Example: Normalized Tables

-- Instead of one denormalized table CREATE TABLE orders_denormalized ( order_id INT PRIMARY KEY, customer_name VARCHAR(100), customer_email VARCHAR(100), customer_address TEXT, product_name VARCHAR(100), product_price DECIMAL(10,2), quantity INT, total_price DECIMAL(10,2) );
-- Use normalized tables CREATE TABLE customers ( customer_id INT PRIMARY KEY, name VARCHAR(100), email VARCHAR(100), address TEXT ); CREATE TABLE products ( product_id INT PRIMARY KEY, name VARCHAR(100), price DECIMAL(10,2) ); CREATE TABLE orders ( order_id INT PRIMARY KEY, customer_id INT, order_date DATE, FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ); CREATE TABLE order_items ( order_id INT, product_id INT, quantity INT, price DECIMAL(10,2), PRIMARY KEY (order_id, product_id), FOREIGN KEY (order_id) REFERENCES orders(order_id), FOREIGN KEY (product_id) REFERENCES products(product_id) );

Choose Appropriate Data Types

Select the most appropriate data type for each column to optimize storage, ensure data integrity, and improve query performance.

Data CategoryBest PracticeMySQL ExampleSQL Server Example
IntegersUse the smallest integer type that can hold your maximum valueTINYINT for 0-255, SMALLINT for larger rangesTINYINT, SMALLINT based on range
Decimal ValuesUse DECIMAL for exact values (money), FLOAT for approximateDECIMAL(10,2) for currencyDECIMAL(10,2) or MONEY
StringsUse VARCHAR for variable length, CHAR for fixedVARCHAR(50) with utf8mb4 charsetNVARCHAR(50) for Unicode
DatesUse date-specific types, not stringsDATE, DATETIME, TIMESTAMPDATE, DATETIME2, DATETIMEOFFSET
BooleanUse boolean or bit types, not integersBOOLEAN or TINYINT(1)BIT

Implement Proper Constraints

Use constraints to enforce data integrity at the database level rather than relying solely on application logic.

Essential Constraints

  • PRIMARY KEY - Uniquely identifies each row
  • FOREIGN KEY - Maintains referential integrity
  • UNIQUE - Prevents duplicate values
  • NOT NULL - Ensures required data is provided
  • CHECK - Validates data against conditions
  • DEFAULT - Provides fallback values
CREATE TABLE employees ( employee_id INT PRIMARY KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL UNIQUE, hire_date DATE NOT NULL, department_id INT NOT NULL, salary DECIMAL(10,2) NOT NULL, manager_id INT, -- Check constraint CONSTRAINT chk_salary CHECK (salary > 0), -- Default constraint CONSTRAINT df_hire_date DEFAULT CURRENT_DATE FOR hire_date, -- Foreign key to departments CONSTRAINT fk_department FOREIGN KEY (department_id) REFERENCES departments(department_id), -- Self-referencing foreign key CONSTRAINT fk_manager FOREIGN KEY (manager_id) REFERENCES employees(employee_id) );

Following these best practices will help you create optimized database schemas regardless of which database system you're using.

Master SQL CREATE TABLE Syntax Across Database Systems

Whether you're working with MySQL, SQL Server, or migrating between database systems, understanding the nuances of CREATE TABLE syntax is essential for building robust, efficient, and maintainable database applications.