SQL刷题日记1

目标

牛客网SQL进阶挑战 46/46

进度

牛客网SQL进阶挑战 26/46

前言

今天又是开心刷SQL的一天,为了成为一名合格的数据分析师,SQL技能不可或缺,为了能够让SQL技能更加娴熟,我将持续将牛客网上的SQL题刷完,以此来复习从前学过的SQL。并将相关的知识整理为日记的形式。

1.插入记录

插入记录的方式汇总

  • 普通插入(全字段):insert into table_name values(value1,value2,value3,...)
  • 普通插入(限定字段): insert into table_name(column1,column2,...) values(value1,value2,...)
  • 多次插入一次性: insert into table_name(column1,column2,...) values(value1,value2,....),(value2_1,value2_2,...)
  • 从另一个表导入: insert into table_name select * from table_name2 where(key=values)
  • 带更新的插入:replace into table_name values(value1,value2,...)

replace into 功能介绍

  1. 如果发现此表中已有此行数据(根据主键或者唯一索引判断),则先删除此行的数据,再插入此行数据
  2. 否则,直接插入新数据

注意:因为replace是通过主键或者唯一索引来判断数据是否重复,因此插入数据必须得有主键或者唯一索引,否则会导致replace into直接插入数据,造成数据重复!

2.更新记录

更新记录的方式汇总

  • 设置为新值: UPDATE table_name SET key1=value1[,key2=values2][where column3=value3]
  • 根据已有值替换:UPDATE table_name SET key1=REPLACE(key1,'原有内容','替换内容')[where columns3=values3]

3.删除记录

删除更新的记录方式汇总

1.按条件删除:delete from table_name [where option][[order by field] Limit n]

2.全部删除(表清空,重置主键计数):truncate table_name

4.表及索引操作

1.表的创建

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] -- 列排序和比较时的规则(是否区分大小写等)

2.从另一张表复制表结构

creat table table_name like table_name_old

3.从另一张表查询结果创建表

create table table_name as select * from table_name_old where option

4.修改表的集合

alter table table_name [修改选项]

  { ADD COLUMN <列名> <类型>  -- 增加列
     | CHANGE COLUMN <旧列名> <新列名> <新列类型> -- 修改列名或类型
     | ALTER COLUMN <列名> { SET DEFAULT <默认值> | DROP DEFAULT } -- 修改/删除 列的默认值
     | MODIFY COLUMN <列名> <类型> -- 修改列类型
     | DROP COLUMN <列名> -- 删除列
     | RENAME TO <新表名> -- 修改表名
     | CHARACTER SET <字符集名> -- 修改字符集
     | COLLATE <校对规则名> } -- 修改校对规则(比较和排序时用到)

5.删除表

drop table [if exists] table_name1 [,table_name2]

5.索引创建、删除与使用

1.create 方式创建索引

create [unique --唯一索引
|fulltext] --全文索引 
index index_name on table_name(colunm1[(length)[desc|asc]] [,column2....])

2.drop 方式删除索引

drop index <标签名> on <表名>

 3.索引的使用

  • 只有在满足最左前缀匹配原则时,如组合索引(col1,col2),只有先满足col1的索引,索引才会生效,因此在索引时,col1的条件,应该在col2索引条件之前
  • 索引不包含有NULL的列
  • 一次查询中只会使用一次索引,如果where使用了索引,则order by不使用索引
  • like 做字段比较时只有满足前缀条件才会使用索引
  • 对某列值进行计算后不再使用该列的索引,如year(start_time)<2024 不会使用start_time的索引

6.聚合函数

1.NULL值对count函数的影响

1、COUNT(*)

对表中行数进行计数

不管是否有NULL

2、COUNT(字段名)

对特定列有数据的行进行计数

忽略NULL值

2..SQL 开窗函数over 与sum(),AVG(),MIN(),MAX()

over函数主要可以帮助聚合函数sum()等函数进行分组,该分组的效率比group by 分组效率要高。

 select 
2   customerID,
3   SUM(totalPrice) over()  as  AllTotalPrice, --所有订单总价
4   SUM(totalPrice) over(partition by customerID)  as  cusTotalPrice,--每个用户所有订单总价

7.分组查询

1.last_day()函数

last_day(date)

可以直接返回月份的最后一天

2.coalesce函数

COALESCE是一个函数,coalesce (expression_1, expression_2, ...,expression_n)依次参考各参数表达式,遇到非null值即停止并返回该值。

SQL实例

1

select coalesce(success_cnt, 1) from tableA

当success_cnt 为null值的时候,将返回1,否则将返回success_cnt的真实值。

1

select coalesce(success_cnt,period,1) from tableA

success_cnt不为null,那么无论period是否为null,都将返回success_cnt的真实值(因为success_cnt是第一个参数),当success_cnt为null,而period不为null的时候,返回period的真实值。只有当success_cnt和period均为null的时候,将返回1。

3.with rollup

with在sql语句中定义在group by之后。当需要对数据库数据进行分类统计的时候,往往会用上groupby进行分组。而在groupby后面还可以加入withcube和withrollup等关键字对数据进行汇总。不过这个cube在mysql中并不适用。

使用 WITH ROLLUP,此函数是对聚合函数进行求和,注意 with rollup是对 group by 后的第一个字段,进行分组计算

4.ONLY_FULL_GROUP_BY 模式的规则

