Data Types trong SQL Server
1. Tổng quan Data Types
Chọn đúng Data Type rất quan trọng vì ảnh hưởng đến:
- Storage - Dung lượng lưu trữ
- Performance - Hiệu năng query
- Data Integrity - Ngăn chặn dữ liệu không hợp lệ
- Functionality - Các phép toán có thể thực hiện
SQL Server Data Types
Data Types
Numeric
INT, BIGINT, SMALLINT
DECIMAL, NUMERIC
FLOAT, REAL
MONEY, SMALLMONEY
String
CHAR, VARCHAR
NCHAR, NVARCHAR
TEXT, NTEXT
Date/Time
DATE
TIME
DATETIME2
DATETIMEOFFSET
Others
BINARY, VARBINARY
UNIQUEIDENTIFIER
XML
JSON (NVARCHAR)
2. Numeric Types
2.1 Integer Types
| Type | Storage | Range | Use Case |
|---|---|---|---|
| TINYINT | 1 byte | 0 to 255 | Status codes, flags |
| SMALLINT | 2 bytes | -32,768 to 32,767 | Small counters |
| INT | 4 bytes | -2.1 billion to 2.1 billion | Most IDs, quantities |
| BIGINT | 8 bytes | ±9.2 quintillion | Large IDs, big numbers |
| BIT | 1 bit | 0, 1, NULL | Boolean flags |
SQL
1-- Integer examples2CREATE TABLE Product (3 ProductID INT IDENTITY(1,1) PRIMARY KEY, -- INT cho ID4 CategoryID SMALLINT, -- SMALLINT cho lookup5 Quantity INT, -- INT cho quantity6 ViewCount BIGINT DEFAULT 0, -- BIGINT cho counters lớn7 IsActive BIT DEFAULT 1, -- BIT cho boolean8 Priority TINYINT DEFAULT 0 -- TINYINT cho 0-2559);1011-- AdventureWorks example12SELECT 13 ProductID, -- INT14 SafetyStockLevel, -- SMALLINT15 ReorderPoint -- SMALLINT16FROM Production.Product17WHERE ProductID = 1;2.2 Exact Numeric (DECIMAL/NUMERIC)
SQL
1-- DECIMAL(p, s) = NUMERIC(p, s)2-- p = precision (tổng số digits, max 38)3-- s = scale (số digits sau decimal point)45CREATE TABLE Financial (6 Amount DECIMAL(10, 2), -- 12345678.90 (8 digits + 2 decimals)7 Rate DECIMAL(5, 4), -- 0.0525 (1 digit + 4 decimals)8 Percentage DECIMAL(5, 2), -- 100.00 (3 digits + 2 decimals)9 TaxRate NUMERIC(4, 3) -- 0.085 (tax 8.5%)10);1112-- Storage by precision13-- 1-9 digits: 5 bytes14-- 10-19 digits: 9 bytes15-- 20-28 digits: 13 bytes16-- 29-38 digits: 17 bytes💡 Best Practice: Dùng
DECIMAL(19, 4)cho tiền tệ thay vìMONEYđể có control tốt hơn.
2.3 Approximate Numeric (FLOAT/REAL)
SQL
1-- FLOAT(n) - n là số bits cho mantissa2-- REAL = FLOAT(24) = 4 bytes3-- FLOAT = FLOAT(53) = 8 bytes45CREATE TABLE Scientific (6 Measurement FLOAT, -- Scientific calculations7 Temperature REAL, -- Less precision needed8 Latitude FLOAT(24), -- Coordinates9 Longitude FLOAT(24)10);1112-- ⚠️ WARNING: FLOAT có rounding errors!13DECLARE @f FLOAT = 0.1 + 0.2;14SELECT @f; -- 0.30000000000000004 (không chính xác!)1516-- ✅ Dùng DECIMAL cho financial calculations17DECLARE @d DECIMAL(10, 2) = 0.1 + 0.2;18SELECT @d; -- 0.30 (chính xác!)2.4 Money Types
SQL
1-- MONEY vs SMALLMONEY2-- MONEY: -922 trillion to 922 trillion (8 bytes)3-- SMALLMONEY: -214,748.3648 to 214,748.3647 (4 bytes)45CREATE TABLE Transaction (6 Amount MONEY, -- Large amounts7 Fee SMALLMONEY -- Small amounts8);910-- AdventureWorks uses MONEY11SELECT 12 ProductID,13 StandardCost, -- MONEY14 ListPrice -- MONEY15FROM Production.Product16WHERE ProductID = 1;1718-- ⚠️ MONEY có precision issues với division19DECLARE @m1 MONEY = 1.00, @m2 MONEY = 3.00;20SELECT @m1 / @m2; -- 0.3333 (chỉ 4 decimal places)2122-- ✅ Recommend: DECIMAL(19, 4) cho financial3. String Types
3.1 Non-Unicode (CHAR, VARCHAR)
SQL
1-- CHAR(n) - Fixed length (1 to 8000)2-- VARCHAR(n) - Variable length (1 to 8000)3-- VARCHAR(MAX) - Up to 2GB45CREATE TABLE Product (6 SKU CHAR(10), -- Fixed 10 chars, padded with spaces7 ProductCode VARCHAR(20), -- Variable up to 20 chars8 Description VARCHAR(MAX) -- Large text9);1011-- Storage12-- CHAR(10): Always 10 bytes13-- VARCHAR(10): 1-10 bytes + 2 bytes overhead14-- VARCHAR(MAX): 2 bytes + actual length (up to 2GB)1516-- ⚠️ CHAR pads with spaces17DECLARE @c CHAR(10) = 'ABC';18SELECT LEN(@c), DATALENGTH(@c); -- LEN=3, DATALENGTH=103.2 Unicode (NCHAR, NVARCHAR)
SQL
1-- N = National/Unicode2-- Supports international characters (Vietnamese, Chinese, etc.)3-- Uses 2 bytes per character (UTF-16)45CREATE TABLE Customer (6 FirstName NVARCHAR(50), -- Vietnamese: "Nguyễn"7 LastName NVARCHAR(50), -- Japanese: "田中"8 Email VARCHAR(100), -- ASCII only9 Bio NVARCHAR(MAX) -- Large Unicode text10);1112-- Storage13-- NCHAR(10): Always 20 bytes (10 × 2)14-- NVARCHAR(10): 2-20 bytes + 2 bytes overhead1516-- Insert Unicode data17INSERT INTO Customer (FirstName, LastName)18VALUES (N'Nguyễn', N'Văn A'); -- Prefix N'' for Unicode literal💡 Best Practice cho Vietnamese:
- Luôn dùng
NVARCHARcho text tiếng Việt- Dùng prefix
N''khi insert literal
3.3 AdventureWorks String Usage
SQL
1-- AdventureWorks dùng NVARCHAR cho names2SELECT 3 c.COLUMN_NAME,4 c.DATA_TYPE,5 c.CHARACTER_MAXIMUM_LENGTH6FROM INFORMATION_SCHEMA.COLUMNS c7WHERE c.TABLE_SCHEMA = 'Person' 8AND c.TABLE_NAME = 'Person'9AND c.DATA_TYPE LIKE '%char%';1011-- FirstName NVARCHAR(50)12-- MiddleName NVARCHAR(50)13-- LastName NVARCHAR(50)3.4 Choosing String Type
| Need | Type | Example |
|---|---|---|
| Fixed-length code | CHAR | SKU, Country Code |
| Variable ASCII | VARCHAR | Email, URL |
| International text | NVARCHAR | Names, Addresses |
| Large text | VARCHAR(MAX) | Articles, Logs |
| Large Unicode | NVARCHAR(MAX) | Documents |
4. Date/Time Types
4.1 Date Types Comparison
| Type | Storage | Range | Precision | Use Case |
|---|---|---|---|---|
| DATE | 3 bytes | 0001-01-01 to 9999-12-31 | 1 day | Birthdate, dates only |
| TIME | 3-5 bytes | 00:00:00 to 23:59:59 | 100ns | Time only |
| DATETIME | 8 bytes | 1753-9999 | 3.33ms | Legacy |
| DATETIME2 | 6-8 bytes | 0001-9999 | 100ns | Recommended |
| SMALLDATETIME | 4 bytes | 1900-2079 | 1 minute | Approximate |
| DATETIMEOFFSET | 8-10 bytes | 0001-9999 | 100ns | With timezone |
SQL
1CREATE TABLE Event (2 EventID INT IDENTITY(1,1) PRIMARY KEY,3 EventDate DATE, -- Date only4 StartTime TIME(0), -- Time, no fractions5 CreatedAt DATETIME2(3), -- Millisecond precision6 ModifiedAt DATETIME2(7) DEFAULT SYSDATETIME(), -- Max precision7 ScheduledTime DATETIMEOFFSET -- With timezone8);910-- Insert examples11INSERT INTO Event (EventDate, StartTime, CreatedAt, ScheduledTime)12VALUES (13 '2024-01-15', -- DATE14 '09:30:00', -- TIME15 '2024-01-15T09:30:00.123', -- DATETIME216 '2024-01-15T09:30:00+07:00' -- DATETIMEOFFSET (Vietnam)17);4.2 Date Functions
SQL
1-- Current date/time2SELECT 3 GETDATE() AS DateTime, -- DATETIME4 SYSDATETIME() AS DateTime2, -- DATETIME2(7)5 GETUTCDATE() AS UTCDateTime, -- UTC DATETIME6 SYSDATETIMEOFFSET() AS WithOffset; -- DATETIMEOFFSET78-- Date parts9SELECT 10 YEAR(GETDATE()) AS Year,11 MONTH(GETDATE()) AS Month,12 DAY(GETDATE()) AS Day,13 DATEPART(WEEKDAY, GETDATE()) AS Weekday;1415-- Date calculations16SELECT 17 DATEADD(DAY, 7, GETDATE()) AS NextWeek,18 DATEADD(MONTH, -1, GETDATE()) AS LastMonth,19 DATEDIFF(DAY, '2024-01-01', GETDATE()) AS DaysSince;4.3 AdventureWorks Date Usage
SQL
1-- AdventureWorks dùng DATETIME (legacy)2SELECT 3 SalesOrderID,4 OrderDate, -- DATETIME5 DueDate, -- DATETIME6 ShipDate, -- DATETIME7 ModifiedDate -- DATETIME8FROM Sales.SalesOrderHeader9WHERE SalesOrderID = 43659;1011-- Recommend: DATETIME2 cho new projects5. Binary Types
5.1 Binary Data
SQL
1-- BINARY(n) - Fixed length (1-8000 bytes)2-- VARBINARY(n) - Variable length (1-8000 bytes)3-- VARBINARY(MAX) - Up to 2GB45CREATE TABLE Document (6 DocumentID INT IDENTITY(1,1) PRIMARY KEY,7 FileName NVARCHAR(255),8 FileContent VARBINARY(MAX), -- Store file binary9 Thumbnail VARBINARY(8000), -- Small image10 HashValue BINARY(32) -- Fixed-size hash11);1213-- Insert binary data14INSERT INTO Document (FileName, HashValue)15VALUES (16 'report.pdf',17 HASHBYTES('SHA2_256', 'some content') -- 32-byte hash18);5.2 AdventureWorks Binary
SQL
1-- Production.Document stores files2SELECT 3 DocumentNode,4 FileName,5 FileExtension,6 DATALENGTH(Document) AS FileSize -- VARBINARY(MAX)7FROM Production.Document8WHERE FileName LIKE '%.doc%';6. Special Types
6.1 UNIQUEIDENTIFIER (GUID)
SQL
1-- 16-byte globally unique identifier2CREATE TABLE Session (3 SessionID UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),4 UserID INT,5 CreatedAt DATETIME2 DEFAULT SYSDATETIME()6);78-- Generate GUID9SELECT NEWID(); -- Random GUID10SELECT NEWSEQUENTIALID(); -- Sequential GUID (better for clustered index)1112-- AdventureWorks uses rowguid13SELECT 14 ProductID,15 rowguid -- UNIQUEIDENTIFIER16FROM Production.Product17WHERE ProductID = 1;6.2 XML Type
SQL
1-- Native XML storage and querying2CREATE TABLE Config (3 ConfigID INT IDENTITY(1,1) PRIMARY KEY,4 ConfigData XML5);67INSERT INTO Config (ConfigData)8VALUES ('<settings><timeout>30</timeout><retries>3</retries></settings>');910-- Query XML11SELECT 12 ConfigID,13 ConfigData.value('(/settings/timeout)[1]', 'INT') AS Timeout,14 ConfigData.value('(/settings/retries)[1]', 'INT') AS Retries15FROM Config;6.3 JSON (stored as NVARCHAR)
SQL
1-- SQL Server 2016+ supports JSON functions2CREATE TABLE APILog (3 LogID INT IDENTITY(1,1) PRIMARY KEY,4 RequestBody NVARCHAR(MAX), -- JSON stored as string5 ResponseBody NVARCHAR(MAX),6 CreatedAt DATETIME2 DEFAULT SYSDATETIME()7);89-- Insert JSON10INSERT INTO APILog (RequestBody)11VALUES ('{"user_id": 123, "action": "login", "timestamp": "2024-01-15T10:30:00"}');1213-- Query JSON14SELECT 15 LogID,16 JSON_VALUE(RequestBody, '$.user_id') AS UserID,17 JSON_VALUE(RequestBody, '$.action') AS Action18FROM APILog;1920-- Validate JSON21SELECT ISJSON('{"name": "test"}'); -- Returns 1 if valid7. Computed Columns
SQL
1-- Computed column tự động tính toán2CREATE TABLE OrderDetail (3 OrderDetailID INT IDENTITY(1,1) PRIMARY KEY,4 Quantity INT,5 UnitPrice DECIMAL(10, 2),6 Discount DECIMAL(4, 2) DEFAULT 0,7 8 -- Computed columns9 LineTotal AS (Quantity * UnitPrice), -- Not stored10 NetTotal AS (Quantity * UnitPrice * (1 - Discount)) PERSISTED -- Stored11);1213-- AdventureWorks computed columns14SELECT 15 SalesOrderDetailID,16 OrderQty,17 UnitPrice,18 UnitPriceDiscount,19 LineTotal -- Computed: OrderQty * UnitPrice * (1 - UnitPriceDiscount)20FROM Sales.SalesOrderDetail21WHERE SalesOrderID = 43659;8. Data Type Conversion
8.1 Implicit vs Explicit
SQL
1-- Implicit conversion (automatic)2SELECT 1 + '1'; -- INT + VARCHAR = INT (result: 2)3SELECT 1 + 1.5; -- INT + DECIMAL = DECIMAL (result: 2.5)45-- Explicit conversion6SELECT CAST('123' AS INT); -- CAST syntax7SELECT CONVERT(INT, '123'); -- CONVERT syntax8SELECT TRY_CAST('abc' AS INT); -- Returns NULL instead of error9SELECT TRY_CONVERT(INT, 'abc'); -- Returns NULL1011-- Date conversion12SELECT CONVERT(VARCHAR, GETDATE(), 103); -- dd/mm/yyyy13SELECT CONVERT(VARCHAR, GETDATE(), 120); -- yyyy-mm-dd hh:mi:ss14SELECT FORMAT(GETDATE(), 'dd/MM/yyyy'); -- Flexible formatting8.2 Conversion Gotchas
SQL
1-- ⚠️ Data loss in conversion2SELECT CAST(123.456 AS INT); -- Result: 123 (truncated)3SELECT CAST('2024-13-01' AS DATE); -- Error! Invalid month45-- ✅ Safe conversion6SELECT TRY_CAST('2024-13-01' AS DATE); -- Returns NULL78-- ⚠️ Unicode loss9DECLARE @nv NVARCHAR(10) = N'Việt Nam';10SELECT CAST(@nv AS VARCHAR(10)); -- "Vi?t Nam" - lost diacritics!9. Best Practices Summary
markdown
1## Data Type Selection Guidelines2 3### Numeric4- ✅ INT for most IDs and quantities5- ✅ DECIMAL(19, 4) for money/financial6- ❌ Avoid FLOAT for financial calculations7- ❌ Avoid MONEY type (limited precision)8 9### String10- ✅ NVARCHAR for international text (Vietnamese)11- ✅ VARCHAR for ASCII-only data (emails, codes)12- ✅ Use appropriate size limits13- ❌ Avoid VARCHAR(MAX) unless necessary14- ❌ Don't use CHAR unless truly fixed-length15 16### Date/Time17- ✅ DATETIME2 for new projects18- ✅ DATE when time not needed19- ✅ DATETIMEOFFSET for multi-timezone apps20- ❌ Avoid legacy DATETIME21 22### General23- ✅ Choose smallest type that fits24- ✅ Consider NULL handling25- ✅ Use consistent types for JOINs26- ✅ Document special types (JSON, XML)| Data Need | Recommended Type |
|---|---|
| Primary Key | INT IDENTITY |
| Price/Money | DECIMAL(19, 4) |
| Vietnamese Name | NVARCHAR(100) |
| VARCHAR(255) | |
| Date only | DATE |
| Timestamp | DATETIME2(3) |
| Boolean | BIT |
| Status | TINYINT hoặc VARCHAR(20) |
Tiếp theo
Bài tiếp theo: Tạo Tables với DDL - học cách viết CREATE TABLE statements hoàn chỉnh với SQL Server!
