牛客网SQL进阶挑战 46/46
牛客网SQL进阶挑战 26/46
今天又是开心刷SQL的一天,为了成为一名合格的数据分析师,SQL技能不可或缺,为了能够让SQL技能更加娴熟,我将持续将牛客网上的SQL题刷完,以此来复习从前学过的SQL。并将相关的知识整理为日记的形式。
replace into 功能介绍
注意:因为replace是通过主键或者唯一索引来判断数据是否重复,因此插入数据必须得有主键或者唯一索引,否则会导致replace into直接插入数据,造成数据重复!
1.按条件删除:delete from table_name [where option][[order by field] Limit n]
2.全部删除(表清空,重置主键计数):truncate table_name
CREATE TABLE
[IF NOT EXISTS] tb_name -- 不存在才创建,存在就跳过
(column_name1 data_type1 -- 列名和类型必选
[ PRIMARY KEY -- 可选的约束,主键
| FOREIGN KEY -- 外键,引用其他表的键值
| AUTO_INCREMENT -- 自增ID
| COMMENT comment -- 列注释(评论)
| DEFAULT default_value -- 默认值
| UNIQUE -- 唯一性约束,不允许两条记录该列值相同
| NOT NULL -- 该列非空
], ...
) [CHARACTER SET charset] -- 字符集编码
[COLLATE collate_value] -- 列排序和比较时的规则(是否区分大小写等)
creat table table_name like table_name_old
create table table_name as select * from table_name_old where option
alter table table_name [修改选项]
{ ADD COLUMN <列名> <类型> -- 增加列
| CHANGE COLUMN <旧列名> <新列名> <新列类型> -- 修改列名或类型
| ALTER COLUMN <列名> { SET DEFAULT <默认值> | DROP DEFAULT } -- 修改/删除 列的默认值
| MODIFY COLUMN <列名> <类型> -- 修改列类型
| DROP COLUMN <列名> -- 删除列
| RENAME TO <新表名> -- 修改表名
| CHARACTER SET <字符集名> -- 修改字符集
| COLLATE <校对规则名> } -- 修改校对规则(比较和排序时用到)
drop table [if exists] table_name1 [,table_name2]
create [unique --唯一索引
|fulltext] --全文索引
index index_name on table_name(colunm1[(length)[desc|asc]] [,column2....])
drop index <标签名> on <表名>
1、COUNT(*)
对表中行数进行计数
不管是否有NULL
2、COUNT(字段名)
对特定列有数据的行进行计数
忽略NULL值
over函数主要可以帮助聚合函数sum()等函数进行分组,该分组的效率比group by 分组效率要高。
select
2 customerID,
3 SUM(totalPrice) over() as AllTotalPrice, --所有订单总价
4 SUM(totalPrice) over(partition by customerID) as cusTotalPrice,--每个用户所有订单总价
last_day(date)
可以直接返回月份的最后一天
COALESCE是一个函数,coalesce (expression_1, expression_2, ...,expression_n)依次参考各参数表达式,遇到非null值即停止并返回该值。
SQL实例
1 |
|
当success_cnt 为null值的时候,将返回1,否则将返回success_cnt的真实值。
1 |
|
当success_cnt不为null,那么无论period是否为null,都将返回success_cnt的真实值(因为success_cnt是第一个参数),当success_cnt为null,而period不为null的时候,返回period的真实值。只有当success_cnt和period均为null的时候,将返回1。
with在sql语句中定义在group by之后。当需要对数据库数据进行分类统计的时候,往往会用上groupby进行分组。而在groupby后面还可以加入withcube和withrollup等关键字对数据进行汇总。不过这个cube在mysql中并不适用。
使用 WITH ROLLUP,此函数是对聚合函数进行求和,注意 with rollup是对 group by 后的第一个字段,进行分组计算。
ONLY_FULL_GROUP_BY
是 MySQL 的 SQL 模式之一,它加强了对 GROUP BY 语句的规则要求。在 ONLY_FULL_GROUP_BY
模式下,MySQL 强制执行以下规则:
这种模式有助于确保查询结果的一致性和可预测性。例如,如果你有一个包含多个记录的数据集,并且你尝试按某个列分组但同时选择了另一个未聚合也未分组的列,那么在这种情况下,如果没有 ONLY_FULL_GROUP_BY
模式,MySQL 可能会返回不确定的结果,因为它不知道应该选择哪个值来代表那一组数据。
启用 ONLY_FULL_GROUP_BY
模式可以提高 SQL 查询的严谨性,使得查询更符合标准 SQL 规范。然而,这也意味着一些在其他 SQL 模式下可能有效的查询,在 ONLY_FULL_GROUP_BY
下需要修改才能运行。
假设有一个 orders
表,包含以下列:order_id
, customer_id
, order_date
, total_amount
。我们想要计算每个客户的订单总数和总金额,但同时也想显示最近的一次订单日期:
SELECT
customer_id,
COUNT(order_id) AS order_count,
SUM(total_amount) AS total_spent,
MAX(order_date) AS last_order_date -- 这里尝试获取最近一次订单日期
FROM
orders
GROUP BY
customer_id;
在这个查询中,MAX(order_date)
是一个聚合函数,所以它符合规则。但是如果我们试图选择一个没有聚合也没有分组的列,比如:
-- 错误示例
SELECT
customer_id,
COUNT(order_id) AS order_count,
SUM(total_amount) AS total_spent,
order_date -- 这里直接选择了未聚合且未分组的列
FROM
orders
GROUP BY
customer_id;
这个查询将会违反 ONLY_FULL_GROUP_BY
规则,因为 order_date
没有被聚合,也没有出现在 GROUP BY 子句中。
考虑同样的 orders
表,现在假设每个 customer_id
对应唯一的 customer_name
,并且我们想要显示每个客户的名称和他们的订单总数:
-- 假设每个 customer_id 对应唯一的 customer_name
SELECT
customer_id,
customer_name, -- 这里虽然没有聚合,但它可以由 customer_id 唯一确定
COUNT(order_id) AS order_count
FROM
orders
GROUP BY
customer_id;
在这种情况下,即使 customer_name
没有出现在 GROUP BY 子句中,MySQL 也允许这种查询,因为它假定 customer_name
是通过 customer_id
唯一确定的。这意味着对于每个 customer_id
,只有一个 customer_name
的值。
如果 customer_name
并不是通过 customer_id
唯一确定的(例如,如果有多个名字对应同一个 customer_id
),那么 MySQL 可能会返回错误,或者结果可能是不确定的。
为了确保查询总是有效并避免潜在的数据一致性问题,最好的做法是明确地将所有非聚合列添加到 GROUP BY 子句中,或者使用聚合函数来处理这些列。
因为前面提到ONLY_FULL_GROUP_BY 模式可避免潜在的一致性问题,因此我在这里补充上mysql常用的保持数据一致性的方法。
事务是数据库管理系统执行的基本工作单位,它由一个或多个 SQL 语句组成。事务具有 ACID 属性(原子性、一致性、隔离性和持久性),这些属性有助于确保数据的一致性。
MySQL 支持多种类型的约束来强制数据完整性规则,例如:
为了处理并发访问,MySQL 提供了不同的锁定机制来防止数据被破坏。主要的锁定类型包括:
表级锁是对整个表进行锁定。当一个事务对某个表加了表级锁,其他事务就不能对该表进行某些操作,直到锁被释放。表级锁通常在简单的读写操作或者在需要锁定整张表以确保一致性的情况下使用。
例子:表级读锁(共享锁)
假设有一个 employees
表,两个用户同时访问这个表:
用户A 执行了一个查询,并且想要确保在查询过程中数据不会发生变化:
LOCK TABLE employees READ; -- 用户A获取了employees表的读锁
SELECT * FROM employees;
UNLOCK TABLES; -- 用户A释放锁
用户B 同时尝试更新 employees
表中的数据:
UPDATE employees SET salary = salary * 1.1 WHERE department = 'Sales';
由于用户A已经持有了 employees
表的读锁,用户B的更新操作会被阻塞,直到用户A释放锁
例子:表级写锁(排他锁)
同样对于 employees
表:
用户A 执行一个更新操作,并且想要确保在更新期间没有其他事务可以读取或修改这张表:
LOCK TABLE employees WRITE; -- 用户A获取了employees表的写锁
UPDATE employees SET salary = salary * 1.1 WHERE department = 'Sales';
UNLOCK TABLES; -- 用户A释放锁
用户B 尝试执行任何涉及 employees
表的操作(无论是读还是写):
SELECT * FROM employees; -- 读取操作
-- 或者
UPDATE employees SET salary = salary * 1.1 WHERE department = 'Marketing'; -- 更新操作
用户B的所有操作都会被阻塞,直到用户A释放了 employees
表的写锁。
行级锁与表级锁概念类似,只是将表的范围缩小到行的范围,当包含一行或多行SQL语句的事物使用共享锁时,其他事务不能对这一行或多行SQL语句涉及的资源进行写的操作,直到该锁被释放。
当包含一行或多行SQL语句的事物使用排他锁时,其他事务不能对这一行或多行SQL语句涉及的资源进行读或者写的操作,直到该锁被释放。
良好的数据库设计也是保持数据一致性的关键因素之一。这包括:
定期进行数据库备份、日志分析和性能调优,可以帮助发现并解决潜在的数据一致性问题。
功能:将多个字符串连接成一个字符串。
语法:concat(str1, str2,...)
返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null
group_concat()函数是将所有的查询结果拼接成一个字符串返回,不过在不同的字段值之间默认是用逗号隔开的
语法:group_concat(option [separator <间隔符>])
功能:和concat()一样,将多个字符串连接成一个字符串,但是可以一次性指定分隔符(concat_ws就是concat with separator)
语法:concat_ws(separator, str1, str2, ...)
说明:第一个参数就是指定的分隔符。需要注意的是分隔符不能为null,如果为null,则返回结果为null(其他参数哪一个为null也是一样的)。