ONLY_FULL_GROUP_BY 是 MySQL 的 SQL 模式之一,它加强了对 GROUP BY 语句的规则要求。在 ONLY_FULL_GROUP_BY 模式下,MySQL 强制执行以下规则:

  1. 所有在 SELECT 列表、HAVING 条件或 ORDER BY 子句中出现的列 必须是聚合函数(如 COUNT, SUM, AVG, MAX, MIN 等)的一部分,或者必须出现在 GROUP BY 子句中。
  2. 如果一个列没有被聚合,并且不在 GROUP BY 子句中,那么该查询将被认为是不合法的,除非这个列可以通过 GROUP BY 子句中的列唯一确定(即功能依赖于 GROUP BY 中的列)。

这种模式有助于确保查询结果的一致性和可预测性。例如,如果你有一个包含多个记录的数据集,并且你尝试按某个列分组但同时选择了另一个未聚合也未分组的列,那么在这种情况下,如果没有 ONLY_FULL_GROUP_BY 模式,MySQL 可能会返回不确定的结果,因为它不知道应该选择哪个值来代表那一组数据。

启用 ONLY_FULL_GROUP_BY 模式可以提高 SQL 查询的严谨性,使得查询更符合标准 SQL 规范。然而,这也意味着一些在其他 SQL 模式下可能有效的查询,在 ONLY_FULL_GROUP_BY 下需要修改才能运行。

规则1: 所有在 SELECT 列表、HAVING 条件或 ORDER BY 子句中出现的列必须是聚合函数的一部分,或者必须出现在 GROUP BY 子句中。
案例1: 不符合规则的情况

假设有一个 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 子句中。

规则2: 如果一个列没有被聚合,并且不在 GROUP BY 子句中,那么该查询将被认为是不合法的,除非这个列可以通过 GROUP BY 子句中的列唯一确定(即功能依赖于 GROUP BY 中的列)。
案例2: 功能依赖的情况

考虑同样的 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 子句中,或者使用聚合函数来处理这些列。

5.SQL如何保证数据的一致性?

因为前面提到ONLY_FULL_GROUP_BY 模式可避免潜在的一致性问题,因此我在这里补充上mysql常用的保持数据一致性的方法。

1. 使用事务

事务是数据库管理系统执行的基本工作单位,它由一个或多个 SQL 语句组成。事务具有 ACID 属性(原子性、一致性、隔离性和持久性),这些属性有助于确保数据的一致性。

  • 原子性:事务中的所有操作要么全部完成,要么全部不完成。
  • 一致性:事务必须使数据库从一个一致状态转换到另一个一致状态。
  • 隔离性:并发执行的事务彼此之间不能看到对方未提交的结果。
  • 持久性:一旦事务提交,其结果就是永久性的
2. 使用约束

MySQL 支持多种类型的约束来强制数据完整性规则,例如:

  • 主键约束 (PRIMARY KEY):确保列中没有重复值,并且不允许空值。
  • 唯一性约束 (UNIQUE):确保列中没有重复值,但允许空值。
  • 外键约束 (FOREIGN KEY):确保引用的记录存在于另一个表中。
  • 检查约束 (CHECK):确保列中的所有值满足某些条件(MySQL 的 InnoDB 存储引擎支持 CHECK 约束)。
  • 非空约束 (NOT NULL):确保列中不允许有空值。
3. 锁定机制

为了处理并发访问,MySQL 提供了不同的锁定机制来防止数据被破坏。主要的锁定类型包括:

  • 表级锁 (Table Locks):锁定整个表,适用于简单的读写操作。
  • 行级锁 (Row-level Locks):只锁定受影响的行,这是 InnoDB 存储引擎默认的行为,可以提高并发性能。
  • 共享锁 (Shared Locks, S Locks):允许多个事务同时读取同一资源,但阻止其他事务获取排他锁。
  • 排他锁 (Exclusive Locks, X Locks):阻止任何其他事务获取共享锁或排他锁,用于写入操作。
1.表级锁

表级锁是对整个表进行锁定。当一个事务对某个表加了表级锁,其他事务就不能对该表进行某些操作,直到锁被释放。表级锁通常在简单的读写操作或者在需要锁定整张表以确保一致性的情况下使用。

例子:表级读锁(共享锁)

假设有一个 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语句涉及的资源进行读或者写的操作,直到该锁被释放。

4. 数据库设计

良好的数据库设计也是保持数据一致性的关键因素之一。这包括:

  • 规范化:将数据结构化为标准格式,减少数据冗余,从而降低更新异常的风险。
  • 参照完整性:确保关系型数据库中的外键总是指向有效的主键值。
5. 定期维护

定期进行数据库备份、日志分析和性能调优,可以帮助发现并解决潜在的数据一致性问题。

6.concate、concat_ws()函数以及group_concat函数

1.concat()函数

功能:将多个字符串连接成一个字符串。

语法:concat(str1, str2,...)

返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null

2、group_concat()函数

group_concat()函数是将所有的查询结果拼接成一个字符串返回,不过在不同的字段值之间默认是用逗号隔开的

语法:group_concat(option [separator <间隔符>])

3.concat_ws()函数

功能:和concat()一样,将多个字符串连接成一个字符串,但是可以一次性指定分隔符(concat_ws就是concat with separator)

语法:concat_ws(separator, str1, str2, ...)

说明:第一个参数就是指定的分隔符。需要注意的是分隔符不能为null,如果为null,则返回结果为null(其他参数哪一个为null也是一样的)。

你可能感兴趣的:(sql,数据库,mysql)