不同表字段聚合、信息重组:当某个查询涉及多表连接、次数频繁时,可以创建视图隐藏底层表的复杂性,简化查询。
控制权限:根据不同用户的权限,可以建立不同的视图,让用户查看部分数据(如只公开非敏感数据)。
安全性需要:用户无法对视图进行随意的修改和删除,只能在只读视图中检索数据,增加了安全性。
更新/重构数据库,同时满足原来原有应用的访问:当数据库需要重构(如删除了一些旧表,创建了一些新表),但不希望这些更改影响之前的应用程序时,可以使用与已删除、修改的旧表相同的表结构创建数据库视图。因为视图保留了原有的表架构,应用程序可以访问视图来完成此前功能而无需修改。
计算列需要:数据库设计范式要求减少冗余字段,所以数据库表大多没有计算列。但报表中通常需要总销售额等计算列字段,所以可以创建一个含有计算列字段的视图。
因为每个数据库都是不相同的。在一个数据库上取得的经验也许可以部分用于另一个数据库,但必须有心理准备,二者可能存在一些基本差别,可能还有一些细微差别。
细微差别(如对null的处理)与基本差别(并发控制机制)可能有同样显著的影响
所以我们应当了解数据库,知道它如何工作,其特性如何实现,这是解决这些问题的唯一途径
不同数据库在性能、安全、连接、锁机制、空值等方面有不同,下面以空值处理和锁机制为例:
不同的数据库系统可能会对空值的处理有所不同,有四种场景:
锁机制是实现并发控制机制的一种主要手段。不同的数据库,实现锁机制是不一样的。
比如Oracle采取多版本控制(MVVC),读一致性的并发模型,支持读一致查询和非阻塞查询,有以下特性:
这种机制让Oracle存在有时读不到正确数据的现象,因为在读取时可能会读到已提交的旧版本数据,而不像其他数据库一样选择检查时间戳和退回。
此外,读不阻塞写可以极大提高数据库的吞吐能力,所以Oracle拥有散回特性。但其他数据库可能不是这样的,比如SQLite不支持非阻塞查询。
此外,因为锁粒度、锁类型、事务隔离级别等不同,Oracle与其他数据库的锁机制还有很多不同。
总之,我们不能简单把数据库当黑盒,否则可能会出错,比如Oracle的无阻塞设计有一个副作用,若想保证一次最多只有一个用户访问一行数据,就需要开发人员自己一些做工作。
已知在数据库系统中,通常采用磁盘来存储索引数据,以支持对大量数据的高效查询操作。
因为索引是顺序结构,可以用二分搜索树构建索引。但是当N比较大的时候,树的深度比较高。数据查询的时间主要依赖于磁盘IO的次数,二叉树深度越大,查找的次数越多,性能越差。最坏的情况是退化成了链表。即使做Tree Balancing,也比较耗时。其存储上的不连续性,也会导致存储空间的浪费。
而B+树有高扇出和低高度的特性,更适合磁盘实现。
基本结构:
使用方式:
使用场景:
B+树自下而上构建,随着叶结点的数量增加,内部节点的数量和树的高度也增加。
分裂涉及到两层:下面分裂,上面增加。移动的是中间值。
叶结点分裂和非叶结点分裂的区别:因为B+树中的所有数据均保存在叶子结点,13移动到内部结点后,也同时存在于叶子结点。而非叶结点分裂时,13上升后不会出现在原有层。
先访问索引再访问基本表,索引只是查询工作的第⼀步,读取基本表中的数据才是查询的结束。
同样的索引,但不同的物理结构,可能会引起查询效率的千差万别:
主键索引肯定存在,和基本表有对应关系:如文件偏移量对应,记录的是该记录在文件中偏移的字节数
二级索引是非主键的其他键值构建的索引,有两种构建方法:
不同数据库选择不同,如MySQL选第二种。
在数据库中,通常将存储空间划分为多个page,每个page包含多个数据块,数据块中包含多个三元组,其中每个三元组对应一个数据项:
关键字(Key):唯一标识每个数据项;
相关值(Value):与关键字对应的具体数据信息;
指针(Pointer):指向与当前数据项相关的数据块或其他地址信息。
在数据的读写过程中,通过k找到对应的v和p,从而实现数据的定位和操作。
优点:
缺点:
可以根据指针快速查找到记录的位置;
两边可以伸缩
最小开销:唯一额外开销是一个指针数组,用于保存记录实际所在位置偏移量
空间回收:通过对页进行碎片整理和重写,可以回收空间
动态布局:从页外部只能通过槽ID引用槽,确切位置由页内部决定
当第一次数据溢出迁移时,系统会将数据存储到其他的块或页中,但是由于系
统自身的机制,可能会出现数据不平均地分布在多个块或页中的情况。
后面再需要进行迁移时,系统会将两端的无效数据都删除掉,只保留有效数
据,然后将数据存储到新的块或页(溢出页)中。
区别
行迁移是原来的页中还会存一部分的数据和指针,而行溢出是原来的页只存指
针。(优先进行行迁移)
为什么同一个分槽页中行迁移只会发生一次
由于数据页中的数据是按照主键值排序的,因此当某个数据行被迁移时,会导致该数据页中的其他数据行的位置发生改变。
因此多次数据迁移操作可能会导致数据页中的数据行位置变得十分混乱,甚至可能会影响查询性能。
所以如果之后的超额数据都将存储在溢出页中,可以减少数据行位置变化的次数,提高查询性能。
映射校验通常用于大规模数据的验证。
映射校验将数据划分为多个任务,然后在每个任务内部通过计算生成本地校验
和(Local Checksum)。最后,在一个Reduce节点上将所有本地校验和进行
合并计算,以生成最终的全局校验和。
分槽页一定要有校验,一页存一个校验和。这样可以尽早识别磁盘文件问题,避免传播到其他子系统和节点。
XOR和CRC是常见的计算校验和方法:
设有一个船员租赁船只系统,表结构如下:
有sailors
表,
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| sid | int |
| sname | varchar |
| rating | int |
| age | int |
+--------------+---------+
sid为该表主键。
该表包含船员的编号,姓名,等级和年龄
有boats
表,
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| bid | int |
| bname | varchar |
| color | varchar |
+--------------+---------+
bid为该表主键。
该表包含船只编号,船只名称和船只颜色
有reserves
表,
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| sid | int |
| bid | int |
| reserve_date | date |
+---------------+---------+
(sid,bid)为该表主键。
该表包含船员编号,船员预定的船只编号,船员预定船只的日期
编写一个sql语句,找出年龄在35以上的并且在2020-09-01至2020-09-30期间没有预定红色(RED)船只的水手,结果返回水手姓名sname。
select sailors.sname from sailors
where sailors.age > 35 and sailors.sid not in (
select reserves.sid from reserves, boats
where reserves.bid = boats.bid and boats.color = 'RED' and
reserves.reserve_date between '2020-09-01' and '2020-09-30'
)
编写一个sql语句,找出预定了所有船的水手,结果返回水手姓名sname。
select s.sname from sailors s where not exists (
select * from boats b where not exists(
select * from reserves r where b.bid = r.bid and s.sid = r.sid
)
)
编写一个sql语句,找出2020-05-01至2020-05-31期间预定过绿色船(GREEN)的等级最高的水手,结果返回水手姓名sname。
select sailors.sname from sailors
where sailors.sid in (
select reserves.sid from reserves, boats
where reserves.bid = boats.bid and boats.color = 'GREEN' and
reserves.reserve_date between '2020-05-01' and '2020-05-31'
)
and sailors.rating >= ALL(
select sailors.rating from sailors where sailors.sid in (
select reserves.sid from reserves, boats
where reserves.bid = boats.bid and boats.color = 'GREEN' and
reserves.reserve_date between '2020-05-01' and '2020-05-31'
)
)
编写一个sql语句,找出年龄在35岁以上,并且在2020-08-01至2020-08-31期间同时预定了红色船(RED)和绿色船(GREEN)的水手,结果返回水手姓名sname。
select sailors.sname from sailors
where sailors.age > 35 and sailors.sid in (
select r.sid from reserves r, boats b
where r.bid = b.bid and b.color = 'GREEN' and
r.reserve_date between '2020-08-01' and '2020-08-31'
) and sailors.sid in (
select r.sid from reserves r, boats b
where r.bid = b.bid and b.color = 'RED' and
r.reserve_date between '2020-08-01' and '2020-08-31'
)
表 Weather
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| recordDate | date |
| temperature | int |
+---------------+---------+
id 是这个表的主键
SELECT tod.`Id` AS Id
FROM Weather yes
JOIN Weather tod
ON DATEDIFF(tod.`RecordDate`, yes.`RecordDate`) = 1
WHERE tod.`Temperature` > yes.`Temperature`
UPDATE tbl_name
SET
field_name = REPLACE(field_name, string_to_find, string_to_replace)
WHERE
conditions;
UPDATE products --表名
SET
productDescription = REPLACE(productDescription, 'abuot', 'about'); --列名
查询查找所有出现的拼写错误词:abuot
,并通过products
表的productDescription
列中使用正确单词将其替换
coalesce(num,0) // 去除null
count(*)包括了所有的列,相当于行数,不会忽略列值为NULL
count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空
简单树:单一结点只有一个父节点
BOM:单一结点可能有多个父节点。
一定通过多表,一张表存不了。一个表存内容,一个表存关系。
如下图Components存内容,Composition存关系。
以邻接表模型为例:使用两个表来存储多父节点的树:
Node表:
| id | value | … |
Relationship表:
| id | parent_id | child_id | order |
--先查询初始成分的占比,再对其他部分计算实际占比
with recursive_composition(actual_pct, component_id)
as (select a.pct, a.component_id
from composition a, components b
where b.component_id = a.recipe_id
and b.component_name = 'Philter #5'
union all
select parent.pct * child.pct, child.component_id
from recursive_composition parent, composition child
where child.recipe_id = parent.component_id)
--主查询,去重
select x.component_name, sum(y.actual_pct)
from recursive_composition y, components x
where x.component_id = y.component_id
and x.component_type = 'I'
group by x.component_name
我对函数索引中where f(indexed_col)='some value'
这样的检索条件会使索引无法发挥作用感兴趣。
老师上课以日期函数为例,说第一种写法会导致 MySQL 无法使用 order_date
上的索引,因为需要对每一行记录执行 DATE_FORMAT
函数,这个过程无法利用索引。
SELECT COUNT(*) FROM orders WHERE DATE_FORMAT(order_date, '%Y-%m-%d') = '2023-03-08';
SELECT COUNT(*) FROM orders WHERE order_date >= '2023-03-08 00:00:00' AND order_date <= '2023-03-08 23:59:59';
隐式类型转换因为触发了CAST函数,也无法发挥作用。因此应该尽量避免在查询条件或者查询字段中使用函数,以便能够充分利用索引提高查询性能。
我好奇背后的原因,通过课下学习,发现B+ 树提供的这个快速定位能力,来源于同一层兄弟节点的有序性。而运行函数后的值会被修改或者变形,可能与实际存储在B+树中的原始值不同。比如假设树的第一层数据是2018-1-1,2019-9-1,2020-7-1
,对每个值执行month()
函数,得到的值(1,9,7)
,破坏了有序的前提。
mysql中规定只要对索引字段使用函数操作(无论是否破坏了了有序性),就放弃走树搜索功能,所以效率降低。