什么是SQL?
SQL是一种用于与数据库通信的编程语言,是技术行业招聘中需要掌握的最有用的技能之一。SQL是结构化查询语言的缩写,这个惊人的工具是分析大型数据集的必备工具。当它应用于关系数据库(以某种方式相互关联的唯一数据表)时,它的优点尤其明显。
由于SQL在技术领域无处不在,许多公司在给出工作邀请之前都会进行SQL面试。这有助于确保求职者(尤其是从事项目管理、分析、商业智能和软件工程相关工作的人员)能够轻松地在工作中使用SQL。
如果你即将进行SQL面谈,你或许想知道可能会遇到什么样的问题。招聘人员通常对细节含糊其辞,像这样一无所知地开始一场谈话会让人不知所措。
因此,我们列出了7个最常见的SQL面试问题,以便你在面试前进行一些练习。通过一些预先准备,你会在面试日感觉到胸有成竹、自信满满。
1、什么是关系数据库,什么是SQL?举例说明
虽然你可能知道关系数据库是什么以及SQL是什么,但很难在面试现场给出连贯、简洁的解释。为此,请确保你已提前准备好回答这个简单的问题。以下是一些答题小技巧:
关系数据库是一组数据表,以某种方式相互链接或关联。它用于存储不同类型的信息,这些信息可以汇集起来回答特定的分析问题。这是在不丢失任何关键信息的情况下,最大限度地减少存储在服务器上的数据量一种有效方法。
这是一个模糊的定义,所以让我们看看实际中的关系数据库。在线零售商的关系数据库的简单版本可能包含两个单独的数据表:
- 顾客。客户信息列表,包括客户姓名、联系信息和发货首选项。此数据库中的每个记录都包含一个唯一的customer_id字段,通过该字段可以识别客户。
- 订单。在零售商网站上购买的订单列表。每个订单列表还包含一个customer_id字段,用于将该订单的详细信息与下订单的特定客户相关联。
当然,如果我们只在Orders 表中包含客户信息,就不需要多表数据库。但这并不是特别有效:如果一个客户下了多个订单,他或她的姓名、联系信息和发货偏好将列在Orders 表的多行上,从而导致不必要的重复和无法管理的大型数据库。相反,我们创建了一个关系数据库来节省空间,并展示了不同数据块是如何链接在一起的。
那么,SQL只是用于与这个关系数据库通信的语言。数据库还不能理解像英语这样的人类语言——英语在语法上太复杂了——所以我们使用一种它们能够理解的标准化的语言来与它们交流。
2、SQL JOIN子句有哪些不同类型,它们是如何被使用的?
在SQL中,JOIN子句用于返回将两个或多个其他表的内容合并在一起的表。 例如,如果我们有两个表——一个包含Customers信息,另一个包含各个客户订购的Orders 信息——我们可以使用JOIN子句将它们组合在一起并创建一个新表:客户的完整订单列表,提供所有必要的信息以便发货。
有多种类型的JOIN子句,它们的功能稍微有所不同:
- INNER JOIN返回在指定的两个表中都匹配的行列表。它是默认的连接类型,因此如果只输入JOIN而不指定任何其他条件,则将自动使用INNER JOIN。
- LEFT JOIN将返回语句中左表的所有结果,并在可能的情况下与右表中的行匹配。如果左表中的一行在右表中不包含相应的匹配项,仍将列出该行——右表中的列为空值。
- RIGHT JOIN 将返回语句中右表的所有结果,并尽可能与左表中的行匹配。如果右表中的行在左表中不包含相应的匹配项,仍将该行列出——左表的列为空值。
- FULL JOIN将返回语句中左表和右表的所有结果。如果存在左表中的行与右表不匹配或右表中的行与左表不匹配的情况,所有数据仍将被返回——但SQL将在所有未匹配的列中输出空值。
- CROSS JOIN返回两个表的笛卡尔积——换句话说,左表的每个单独行与右表的每个单独行匹配。
3、为什么此查询未返回预期结果?
orders表中总共有1000行:
SELECT * FROM orders; -- 1000 rows in set (0.05 sec)
其中23个订单来自customer_id = 45的用户:
SELECT * FROM orders WHERE customer_id = 45; -- 23 rows in set (0.10 sec)
然而,当我们选择不是来自customer_id = 45的订单数量时,我们只得到973个结果:
SELECT * FROM orders WHERE customer_id <> 45; -- 973 rows in set (0.11 sec)
973 + 23 = 996。但是,customer_id =45加上customer_id不等于45的订单数量不应该等于1000吗?为什么此查询未返回预期结果?
答案是:此数据集很可能包含具有 customer_id为空的order值。在条件中使用SELECT子句时,具有空值的行与=或<>运算符不匹配。
上面我们所指的第二个查询可以修改如下,以产生预期的结果:
SELECT * FROM orders WHERE (customer_id <> 45 OR customer_id IS NULL); -- 977 rows in set (0.11 sec)
4、为什么其中一个查询有效而另一个查询无效
请看以下查询,该查询返回预期结果:
SELECT CASE WHEN (3 IN (1, 2, 3, NULL)) THEN 'Three is here!' ELSE "Three isn't here!" END AS result; /* +----------------+ | result | +----------------+ | Three is here! | +----------------+ 1 row in set (0.00 sec) */输出中显示"Three is here!",因为值3包含在IN子句中。但是以下查询呢?SELECT CASE WHEN (3 NOT IN (1, 2, NULL)) THEN "Three isn't here!" ELSE 'Three is here!' END AS result; /* +----------------+ | result | +----------------+ | Three is here! | +----------------+ 1 row in set (0.00 sec) */
第二个数据集中不包含3,所以为什么我们的查询错误地输出了"Three is here!"?
答案再一次与MYSQL处理NULL值的方式有关。让我们仔细看看。在我们的第一个查询中,我们询问值3是否包含在集合中(1, 2, 3, NULL)。我们的语句在功能上等同于以下内容:
SELECT CASE WHEN ((3 = 1) OR (3 = 2) OR (3 = 3) OR (3 = NULL)) THEN 'Three is here!' ELSE "Three isn't here!" END AS result; /* +----------------+ | result | +----------------+ | Three is here! | +----------------+ 1 row in set (0.00 sec) */
由于3绝对等于3,所以满足了我们的OR条件之一,语句输出"Three is here!"。另一方面,我们的第二个语句询问值3是否未包含在集合中(1, 2, NULL)。此语句在功能上等同于以下内容:
SELECT CASE WHEN ((3 <> 1) AND (3 <> 2) AND (3 <> NULL)) THEN "Three isn't here!" ELSE "Three is here!" END AS result; /* +----------------+ | result | +----------------+ | Three is here! | +----------------+ 1 row in set (0.00 sec) */
在这种情况下,条件检查3 <> NULL失败,因为在ANSI标准SQL中,我们需要使用IS NULL语句而不是<>运算符。
5、你能构建一个基本的INNER JOIN吗
customers和 orders表具有以下相应的结构:
CREATE TABLE `customers` ( `customer_id` int(11) NOT NULL AUTO_INCREMENT, `first_name` varchar(255) NOT NULL, `last_name` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, `address` varchar(255) DEFAULT NULL, `city` varchar(255) DEFAULT NULL, `state` varchar(2) DEFAULT NULL, `zip_code` varchar(5) DEFAULT NULL, PRIMARY KEY (`customer_id`) ); CREATE TABLE `orders` ( `order_id` int(11) NOT NULL AUTO_INCREMENT, `customer_id` int(11) NOT NULL, `order_placed_date` date NOT NULL, PRIMARY KEY (`order_id`), KEY `customer_id` (`customer_id`), FOREIGN KEY (`customer_id`) REFERENCES `customers` (`customer_id`) );
你是否可以构造一个简单的SELECT 语句,该语句使用INNER JOIN来组合来自customers和orders表的所有信息?
这里的答案非常简单。我们是这样做到的:
SELECT * FROM orders INNER JOIN customers on orders.customer_id = customers.customer_id;
6、使用 AS 语句
我们根据上面的orders表编写了一个查询,以选择2016年的所有订单。但是我们的查询有错。你能找出问题出在哪儿吗?
SELECT order_id, customer_id, YEAR(order_placed_date) AS order_year FROM orders WHERE order_year = 2016;
答案是:order_year是一个别名,意味着它被用作更复杂引用的另一个名称:YEAR(order_placed_date)。 事实证明,在SQL中,别名只能在 GROUP BY、 ORDER BY 和 HAVING子句中引用 - 它们不能在WHERE子句中使用。 运行上面的代码将产生以下结果:
--ERROR 1054 (42S22): Unknown column 'order_year' in 'where clause'
要解决这个问题,我们需要重申WHERE子句中order_year别名的定义,如下所示:
SELECT order_id, customer_id, YEAR(order_placed_date) AS order_year FROM orders WHERE YEAR(order_placed_date) = 2016; --498 rows in set (0.00 sec)
7、使用SUM 函数
请看以下数据库结构:
CREATE TABLE `products` ( `product_id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `price` decimal(19,4) NOT NULL, PRIMARY KEY (`product_id`) ); CREATE TABLE `order_products` ( `order_product_id` int(11) NOT NULL AUTO_INCREMENT, `order_id` int(11) NOT NULL, `product_id` int(11) NOT NULL, PRIMARY KEY (`order_product_id`), KEY `order_id` (`order_id`), KEY `product_id` (`product_id`), FOREIGN KEY (`order_id`) REFERENCES `orders` (`order_id`), FOREIGN KEY (`product_id`) REFERENCES `products` (`product_id`) )
你是否可以编写一个可以查找所有order_ids 的 product.price 的查询?(例如,每个订单的product.price的总和)
这个问题有点棘手,因为我们必须同时使用SUM函数和 GROUP BY子句并通过order_id聚合订单。我们是这样做的:
SELECT order_id, SUM(price) AS total_order_price FROM order_products INNER JOIN products ON order_products.product_id = products.product_id GROUP BY order_id; --1000 rows in set (0.01 sec)