当前位置:首页 > 问答 > 正文

SQLServer 数据存储:据使用SQLServer实现数据的正确存储,sqlserver 正数应用解析

SQLServer | 数据存储:如何用SQLServer实现数据的正确存储

场景引入:当数据开始"造反"

想象一下这个场景:周一早上,你刚泡好咖啡准备开始一周的工作,突然接到业务部门的紧急电话——"系统里的客户订单数据乱了!上周五还正常的报表今天显示完全对不上!"你心跳加速,额头冒汗,赶紧登录SQLServer检查,发现某些表里的数值莫名其妙变成了负数,日期字段里混入了无效值,甚至有些关键字段干脆是NULL...

这种情况在数据存储不规范的环境中时有发生,今天我们就来聊聊如何用SQLServer实现数据的正确存储,避免这种"数据造反"的尴尬局面。

SQLServer数据类型的选择艺术

数值类型:不只是INT那么简单

很多开发者习惯性地对所有数字字段使用INT,这其实是个坏习惯,SQLServer提供了丰富的数值类型:

  • 精确数值:适合财务等需要精确计算的场景

    • TINYINT (0-255)
    • SMALLINT (-32,768到32,767)
    • INT (-2^31到2^31-1)
    • BIGINT (-2^63到2^63-1)
    • DECIMAL/NUMERIC(精度,小数位数)
  • 近似数值:适合科学计算,但可能有舍入误差

    SQLServer 数据存储:据使用SQLServer实现数据的正确存储,sqlserver 正数应用解析

    • FLOAT
    • REAL
-- 正确的数值类型选择示例
CREATE TABLE ProductPrices (
    ProductID INT PRIMARY KEY,
    -- 价格需要精确到分,使用DECIMAL
    UnitPrice DECIMAL(10, 2) NOT NULL,
    -- 库存量不会超过3万,用SMALLINT足够
    StockQuantity SMALLINT NOT NULL CHECK (StockQuantity >= 0),
    -- 产品重量可能有小数,但不需要极高精度
    WeightKg FLOAT NULL
);

字符串类型:VARCHAR不是万能的

  • CHAR(n):定长字符串,适合像身份证号这样长度固定的数据
  • VARCHAR(n):变长字符串,适合大多数文本字段
  • NVARCHAR(n):Unicode版本,支持多语言
  • TEXT/NTEXT:已过时,不推荐使用(2025年注:SQLServer 2022已移除)
  • VARCHAR(MAX)/NVARCHAR(MAX):替代TEXT的大文本类型
-- 字符串类型选择示例
CREATE TABLE Customers (
    CustomerID INT PRIMARY KEY,
    -- 中文姓名需要用NVARCHAR
    CustomerName NVARCHAR(50) NOT NULL,
    -- 身份证号固定18位,用CHAR最合适
    IDNumber CHAR(18) NOT NULL,
    -- 地址可能较长但不确定长度
    Address NVARCHAR(200) NULL,
    -- 备注可能很长,用MAX类型
    Notes NVARCHAR(MAX) NULL
);

约束:数据的"交通警察"

NOT NULL约束:杜绝NULL的混乱

NULL在SQL中是个特殊存在,它不等于空字符串或0,而是"未知",允许NULL值会增加查询复杂度并可能引发错误。

-- 不好的设计:允许NULL
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    OrderDate DATETIME NULL, -- 订单日期怎么能为空?
    CustomerID INT NULL -- 匿名订单?真的需要吗?
);
-- 改进后的设计
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    OrderDate DATETIME NOT NULL DEFAULT GETDATE(),
    CustomerID INT NOT NULL,
    -- 如果真的需要匿名订单,应该用特殊值如-1表示
    IsAnonymous BIT NOT NULL DEFAULT 0
);

CHECK约束:数据验证的第一道防线

CREATE TABLE Employees (
    EmployeeID INT PRIMARY KEY,
    EmployeeName NVARCHAR(50) NOT NULL,
    -- 年龄应该在合理范围内
    Age INT NOT NULL CHECK (Age >= 18 AND Age <= 70),
    -- 邮箱格式验证
    Email VARCHAR(100) NULL CHECK (
        Email IS NULL OR 
        Email LIKE '%@%.%' AND
        Email NOT LIKE '@%' AND
        Email NOT LIKE '%@%@%'
    ),
    -- 工资不能为负
    Salary DECIMAL(10, 2) NOT NULL CHECK (Salary >= 0)
);

表设计:关系型数据库的核心

规范化设计:避免数据冗余

-- 不规范的设计:订单表中直接存储产品信息
CREATE TABLE BadOrders (
    OrderID INT PRIMARY KEY,
    ProductName NVARCHAR(100) NOT NULL,
    ProductPrice DECIMAL(10, 2) NOT NULL,
    Quantity INT NOT NULL
    -- 如果产品价格变化,历史订单会显示错误价格
);
-- 规范化的设计
CREATE TABLE Products (
    ProductID INT PRIMARY KEY,
    ProductName NVARCHAR(100) NOT NULL,
    CurrentPrice DECIMAL(10, 2) NOT NULL
);
CREATE TABLE OrderDetails (
    OrderDetailID INT PRIMARY KEY,
    OrderID INT NOT NULL,
    ProductID INT NOT NULL,
    -- 下单时的实际价格,不受后续价格变化影响
    UnitPriceAtOrder DECIMAL(10, 2) NOT NULL,
    Quantity INT NOT NULL,
    FOREIGN KEY (ProductID) REFERENCES Products(ProductID)
);

