如何在SQL Server中使用栈/队列作数据类型?能否在表列存储栈型食品ID?
在SQL Server中使用栈/队列数据类型及订单食品ID存储方案分析
1. SQL Server里怎么实现栈或队列?
SQL Server本身并没有原生的栈、队列数据类型,但咱们可以用几种方式模拟出来,满足不同场景的需求:
- 用普通表模拟栈:栈是后进先出(LIFO)的逻辑,咱们可以建一个带自增标识列的表,比如:
入栈就是直接CREATE TABLE StackDemo ( StackId INT IDENTITY(1,1) PRIMARY KEY, StackValue VARCHAR(100) NOT NULL );INSERT新记录(最新插入的就是栈顶),出栈就取最后插入的那条记录删除并返回:-- 入栈 INSERT INTO StackDemo (StackValue) VALUES ('A'), ('B'); -- 出栈(取栈顶值) DELETE TOP(1) FROM StackDemo OUTPUT deleted.StackValue ORDER BY StackId DESC; - 用普通表模拟队列:队列是先进先出(FIFO),逻辑类似,出队时按自增列升序取第一条即可:
-- 出队 DELETE TOP(1) FROM QueueDemo OUTPUT deleted.QueueValue ORDER BY QueueId ASC; - 高性能场景用内存优化表:如果你的栈/队列需要处理高并发、低延迟的请求,可以用内存优化表,它比普通表的读写速度快很多,结合自增列或者序列来维护顺序就行。
- CLR自定义类型(不推荐):早年有人用.NET写CLR代码自定义栈/队列类型,注册到SQL Server里当列类型用,但现在这种方式维护成本高,还需要额外权限,基本被更简单的方案替代了。
2. 你的订单表存储方案可行吗?
先直接给结论:这种把多个食品ID塞进orderedItems列(比如存成stack(1,4,5,7))的方案,在SQL Server里技术上能强行实现,但严重违反关系型数据库的设计规范,绝对不推荐用。
为什么这个方案不行?
- 破坏第一范式:关系型数据库要求列的值必须是原子的,也就是一个列只能存一个值。你把多个食品ID塞到一个列里,后续想查某个食品被多少订单买过、想修改订单里的某一个食品,都得先拆分这个列的字符串,不仅麻烦,性能还极差。
- 数据完整性没法保证:你没法用外键约束来确保这个列里的ID都是
tbl_foods里存在的有效ID,很容易出现写错ID的情况,导致数据不一致。 - 查询效率极低:比如你想找所有买了食品ID=1的订单,只能用
LIKE '%1%'这种模糊查询,完全用不上索引,数据量一大,查询慢到让你崩溃。
正确的做法:用多对多关联表
一个订单对应多个食品,一个食品对应多个订单,这是典型的多对多关系,咱们需要建一个中间关联表,比如tbl_order_items:
CREATE TABLE tbl_order_items ( OrderItemId INT IDENTITY(1,1) PRIMARY KEY, OrderId INT NOT NULL FOREIGN KEY REFERENCES tbl_order(orderID), FoodId INT NOT NULL FOREIGN KEY REFERENCES tbl_foods(foodID), Quantity INT NOT NULL DEFAULT 1, -- 可以加数量字段,符合实际业务 UNIQUE(OrderId, FoodId) -- 可选,防止同一个订单重复加同一个食品 );
这么设计的好处太多了:
- 完全符合数据库范式,数据结构清晰,谁看都懂。
- 外键约束能保证
OrderId和FoodId都是有效的,不会出现无效数据。 - 查询、更新、删除都能利用索引,速度飞快。比如查订单1的所有食品:
SELECT f.foodname FROM tbl_order_items oi JOIN tbl_foods f ON oi.FoodId = f.foodID WHERE oi.OrderId = 1; - 扩展性强,后续想加单价、商品备注这些字段,直接加在中间表里就行。
实在要存集合?退而求其次用JSON/XML
如果因为某些特殊业务限制,必须把多个ID塞到一个列里,那也别存成自定义的stack格式,用SQL Server支持的JSON类型会好很多。比如把食品ID存成[1,4,5,7]:
-- 修改列类型为JSON(先确保列里没数据,或者转成JSON格式) ALTER TABLE tbl_order ALTER COLUMN orderedItems NVARCHAR(MAX); ALTER TABLE tbl_order ADD CONSTRAINT CHK_orderedItems_IsJson CHECK (ISJSON(orderedItems) = 1); -- 插入数据 INSERT INTO tbl_order (orderID, orderedItems, date, customerID) VALUES (1, '[1,4,5,7]', GETDATE(), 1); -- 查询订单1的所有食品 SELECT f.foodname FROM tbl_order o CROSS APPLY OPENJSON(o.orderedItems) WITH (FoodId INT '$') AS oi JOIN tbl_foods f ON oi.FoodId = f.foodID WHERE o.orderID = 1;
但还是得强调,这只是权宜之计,长期来看关联表才是最合理的选择。
内容的提问来源于stack exchange,提问作者Saeed Areffard
相关产品推荐
相关产品推荐

