ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AdventureWorks示例数据库完全指南:从PDF到SQL Server实战

AdventureWorks示例数据库完全指南:从PDF到SQL Server实战 简介AdventureWorks示例数据库说明文档面向SQL Server初学者与备考人员系统梳理了微软官方示例数据库的体系结构与应用场景。文档围绕虚构的Adventure Works Cycles公司业务按模块解析OLTP示例库、AdventureWorksDW数据仓库以及Analysis Services数据库的设计思路涵盖客户类型划分、产品分类、销售与库存管理等核心表结构并用表格对比不同表的数据存储逻辑便于读者快速掌握示例库的层次关系。资源为单个PDF文件体积仅89KB内容紧凑、可直接查阅适合考前复习或项目开发前的快速参考。已有319人学习使用被广泛用于SQL Server功能演示与数据库设计学习。通过这份说明读者可以理解AdventureWorks各示例库的用途与彼此关联明确Customer、Product、SalesOrderHeader等关键表的业务含义为深入学习SQL Server联机丛书示例及开展数据库开发打下基础。1. 从一份 PDF 认识 AdventureWorks它为什么是 SQL Server 学习路上的“标配”如果你是第一次接触 AdventureWorks示例数据库说明.pdf先别急着把它当成又一份躺在硬盘里的文档。对于所有在 SQL Server、Azure SQL Database 上练手的人来说AdventureWorks 就是一套“玩具数据库”但它不是简笔画——它模拟了一家真实的自行车销售公司的完整业务包含了从销售订单、客户信息、产品库存到财务、人事、采购的几十个表和几百个视图、存储过程。这份 PDF 的价值不在于它把表结构罗列了一遍而在于它给了你一张完整的地图表跟表之间怎么挂接、业务字段怎么设计、一个订单在库里是怎么从 Insert 一路走完的。我遇到不少开发者和 DBA手上有这个库但只会跑几条 SELECT根本不敢动里面的存储过程和函数就是因为没把这张“地图”读透。这篇文章就是带你把它读透再敢上手拆。2. 示例数据库说明里的“业务地图”先从三个核心架构点看起2.1 为什么是自行车公司理解了业务模型表结构就不难背了AdventureWorks 的数据库结构并非随意堆出来的。它的业务模型是一个跨国自行车制造和销售企业有生产、采购、销售、售后、财务这些典型制造零售链路。说明文档往往会按业务域把表分组Person、Sales、Production、Purchasing、Inventory、HumanResources、Finance 等。你去看文档时不要孤立地看某一张表而是看这一组表服务哪个业务节点。比如 Sales 域里最重要的三张表SalesOrderHeader、SalesOrderDetail、Customer。Header 记录一张订单的抬头信息订单号、下单时间、客户 ID、总金额Detail 记录这张订单里的每一个产品行型号、数量、单价Customer 记录客户主数据。三张表靠 SalesOrderID 和 CustomerID 串起来。看懂这一条线你就理解了一个“一对多”关系的标准实现Header 是“一”Detail 是“多”。而 Customer 和 SalesOrderHeader 又是一对多。文档里标注的每个外键其实都在讲这种业务线上的上下游关系。2.2 从说明文档里提炼“层级表”和“维度表”设计上的最大亮点AdventureWorks 最值得反复琢磨的两个设计点一是 Employee 表的自引用结构ManagerID 指向自己的 EmployeeID二是 Product 表的类目层级ProductCategory、ProductSubcategory、Product。这俩结构在一般的教学库中很少见但真实业务里到处都有。Employee 自引用如果你在文档里看到 ManagerID 这一列就应该意识到一个人既是被管理的人也可能是别人的经理。查询某个员工的上级或下级实际上是在同一张表上做自连接。很多新手一看这种表就懵但文档里通常会用几个示例查询来演示“找层级上司”或“找所有下属”。你照着跑一遍就会理解自连接的本质把同一张表从逻辑上拆成两份像普通的两表 Join 一样处理。Product 类目层级ProductSubcategory 表里有 ProductCategoryID 外键Product 表里又有 ProductSubcategoryID 外键。从大类到小类再到具体产品是一条两层的“雪花型”路径。明细表里只存最底层的 ProductID靠嵌套 Join 才能统计出一个大类下的销售额。这类中间层表的设计思路在你以后设计商品、分类、文章、菜单的时候都能直接复用。2.3 文档里的“命名规范”本身就是一条经验看懂列名后缀少踩坑看 AdventureWorks 示例数据库说明时注意它的命名规律。ID 列通常是“表名ID”比如 ProductID、SalesOrderID、CustomerID日期列经常叫 OrderDate、DueDate、ShipDate数量列叫 Quantity金额列叫 LineTotal、SubTotal、TaxAmt。这套规则虽然简单但对写查询非常友好你不需要猜某个字段是干嘛的看了名字就知道。例如Document 表里有 DocumentNode 和 DocumentLevel 这两个特殊列用于层次结构说明文档里通常会强调这类列用法不熟悉时会误把它们当普通字符串存数据。我一般会先看文档末尾的关系图再回头去理解命名。先看图再对字段名比从第一页顺序翻到最后一页要高效得多。3. 把 AdventureWorks 装上你的电脑两种落地路径与必备参数3.1 官方包还原法与文件名、路径这些“隐形参数”的处理无论你手里是 PDF 文档还是数据库备份文件最标准的落地方式是拿到一份 AdventureWorks 的备份通常是 .bak 或 .bacpac然后通过 RESTORE 命令还原到本地 SQL Server。还原前必须确认备份文件的版本和路径。下面这段是典型的还原命令很多环境里只要改掉路径就能直接跑通USE [master]; GO RESTORE DATABASE [AdventureWorks2022] FROM DISK NC:\DatabaseBackup\AdventureWorks2022.bak WITH MOVE NAdventureWorks2022_Data TO NC:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA\AdventureWorks2022.mdf, MOVE NAdventureWorks2022_Log TO NC:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA\AdventureWorks2022_log.ldf, REPLACE, RECOVERY;这条命令的“隐形参数”主要在 WHERE 之前的两个 MOVE 子句里。备份文件内部的逻辑文件名和硬盘上实际物理文件名可能不一致所以要用RESTORE FILELISTONLY FROM DISK N...bak先查看一下逻辑名把上面两个 MOVE 里的逻辑名替换成真实值然后再执行还原。另外REPLACE参数负责覆盖同名数据库RECOVERY表示还原完成后数据库直接可读写。如果只是想看数据不强求恢复可改成NORECOVERY但那只是在多文件还原时用的场景单文件还原一般不这么写。实际操作中最容易翻车的点就是路径不存在或权限不足尤其是把 .mdf 和 .ldf 放到自定义目录时。建议统一放到 SQL Server 默认数据目录里避免权限坑。3.2 用 PowerShell 内存中创建数据库的方式适合不想污染实例的验证环境如果你只是为了跑几个查询不想真的把一份几百兆的库固化成正式实例的一部分常见做法是借助 PowerShell 在一台独立的容器实例里完成安装和还原。很多开发者现在直接用 Docker 拉一个 SQL Server 容器再通过管道把备份文件传进去。示例如下# 拉取 SQL Server 2022 Linux 容器镜像并命名为 awdb docker run -e ACCEPT_EULAY -e MSSQL_SA_PASSWORDYourStrongPassword123 \ -p 1433:1433 --name awdb -d mcr.microsoft.com/mssql/server:2022-latest这是把 SQL Server 放到容器里。等容器状态变成 healthy 后再在宿主机上执行docker cp把 .bak 文件丢进容器然后用sqlcmd执行RESTORE DATABASE。这样做的好处是用完直接docker rm删掉不遗留环境坏处是每次都要重新拉镜像磁盘占用大。如果你已经有本机实例用 3.1 的 RESTORE 更省事。需要说明的是AdventureWorks 还可能有 MSTDC 等离线安装包中的项目这种情况下直接执行安装向导就可以了。但如果是在 Windows 上用安装向导装经常遇到的坑是安装顺序先装 SQL Server 再装示例库比倒过来顺利得多。另外要注意检查数据库兼容级别比如ALTER DATABASE AdventureWorks2022 SET COMPATIBILITY_LEVEL 160;这样设置为 SQL Server 2022 所对应的兼容级。如果还原的是旧版库如 AdventureWorks2019在新实例上使用前可以顺手升级。3.3 还原成功后怎么自检一分钟判断数据库状态是否正常还原完成不代表万事大吉。第一件要做的事是查“数据库是否真的处于 ONLINE 且 READ_ONLY0”USE [AdventureWorks2022]; GO SELECT name, state_desc, recovery_model_desc, compatibility_level FROM sys.databases WHERE name NAdventureWorks2022; GO如果 state_desc 是 ONLINE再顺手查几个关键表的行数确认表和视图真的带数据。不是所有版本的 AdventureWorks 都包含物联网和地理信息那些扩展模块但如果 Sales 和 Production 下查不到 500 条记录大概率还原或安装版本不对。参数里要留意 recovery_model_desc一般建议设成 SIMPLE这样日志文件不会飞快膨胀后续做测试改坏了一大片数据还能靠还原备份恢复不至于卡在“事务日志满”这种地狱级问题上。安装阶段就多花两分钟把这些基础状态确认清楚比做了一堆练习发现库有问题再返工要划算得多。4. 照着 PDF 查数据从写第一条 JOIN 到看懂执行计划4.1 用说明文档当“外键词典”写多表关联时先查表关系一旦库里数据可读下一步就是拿文档来当字典。很多初学者的死法是在错的条件上 Join。这里给出一个典型的“订单 客户 产品 类别”四表关联查询这是文档中业务线上最常出现的走法SELECT TOP 20 c.CustomerID, p.FirstName ISNULL(p.MiddleName, ) p.LastName AS FullName, soh.SalesOrderID, sod.ProductID, pr.Name AS ProductName, sod.OrderQty, sod.LineTotal FROM Sales.SalesOrderHeader AS soh JOIN Sales.Customer AS c ON soh.CustomerID c.CustomerID JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID sod.SalesOrderID JOIN Production.Product AS pr ON sod.ProductID pr.ProductID JOIN Person.Person AS p ON c.PersonID p.BusinessEntityID ORDER BY soh.OrderDate DESC;这段查询的关键是把文档里关系图上的外键一一“翻译”成了 ON 条件。比如 Person 表和 Customer 表的关联是c.PersonID p.BusinessEntityID这个对应关系不看文档你可能根本不知道。而把SalesOrderHeader看作事实表Customer、Person、Product 都是它的维度表这条语句就能向“星型查询”靠拢。注意ISNULL和空字符串拼接处中间名如果为 NULL不处理结果就是 NULL这个细节在工作里最容易漏。参数层面我们可以查出来的字段不多不必在 SELECT 里写*。TOP 20 只是先看一眼数据量不是固定值。LineTotal其实可以由OrderQty * UnitPrice算出来但库里已经做了冗余可以直接用省一次表达式。4.2 用视图把“PDF 里的业务查询”变成可复用对象以视图为中间层AdventureWorks 自带了几十个视图说明文档通常也会收录它们的定义。我见过不少团队拿到示例库后直接把视图删掉改成写底层表查询——这样做特别可惜。视图是“中间层”既能封装复杂逻辑又能限定访问粒度。例如系统自带的Sales.vIndividualCustomer把 Person、Customer、SalesOrderHeader 等一堆表的关联浓缩成了一份“客户维度”。跑查询时直接用SELECT TOP 100 CustomerID, Title, FirstName, LastName, PhoneNumberType, PhoneNumber FROM Sales.vIndividualCustomer ORDER BY CustomerID;重点是这个视图和底层表不是一对一的关系。视图背后的关联逻辑已经被封装业务代码不感知。以后底层表结构变我们可以只改视图不碰调用方。在个人学习环境里这也是一种“练内功”的好方式把一条复杂查询封装成视图再对着文档思考“为什么让视图暴露这几个列、不暴露另外几个”这正是做数据分析或数据平台时搭建中间层的日常。4.3 让执行计划告诉你文档没写的坑索引缺失和隐式转换写查询之后要养成看执行计划肌肉记忆。AdventureWorks 官方库的索引设计相对完备但有些业务场景依然会绕开索引特别是字符串与数字比较这种隐式转换。下面这个例子里如果SalesOrderNumber在库中是 nvarchar却拿 123去匹配就会导致索引失效并产生 CONVERT_IMPLICIT 警告SET SHOWPLAN_ALL ON; GO SELECT SalesOrderID, SalesOrderNumber FROM Sales.SalesOrderHeader WHERE SalesOrderNumber 43785; GO SET SHOWPLAN_ALL OFF;虽然这个例子里 SalesOrderNumber 通常不是数字类型这里只是为了演示。更常见的坑是日期字段把OrderDate写成OrderDate 2013-05-01时如果列类型是 datetime筛选没有问题如果后台新版本把列变成 datetime2字符串可能还要再转换一次。我一般会在排查慢查询时直接看执行计划里有没有黄色感叹号。每回遇到“明明有索引还全表扫描”的问题十有八九就是隐式转换。先把 SET SHOWPLAN_ALL 开起来再看索引建议比盲猜快得多。而这个技巧说穿了就是从文档里反复看字段类型得来的。5. 避坑还原与查询 AdventureWorks 的 6 个高频“翻车”现场5.1 坑备份文件确定存在但 RESTORE 一直报 Could not open File现象执行RESTORE DATABASESQL Server 明确报错说指定的路径无法打开或找不到文件但你在 Windows 资源管理器里能看到 .bak 文件。原因SQL Server 服务账号对这些文件/文件夹没有访问权限或者你把备份文件放在了类似C:\Users\某用户\Downloads的系统用户目录下。SQL Server 服务进程跑在 NETWORK SERVICE 或专用账号下进不了这个目录。解决把 .bak 挪到 SQL Server 实例数据目录或共享目录比如C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA下再执行还原。手动右键“属性—安全”给服务账号加权限有时也能解决但路径尽量走默认目录。5.2 坑备份文件是 2019 版本还原到 2017 实例直接报版本不兼容现象还原时提示“数据库备份在版本 156 的服务器上创建该服务器支持版本 150 及更低版本”。原因备份文件来自较新的 SQL Server 引擎版本旧实例无法“降级”还原。解决换一台高版本实例或者用低版本库重新备份而不要强行还原。还有种侧面方案把原始库导出成 bacpac再在新实例上导入但 bacpac 导入时也可能因兼容级别不支持而失败。为了避免这类问题下载离线包前先确认对应 SQL Server 大版本再去找匹配的备份文件版本。5.3 坑还原成功但业务账号无权限应用程序连不上现象SSMS 里能查数据但一个只给了 public 角色的账号登录后看不到任何表或一张表都查不了。原因AdventureWorks 的 schema 和用户权限不是自动绑定的。默认登录只是 public没有访问Sales、Production这些 schema 的权限。解决给业务账号最少按需授权例如USE [AdventureWorks2022]; CREATE USER [app_reader_server] FOR LOGIN [app_reader]; ALTER ROLE db_datareader ADD MEMBER [app_reader_server];如果确实需要写操作再加db_datawriter但生产环境不要这么干。在测试环境里最直接的办法是把登录加入db_owner但被审计时这就是一条“红线”。权限问题会伪装成“查询不到数据”或“对象名无效”排查时优先把登录名、数据库用户名和角色成员关系连在一起看。5.4 坑文档里的表都认识但数据量太大过滤老是把范围选错现象按OrderDate 2011-01-01过滤返回行数少得离谱或直接空结果以为数据缺失。原因AdventureWorks 各版本的财年数据范围不同。比如旧版本主要模拟 2001 到 2004 财年新版本可能把时间线拉到了 2011 到 2014 年。年份写错查询不到是正常现象。解决先执行SELECT MIN(OrderDate), MAX(OrderDate) FROM Sales.SalesOrderHeader;快速确认数据边界再调整查询的时间范围。不要一上来就套网上三年前的语句库版本变了时间轴就变了。其它表类似先做一次MIN/MAX探路再写业务查询能省很多冤枉时间。5.5 坑还原之后把某个表改坏了没有后悔药可用现象想练 UPDATE 或 DELETE结果 WHERE 过滤没写整表被清随后意识到收不回。原因SQL Server 默认没有“后悔药”机制。除非开了时点还原或事务早已经被显式事务包裹住否则已经提交的删除不可逆。解决做破坏性操作前先开启显式事务。最稳妥的写法是BEGIN TRANSACTION; DELETE FROM Sales.SalesOrderDetail WHERE OrderQty 1; -- 检查影响行数后再决定提交还是回滚 ROLLBACK TRANSACTION;这里的 ROLLBACK 就是后悔药。练习时一定要养成“先 BEGIN再操作最后确定 ROLLBACK 或 COMMIT”的肌肉记忆。即使真的误伤了大量数据只要操作在一个事务里回滚是唯一出路。5.6 坑安装了多个版本连接串接错实例现象本地既有 SQL Server 2019 又有 2022用 SSMS 连进去后看到的不是 AdventureWorks 的完整数据或根本找不到该数据库。原因连接到了默认实例但备份文件还原到了命名实例或两个实例共用端口 1433 导致串连。解决连接时明确写服务器名\实例名或者用 sqlcmd 指定-S 服务器名\实例名。我一般会建议在连接串里显式加上TrustServerCertificateTrue仅在开发环境避免加密证书问题干扰。如果在容器里还要确认端口映射是否指向了正确的实例。多实例环境下的“找不到库”大多是连接错而不是库没装好。6. 把这份 PDF 变成你的“内功修炼册”索引诊断与基线校验Grinding 到这里你已经能把 AdventureWorks 正常跑起来。最后一章我想给你一个能长期用的技巧把文档中的表清单当成一个天然的性能测试台对关键查询做“基线校验”。具体做法是找一条经常要用的多表 Join比如第二节里的订单客户产品关联把查询加上SET STATISTICS IO, TIME ON跑一遍记录逻辑读、扫描次数和 CPU 时间这就是基线。接着尝试修改索引或重写语句比较前后差异这样你能直观感受到“索引覆盖”和“隐式转换”到底带来多少差距。另一个做法是拿 AdventureWorks 当“调参试验田”。先看文档里给出的索引定义再手动删掉一两个非聚集索引重新跑同一查询。观察执行计划从 Nested Loop 变成 Hash Match再从 Hash Match 变回 Nested Loop比看任何理论的书都深刻。你甚至可以故意造一条性能极差的查询让执行计划推荐你建索引然后自己评估推荐是否合理。这种“故意制造问题—诊断—调整”的循环就是 DBA 和高级开发真正吃饭的手艺。我自己的习惯是建一个_PracticeDb数据库把 AdventureWorks 的文档放在里面当备注表使用把常用查询的脚本存成存储过程再附上“建这条查询时从文档第几页对应关系图”的注释。这样即使一个月后完全想不起来当初为什么这么写打开文档一看就能对上。后面我换了公司也一直把这个习惯带过去。每次有人来问“这个字段外键到底指向哪”我就把 AdventureWorks 的关系图拿给他们当入门例子讲三句话就能解决。最后再说一句希望帮到你——先把库装好再把文档里的关系图读懂AdventureWorks 就会从一份“PDF 说明”变成你最有用的练功房。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进