简介: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 = N'C:\DatabaseBackup\AdventureWorks2022.bak' WITH MOVE N'AdventureWorks2022_Data' TO N'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA\AdventureWorks2022.mdf', MOVE N'AdventureWorks2022_Log' TO N'C:\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_EULA=Y" -e "MSSQL_SA_PASSWORD=YourStrongPassword123" \ -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_ONLY=0”:
USE [AdventureWorks2022]; GO SELECT name, state_desc, recovery_model_desc, compatibility_level FROM sys.databases WHERE name = N'AdventureWorks2022'; 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 DATABASE,SQL 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 服务器名\实例名。我一般会建议在连接串里显式加上TrustServerCertificate=True(仅在开发环境),避免加密证书问题干扰。如果在容器里,还要确认端口映射是否指向了正确的实例。多实例环境下的“找不到库”大多是连接错,而不是库没装好。
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 说明”变成你最有用的练功房。
本文还有配套的精品资源,点击获取