外键关系:维护数据完整性

CREATE TABLE Departments (
    DeptID INT PRIMARY KEY,
    DeptName NVARCHAR(50) NOT NULL
);
CREATE TABLE Employees (
    EmployeeID INT PRIMARY KEY,
    EmployeeName NVARCHAR(50) NOT NULL,
    DeptID INT NOT NULL,
    -- 级联删除要慎重考虑业务需求
    FOREIGN KEY (DeptID) REFERENCES Departments(DeptID)
    ON DELETE NO ACTION -- 部门有员工时不允许删除部门
    ON UPDATE CASCADE -- 部门ID更新时自动更新员工记录
);

SQLServer特有的存储技巧

计算列:自动计算的智能字段

CREATE TABLE OrderDetails (
    OrderDetailID INT PRIMARY KEY,
    UnitPrice DECIMAL(10, 2) NOT NULL,
    Quantity INT NOT NULL,
    Discount DECIMAL(4, 2) NOT NULL DEFAULT 0,
    -- 自动计算的实际价格
    ActualPrice AS (UnitPrice * Quantity * (1 - Discount)),
    -- 持久化计算列,提高查询性能
    TotalPrice AS (UnitPrice * Quantity * (1 - Discount)) PERSISTED
);

序列对象:比IDENTITY更灵活

-- 创建序列
CREATE SEQUENCE OrderNumberSeq
    START WITH 1000
    INCREMENT BY 1
    MINVALUE 1000
    NO MAXVALUE
    NO CYCLE;
-- 使用序列
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY DEFAULT (NEXT VALUE FOR OrderNumberSeq),
    OrderDate DATETIME NOT NULL DEFAULT GETDATE()
);

实战案例:电商系统数据存储设计

让我们看一个电商系统的核心表设计示例:

-- 产品分类
CREATE TABLE Categories (
    CategoryID INT PRIMARY KEY,
    CategoryName NVARCHAR(50) NOT NULL,
    ParentCategoryID INT NULL,
    FOREIGN KEY (ParentCategoryID) REFERENCES Categories(CategoryID)
);
-- 产品表
CREATE TABLE Products (
    ProductID INT PRIMARY KEY,
    ProductName NVARCHAR(100) NOT NULL,
    CategoryID INT NOT NULL,
    UnitPrice DECIMAL(10, 2) NOT NULL CHECK (UnitPrice > 0),
    Discontinued BIT NOT NULL DEFAULT 0,
    LastStockUpdate DATETIME2 NOT NULL DEFAULT SYSDATETIME(),
    FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID)
);
-- 客户表
CREATE TABLE Customers (
    CustomerID INT PRIMARY KEY,
    CustomerName NVARCHAR(50) NOT NULL,
    Email NVARCHAR(100) NOT NULL CHECK (Email LIKE '%@%.%'),
    JoinDate DATETIME2 NOT NULL DEFAULT SYSDATETIME(),
    CreditLimit DECIMAL(12, 2) NOT NULL DEFAULT 0 CHECK (CreditLimit >= 0)
);
-- 订单表
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    CustomerID INT NOT NULL,
    OrderDate DATETIME2 NOT NULL DEFAULT SYSDATETIME(),
    RequiredDate DATETIME2 NULL CHECK (RequiredDate > OrderDate),
    ShippedDate DATETIME2 NULL,
    Status TINYINT NOT NULL DEFAULT 0, -- 0=新订单,1=处理中,2=已发货,3=已完成
    FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);
-- 订单明细
CREATE TABLE OrderDetails (
    OrderID INT NOT NULL,
    ProductID INT NOT NULL,
    UnitPrice DECIMAL(10, 2) NOT NULL CHECK (UnitPrice > 0),
    Quantity SMALLINT NOT NULL CHECK (Quantity > 0),
    Discount DECIMAL(4, 2) NOT NULL DEFAULT 0 CHECK (Discount BETWEEN 0 AND 1),
    PRIMARY KEY (OrderID, ProductID),
    FOREIGN KEY (OrderID) REFERENCES Orders(OrderID),
    FOREIGN KEY (ProductID) REFERENCES Products(ProductID)
);

常见陷阱与最佳实践

  1. 过度使用VARCHAR:能用更具体类型时就不要用VARCHAR,比如日期用DATETIME2而不是VARCHAR存储

  2. 忽略字段大小:不要对所有字符串字段都用NVARCHAR(MAX),这会影响性能

  3. 缺少默认值:为常用字段设置合理的默认值,减少应用层代码复杂度

    SQLServer 数据存储:据使用SQLServer实现数据的正确存储,sqlserver 正数应用解析

  4. 滥用触发器:触发器难以调试和维护,优先考虑约束和存储过程

  5. 忽视索引设计:良好的索引设计是性能关键,但不在本文讨论范围内

正确的数据存储设计就像建筑的地基,虽然用户看不见,但决定了整个系统的稳定性和可靠性,通过合理选择数据类型、设置约束、规范化设计和利用SQLServer特有功能,你可以构建出既健壮又高效的数据存储结构,今天多花一小时设计表结构,未来可能节省上百小时的故障排查和数据修复时间。

下次当你设计新的SQLServer表时,不妨问问自己:这个设计能防止数据"造反"吗?如果答案是肯定的,那么你已经走在正确的道路上了。

发表评论