题目
给你一个长桌子,桌子上盘子和蜡烛排成一列。给你一个下标从 0 开始的字符串 s ,它只包含字符 ‘‘ 和 ‘|’ ,其中 ‘‘ 表示一个 盘子 ,’|’ 表示一支 蜡烛 。
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
nums = [1,3,5,6], target = 5
2
nums = [1,3,5,6], target = 2
1
1 | func searchInsert(nums []int, target int) int { |
equals()方法而不能用==。1 | public class Main { |
==和equals()比较都为true,但实际上那只是Java编译器在编译期,会自动把所有相同的字符串当作一个对象放入常量池,自然s1和s2的引用就是相同的。1 | // 是否包含子串: |
contains()方法的参数是CharSequence CharSequence是String的父类。
使用trim()方法可以移除字符串首尾空白字符。空白字符包括空格,\t,\r,\n:
另一个strip()方法也可以移除字符串首尾空白字符。类似中文的空格字符\u3000也会被移除
任意基本类型或引用类型转换为字符串,使用静态方法valueOf()
1 | public class Main { |
String的不变性设计可以看出,如果传入的对象有可能改变,我们需要复制而不是直接引用。
String则以byte[]存储:如果String仅包含ASCII字符,则每个byte存储一个字符,否则,每两个byte存储一个字符,这样做的目的是为了节省内存。
StringBuilder,它是一个可变对象,可以预分配缓冲区,往StringBuilder中新增字符时,不会创建新的临时对象。1 | public class Main { |
普通的字符串
+操作,并不需要将其改写为StringBuilder,因为Java编译器在编译时就自动把多个连续的+操作编码为StringConcatFactory的操作。在运行期,StringConcatFactory会自动把字符串连接操作优化为数组复制或者StringBuilder操作。
1 | public class Main { |
主键是关系表中记录的唯一标识。主键的选取非常重要:主键不要带有业务含义,而应该使用BIGINT自增或者GUID类型。主键也不应该允许NULL。
所以,选取主键的一个基本原则是:不使用任何业务相关的字段作为主键。
因此,身份证号、手机号、邮箱地址这些看上去可以唯一的字段,均不可用作主键。
作为主键最好是完全业务无关的字段,我们一般把这个字段命名为id。常见的可作为id字段的类型有:
8f55d96b-8acc-4636-8cb8-76bf8abc2f57。GUID算法通过网卡MAC地址、时间戳和随机数保证任意计算机在任意时间生成的字符串都是不同的,大部分编程语言都内置了GUID算法,可以自己预算出主键。关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。
一对多
classes的记录可以对应多个students表的记录。students表中,通过class_id的字段,可以把数据与另一张表关联起来,这种列称为外键。1 | ALTER TABLE students |
1 | ALTER TABLE students |
多对多
teacher_class可知teachers到classes的关系一对一
通过使用索引,可以让数据库系统不必扫描整个表,而是直接定位到符合条件的记录,这样就大大加快了查询速度。
关系数据库会自动对其创建主键索引。使用主键索引的效率是最高的,因为主键会保证绝对唯一。
1 | ALTER TABLE students |
设计关系数据表的时候,看上去唯一的列,例如身份证号、邮箱地址等,因为他们具有业务含义,因此不宜作为主键
1 | # 给该学生表姓名列添加一个唯一索引 |
1 | # 只对某一列添加一个唯一约束而不创建唯一索引 |
1 | # SELECT * FROM <表名> |
1 | SELECT * FROM <表名> WHERE <条件表达式> |
1 | # SELECT 列1, 列2, 列3 FROM ...时,还可以给每一列起个别名,这样,结果集的列名就可以与原表的列名不同。它的语法是SELECT 列1 别名1, 列2 别名2, 列3 别名3 FROM ... |
ORDER BY可以对结果集进行排序1 | # 查询结果集通常是按照id排序的,也就是根据主键排序。加上ORDER BY子句。例如按照成绩从低到高进行排序 |
LIMIT <M> OFFSET <N>子句实现。LIMIT总是设定为pageSize;OFFSET计算公式为pageSize * (pageIndex - 1)。1 | # 每页3条记录。要获取第1页的记录,可以使用LIMIT 3 OFFSET 0: |
OFFSET超过了查询的最大数量并不会报错,而是得到一个空的结果集。
在MySQL中,LIMIT 15 OFFSET 30还可以简写成LIMIT 30, 15。
OFFSET是可选的,如果只写LIMIT 15,那么相当于LIMIT 15 OFFSET 0
使用LIMIT <M> OFFSET <N>分页时,随着N越来越大,查询效率也会越来越低。
1 | -- 使用聚合查询: |
| 函数 | 说明 |
|---|---|
| SUM | 计算某一列的合计值,该列必须为数值类型 |
| AVG | 计算某一列的平均值,该列必须为数值类型 |
| MAX | 计算某一列的最大值 |
| MIN | 计算某一列的最小值 |
WHERE条件没有匹配到任何行,COUNT()会返回0,而SUM()、AVG()、MAX()和MIN()会返回NULL:GROUP BY子句指定了按class_id分组,因此,执行该SELECT语句时,会把class_id相同的列先分组,再分别计算。与ODERBY 先查询出结果再筛选不同。1 | SELECT class_id, COUNT(*) num FROM students GROUP BY class_id; |
1 | SELECT |
FROM <表1>的语法;INNER JOIN <表2>的语法;ON <条件...>,这里的条件是s.class_id = c.id,表示students表的class_id列与classes表的id列相同的行需要连接;WHERE子句、ORDER BY等子句。1 | SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score |
INNER JOIN只返回同时存在于两张表的行数据,由于students表的class_id包含1,2,3,classes表的id包含1,2,3,4,所以,INNER JOIN根据条件s.class_id = c.id返回的结果集仅包含1,2,3。
RIGHT OUTER JOIN返回右表都存在的行。如果某一行仅在右表存在,那么结果集就会以NULL填充剩下的字段。
LEFT OUTER JOIN则返回左表都存在的行。如果我们给students表增加一行,并添加class_id=5,由于classes表并不存在id=5的行,所以,LEFT OUTER JOIN的结果会增加一行,对应的class_name是NULL:
FULL OUTER JOIN,它会把两张表的所有记录全部选择出来,并且,自动把对方不存在的列填充为NULL

1 | # 右连接 |
增删改查,即CRUD:Create、Retrieve、Update、Delete
增、删、改,对应的SQL语句分别是:
基本语法是:INSERT INTO <表名> (字段1, 字段2, …) VALUES (值1, 值2, …);
要注意,字段顺序不必和数据库表的字段顺序一致,但值的顺序必须和字段顺序一致。也就是说,可以写INSERT INTO students (score, gender, name, class_id) ...,但是对应的VALUES就得变成(80, 'M', '大牛', 2)。
1 | INSERT INTO students (class_id, name, gender, score) VALUES (2, '大牛', 'M', 80); |
1 | syms t,x,a1,a4,a3,a,b,c,J; |
UPDATE语句的基本语法是:UPDATE <表名> SET 字段1=值1, 字段2=值2, … WHERE …;UPDATE语句时要非常小心,最好先用SELECT语句来测试WHERE条件是否筛选出了期望的记录集,然后再用UPDATE更新1 | UPDATE students SET name='大牛', score=66 WHERE id=1; |
删除数据库表中的记录,我们可以使用DELETE语句。
和UPDATE类似,不带WHERE条件的DELETE语句会删除整个表的数据
1 | DELETE FROM <表名> WHERE ...; |