文章目录
- 前言
- 回顾
- 介绍性问题
- 7.产品名称中包含“queso”的产品
- 8.运往法国或比利时的订单
- 9.运往拉丁美洲任何国家的订单
- 10.员工,按年龄的顺序排列
- 11.让DateTime列仅显示Date
- 12.员工全名
- 13.每个订单的详细金额
- 14.有多少客户?
- 15.第一个订单是什么时候?
- 16.已有客户的国家
- 17.统计每个ContactTitle的数量
- 18.产品与相关供应商名
- 答案
- 7.产品名称中包含“queso”的产品
- 8.运往法国或比利时的订单
- 9.运往拉丁美洲任何国家的订单
- 10.员工,按年龄的顺序排列
- 11.让DateTime列仅显示Date
- 12.员工全名
- 13.每个订单的详细金额
- 14.有多少客户?
- 15.第一个订单是什么时候?
- 16.已有客户的国家
- 17.统计每个ContactTitle的数量
- 18.产品与相关供应商名
- 未完待续
前言
该系列教程,将会从实际问题出发,边干边学,逐步深入讲解SQL的各方面知识。
你需要完成所有的问题吗?绝对不是。介绍性的问题相当简单,所以你可以直接跳过到“中级问题”部分。如果你不是初学者,但不确定应该从哪里开始,请在“入门问题”部分看看问题和预期结果,并确保你理解这些概念。如果已经理解了这些概念,请开始阅读“中级问题”部分。
你想从这本书中复制代码并在你的服务器上运行?我建议你手动输入,而不是复制粘贴。为什么要去麻烦地重新打字呢?科学表明,打字的行为会在你的脑中留下更深刻的印象。当你只是复制和粘贴时,代码只是直接从你电脑里的一个窗口转到另一个窗口,而不会给你留下多少印象。但是当你把它打出来时,你必须集中精力,这非常有助于保留信息。
一旦你完成了所有的问题,将拥有一些在数据分析和高级Select语句使用方面非常有用的技能。当然,这并不是SQL的全部内容。还有修改数据(更新、插入、删除)、DDL(数据定义语言,即如何创建和修改数据库对象)、编程(如存储过程)和许多其他主题。
该系列教程中,只涉及到了使用Select语句检索数据的问题,这几乎是所有其他数据库主题的基础开端。
回顾
上篇文章👉《【SQL边干边学系列】01介绍性问题》 讨论了部分介绍性问题,这篇我们接着讨论剩余的介绍性问题。
介绍性问题
7.产品名称中包含“queso”的产品
在产品表(products)中,我们希望看到那些产品名称(ProductName)中包含字符串“queso”的产品的产品标识(ProductID)和产品名称(ProductName)。
-- 预期结果
ProductID ProductName
----------- ----------------------------------------
11 Queso Cabrales
12 Queso Manchego La Pastora
(2 row(s) affected)
提示:在前面的一个问题中,我们正在寻找精确的匹配(过滤器与字段中的值精确匹配)。在这里,我们正在寻找产品名称字段中包含“queso”的那些行。在答案中使用带有通配符的“like”运算符。
8.运往法国或比利时的订单
看看订单表(Orders),有一个列叫做目的地(ShipCountry)。编写一个查询,显示目的地为法国(France)或比利时(Belgium)的订单的订单ID(OrderID)、客户ID(CustomerID)和目的地(ShipCountry)。
-- 预期结果
OrderID CustomerID ShipCountry
----------- ---------- ---------------
10248 VINET France
10251 VICTE France
10252 SUPRD Belgium
10265 BLONP France
.......
11043 SPECD France
11051 LAMAI France
11076 BONAP France
(96 row(s) affected)
提示: 在where子句中,使用Or将过滤器连接起来。
9.运往拉丁美洲任何国家的订单
现在,我们想展示来自任何一个拉丁美洲国家的所有订单。拉丁美洲国家列表:巴西(Brazil),墨西哥(Mexico),阿根廷(Argentina)和委内瑞拉(Venezuela)。
不能再使用"Or"语句,它会变得太复杂,应该使用“In”语句。
-- 预期结果
OrderID CustomerID ShipCountry
----------- ---------- ---------------
10250 HANAR Brazil
10253 HANAR Brazil
10256 WELLI Brazil
......
11071 LILAS Venezuela
11073 PERIC Mexico
(173 row(s) affected)
10.员工,按年龄的顺序排列
对于“员工”表(employees)中的所有员工,请显示FirstName、LastName、头衔(Title)和出生日期(BirthDate)。按出生日期排序,所以我们先看到最年长的员工。
-- 预期结果
FirstName LastName Title BirthDate
---------- -------------------- ------------------------------ -----------------------
Margaret Peacock Sales Representative 1955-09-19 00:00:00.000
Nancy Davolio Sales Representative 1966-12-08 00:00:00.000
Andrew Fuller Vice President, Sales 1970-02-19 00:00:00.000
Steven Buchanan Sales Manager 1973-03-04 00:00:00.000
Laura Callahan Inside Sales Coordinator 1976-01-09 00:00:00.000
Robert King Sales Representative 1978-05-29 00:00:00.000
Michael Suyama Sales Representative 1981-07-02 00:00:00.000
Janet Leverling Sales Representative 1981-08-30 00:00:00.000
Anne Dodsworth Sales Representative 1984-01-27 00:00:00.000
(9 row(s) affected)
11.让DateTime列仅显示Date
在上面的查询的输出中,按出生日期的顺序显示员工,展示了BirthDate字段(Datetime类型),我们只想显示Date,不想显示Time。
--- 预期结果
FirstName LastName Title DateOnlyBirthDate
---------- -------------------- ------------------------------ -----------------
Margaret Peacock Sales Representative 1955-09-19
Nancy Davolio Sales Representative 1966-12-08
Andrew Fuller Vice President, Sales 1970-02-19
Steven Buchanan Sales Manager 1973-03-04
Laura Callahan Inside Sales Coordinator 1976-01-09
Robert King Sales Representative 1978-05-29
Michael Suyama Sales Representative 1981-07-02
Janet Leverling Sales Representative 1981-08-30
Anne Dodsworth Sales Representative 1984-01-27
(9 row(s) affected)
提示:使用“转换函数”可以将“BirthDate”列转换为“Date”列
12.员工全名
显示Employees表中的FirstName和LastName列,然后创建一个名为FullName的新列,显示FirstName和LastName连接在一起的效果,中间有一个空格。
-- 预期结果
FirstName LastName FullName
---------- -------------------- -------------------------------
Nancy Davolio Nancy Davolio
Andrew Fuller Andrew Fuller
Janet Leverling Janet Leverling
Margaret Peacock Margaret Peacock
Steven Buchanan Steven Buchanan
Michael Suyama Michael Suyama
Robert King Robert King
Laura Callahan Laura Callahan
Anne Dodsworth Anne Dodsworth
(9 row(s) affected)
13.每个订单的详细金额
在订单详细信息表(OrderDetails)中,我们有字段的单位价格(UnitPrice)和数量(Quantity)。创建一个新的字段,总价格(TotalPrice),它将这两个值相乘。我们现在将忽略折扣字段。此外,还可以显示订单ID(OrderID)、产品ID(ProductID)、单位价格(UnitPrice)和数量(Quantity)。按订单ID(OrderID)和产品标识(ProductID)排序。
-- 预期结果
OrderID ProductID UnitPrice Quantity TotalPrice
----------- ----------- --------------------- -------- ---------------------
10248 11 14.00 12 168.00
10248 42 9.80 10 98.00
10248 72 34.80 5 174.00
...
11077 75 7.75 4 31.00
11077 77 13.00 2 26.00
(2155 row(s) affected)
14.有多少客户?
我们的客户表(Customers)中有多少个客户?只显示一个值,并且不要依赖于返回结果最后附带的统计计数。
-- 预期结果
TotalCustomers
--------------
91
(1 row(s) affected)
提示:为了获得客户的总数,我们需要使用所谓的聚合函数。
15.第一个订单是什么时候?
显示在订单表(Orders)中创建的第一个订单的日期。
-- 预期结果
FirstOrder
-----------------------
2014-07-04 08:00:00.000
(1 row(s) affected)
提示:有一个叫做Min的聚合函数来解决这个问题。
16.已有客户的国家
显示该公司拥有客户的国家名单。
-- 预期结果
Country
---------------
Argentina
Austria
Belgium
Brazil
Canada
Denmark
Finland
France
Germany
Ireland
Italy
Mexico
Norway
Poland
Portugal
Spain
Sweden
Switzerland
UK
USA
Venezuela
(21 row(s) affected)
提示:需要使用 Group By 子句。
17.统计每个ContactTitle的数量
显示“Customers”表中“ContactTitles”列的所有不同值,还包括每个值的统计数。
-- 预期结果
ContactTitle TotalContactTitle
------------------------------ -----------------
Owner 17
Sales Representative 17
Marketing Manager 12
Sales Manager 11
Accounting Manager 10
Sales Associate 7
Marketing Assistant 6
Sales Agent 5
Assistant Sales Agent 2
Order Administrator 2
Assistant Sales Representative 1
Owner/Marketing Assistant 1
(12 row(s) affected)
提示:需要使用分组、聚合函数和别名。
18.产品与相关供应商名
我们想展示每个产品的相关供应商。显示供应商的ProductID、ProductName和CompanyName,按ProductID排序。
这个问题将介绍一个新的概念,即SQL中的Join子句。Join子句用于以逻辑的方式将两个或多个关系数据库表连接在一起。
-- 预期结果
ProductID ProductName Supplier
----------- ---------------------------------------- ------------------------------
1 Chai Exotic Liquids
2 Chang Exotic Liquids
3 Aniseed Syrup Exotic Liquids
...
74 Longlife Tofu Tokyo Traders
75 Rhönbräu Klosterbier Plutzer Lebensmittelgroßmärkte AG
76 Lakkalikööri Karkki Oy
77 Original Frankfurter grüne Soße Plutzer Lebensmittelgroßmärkte AG
(77 row(s) affected)
答案
7.产品名称中包含“queso”的产品
答案
Select
ProductID
,ProductName
From Products
Where
ProductName like '%queso%'
讨论
“Like”运算符总是与通配符一起使用,例如百分比符号(%),它可以替换任意数量的字符。
请注意,即使搜索字符串使用了一个小写的“q”和一个类似的子句ProductName like '%queso%'
,得到的结果也可能包含大写Q。例如:
Queso Cabrales
Queso Manchego La Pastora
这是因为SQL Server的默认安装不区分大小写,尽管也可以安装为区分大小写。
8.运往法国或比利时的订单
答案
Select
OrderID
,CustomerID
,ShipCountry
From Orders
where
ShipCountry = 'France'
or ShipCountry = 'Belgium'
讨论
这是一个非常简单的例子,但在许多情况下,将有多个where子句,并用“Or”和“And”连接起来。
在这种情况下,另一种替代方法是使用“In”运算符。
9.运往拉丁美洲任何国家的订单
答案
Select
OrderID
,CustomerID
,ShipCountry
From Orders
where
ShipCountry in
(
'Brazil'
,'Mexico'
,'Argentina'
,'Venezuela'
)
讨论
在编写SQL时,像这样使用“In”语句是一个非常常见的场景,通常把值放在单独的行上,以便让它更容易阅读、理解和修改。
10.员工,按年龄的顺序排列
答案
Select
FirstName
,LastName
,Title
,BirthDate
From Employees
Order By Birthdate
讨论
这是一个按顺序排列的简单例子。
默认情况下,SQL Server按升序排序。要按降序排列,请使用desc
关键字:
Select
FirstName
,LastName
,Title
,BirthDate
From Employees
Order By Birthdate desc -- desc代表降序
11.让DateTime列仅显示Date
答案
Select
FirstName
,LastName
,Title
,DateOnlyBirthDate = convert(date, BirthDate)
From Employees
Order By Birthdate
讨论
我们在这里使用的是计算列(相对于返回列来说)。在这种情况下,我们正在使用一个函数来转换返回的数据类型。
请注意,我们已经为计算列添加了一个名称(DateOnlyBirthDate),这被称为别名。
DateOnlyBirthDate = convert(date, BirthDate)
如果你没有指定列别名,那么将得到一个空的列头,这非常不利于阅读。
12.员工全名
答案
Select
FirstName
,LastName
,FullName = FirstName + ' ' + LastName
From Employees
讨论
这是已计算的列的另一个示例。在这种情况下,我们不是对一个字段使用一个函数,而是连接两个字段。
13.每个订单的详细金额
答案
Select
OrderID
,ProductID
,UnitPrice
,Quantity
,TotalPrice = UnitPrice * Quantity
From OrderDetails
Order by
OrderID
,ProductID
讨论
这里我们有另一个计算列的例子,这次使用算术算子“*”来进行乘法。
当然,你也可以使用“as”语句:
Select
OrderID
,ProductID
,UnitPrice
,Quantity
,UnitPrice * Quantity as TotalPrice -- Alias using "as"
From OrderDetails
Order by
OrderID
,ProductID
14.有多少客户?
答案
Select
TotalCustomers = count(*)
from Customers
15.第一个订单是什么时候?
答案
Select
FirstOrder = min(OrderDate)
From Orders
16.已有客户的国家
答案
Select
Country
From Customers
Group by
Country
讨论
Group By子句是SQL的基石。对于任何复杂度的分析,你将使用多个 Group By子句,因此理解它们很重要。
17.统计每个ContactTitle的数量
答案
Select
ContactTitle
,TotalContactTitle = count(*)
From Customers
Group by
ContactTitle
Order by
count(*) desc
讨论
这种特殊的构造,有一个分组,然后是每个组的总数的计数,它本身和作为其他查询的一部分都是非常常见的。
18.产品与相关供应商名
答案
Select
ProductID
,ProductName
,Supplier = CompanyName
From Products
Join Suppliers
on Products.SupplierID = Suppliers.SupplierID
讨论
连接可以非常简单,也可以非常复杂。你需要彻底地理解它们,因为除了最简单的SQL之外,它们对编写任何东西都至关重要。
当你阅读SQL代码时,你也会看到这样的答案:
Select
ProductID
,ProductName
,Supplier = CompanyName
From Products P -- 别名表
Join Suppliers S -- 别名表
on P.SupplierID = S.SupplierID
如果这样做,也需要在On子句中使用P和S。
尽管这很常见,但是我不喜欢这种风格。唯一的好处是避免了一些重复输入,但缺点很严重,它导致代码更难阅读。
在像这样的小块SQL中,这并不是什么问题。然而,在长而复杂的SQL中,你会发现自己想知道一个字母的别名是什么意思,总是需要查阅From子句,并在脑海中翻译。
未完待续
下次我们接着讨论中级问题。
如果喜欢这篇文章,请不要忘记关注🧡、点赞👍和收藏📔哦!