读SQL学习指南(第3版)笔记09_条件逻辑与事务

news2025/1/23 4:40:01

1. 条件逻辑

1.1. SQL逻辑根据特定列或表达式转向不同的分支来处理

1.2. 在程序执行时从多个路径中选取一个路径的能力

1.3. case表达式

1.3.1. 所有的主流数据库服务器都提供了旨在模拟大多数编程语言中if-then-else 语句的内建函数

1.3.1.1. Oracle的decode()函数

1.3.1.2. MySQL的if()函数

1.3.1.3. SQL Server的coalesce()函数

1.3.2. case表达式是SQL标准的一部分(SQL92发布版)

1.3.2.1. 在Oracle Database、SQL Server、MySQL、PostgreSQL、IBM UDB等数据库中实现

1.3.3. case表达式内建于SQL语法中

1.3.3.1. 用于select、insert、update和delete语句

1.3.4. 搜索型case表达式

1.3.4.1. sql

CASE
  WHEN C1 THEN E1
  WHEN C2 THEN E2
  ...
  WHEN CN THEN EN
  [ELSE ED]
END

1.3.4.2. mysql

-> SELECT c.first_name, c.last_name,
    ->   CASE
    ->     WHEN active = 0 THEN 0
    ->     ELSE
    ->      (SELECT count(*) FROM rental r
    ->       WHERE r.customer_id = c.customer_id)
    ->   END num_rentals
    -> FROM customer c;

1.3.5. 简单的case表达式

1.3.5.1. simple case expression

1.3.5.2. 类似于搜索型case表达式,但是在灵活性上略逊

1.3.5.3. sql

CASE V0
  WHEN V1 THEN E1
  WHEN V2 THEN E2
  ...
  WHEN VN THEN EN
  [ELSE ED]
END

1.3.5.4. sql

CASE category.name
  WHEN 'Children' THEN 'All Ages'
  WHEN 'Family' THEN 'All Ages'
  WHEN 'Sports' THEN 'All Ages'
  WHEN 'Animation' THEN 'All Ages'
  WHEN 'Horror' THEN 'Adult'
  WHEN 'Music' THEN 'Teens'
  WHEN 'Games' THEN 'Teens'
  ELSE 'Other'
END

1.4. 结果集变换

1.4.1. mysql

-> SELECT monthname(rental_date) rental_month,
    ->   count(*) num_rentals
    -> FROM rental
    -> WHERE rental_date BETWEEN '2005-05-01' AND '2005-08-01'
    -> GROUP BY monthname(rental_date);

1.4.2. mysql

-> SELECT
    ->   SUM(CASE WHEN monthname(rental_date) = 'May' THEN 1
    ->         ELSE 0 END) May_rentals,
    ->   SUM(CASE WHEN monthname(rental_date) = 'June' THEN 1
    ->         ELSE 0 END) June_rentals,
    ->   SUM(CASE WHEN monthname(rental_date) = 'July' THEN 1
    ->         ELSE 0 END) July_rentals
    -> FROM rental
    -> WHERE rental_date BETWEEN '2005-05-01' AND '2005-08-01';

1.4.3. 如果monthname()函数返回了所需的列值,case表达式返回1;否则,返回0

1.5. 检查存在性

1.5.1. 只希望确定两个实体之间是否存在某种关系,而并不关心数量

1.5.2. mysql

-> SELECT a.first_name, a.last_name,
    ->   CASE
    ->     WHEN EXISTS (SELECT 1 FROM film_actor fa
    ->                    INNER JOIN film f ON fa.film_id = f.film_id
    ->                  WHERE fa.actor_id = a.actor_id
    ->                    AND f.rating = 'G') THEN 'Y'
    ->     ELSE 'N'
    ->   END g_actor,
    ->   CASE
    ->     WHEN EXISTS (SELECT 1 FROM film_actor fa
    ->                    INNER JOIN film f ON fa.film_id = f.film_id
    ->                  WHERE fa.actor_id = a.actor_id
    ->                    AND f.rating = 'PG') THEN 'Y'
    ->     ELSE 'N'
    ->   END pg_actor,
    ->   CASE
    ->     WHEN EXISTS (SELECT 1 FROM film_actor fa
    ->                    INNER JOIN film f ON fa.film_id = f.film_id
    ->                  WHERE fa.actor_id = a.actor_id
    ->                    AND f.rating = 'NC-17') THEN 'Y'
    ->     ELSE 'N'
    ->   END nc17_actor
    -> FROM actor a
    -> WHERE a.last_name LIKE 'S%' OR a.first_name LIKE 'S%';

1.6. 除零错误

1.6.1. 执行涉及除法的运算时,应该始终注意确保分母不能为0

1.6.2. mysql

-> SELECT c.first_name, c.last_name,
    ->   sum(p.amount) tot_payment_amt,
    ->   count(p.amount) num_payments,
    ->   sum(p.amount) /
    ->     CASE WHEN count(p.amount) = 0 THEN 1
    ->       ELSE count(p.amount)
    ->     END avg_payment
    -> FROM customer c
    ->   LEFT OUTER JOIN payment p
    ->   ON c.customer_id = p.customer_id
    -> GROUP BY c.first_name, c.last_name;

1.7. 条件更新

1.7.1. 在更新数据表中的行时,有时候需要根据条件逻辑生成列值

1.7.2. sql

UPDATE customer
SET active =
  CASE
    WHEN 90 <= (SELECT datediff(now(), max(rental_date))
                FROM rental r
                WHERE r.customer_id = customer.customer_id)
    THEN 0
   ELSE 1
 END
WHERE active = 1;

1.8. 处理null值

1.8.1. null是在列值未知时存储在数据表中的值,但是在检索时显示null值或者null作为表达式的组成部分时未必总是合适

1.8.2. sql

SELECT c.first_name, c.last_name,
  CASE
    WHEN a.address IS NULL THEN 'Unknown'
    ELSE a.address
  END address,
  CASE
    WHEN ct.city IS NULL THEN 'Unknown'
    ELSE ct.city
  END city,
  CASE
    WHEN cn.country IS NULL THEN 'Unknown'
    ELSE cn.country
  END country
FROM customer c
  LEFT OUTER JOIN address a
  ON c.address_id = a.address_id
  LEFT OUTER JOIN city ct
  ON a.city_id = ct.city_id
  LEFT OUTER JOIN country cn
  ON ct.country_id = cn.country_id;

2. 锁定

2.1. 锁是数据库服务器用来控制数据资源被同时使用的一种机制

2.2. 当部分数据库被锁定时,任何打算修改(也可能是读取)相应数据的用户必须等到锁被释放

2.3. 数据库的写操作必须向服务器发出请求并获得写入锁才能修改数据,而读操作必须发出请求并获得读取锁才能查询数据

2.3.1. 数据库的写操作必须向服务器发出请求并获得写入锁才能修改数据,而读操作必须发出请求并获得读取锁才能查询数据

2.3.2. 在有较多并发读写请求时等待时间过长

2.3.3. Microsoft SQL Server采用

2.3.4. 反映服务器为报表应用程序发放读取锁时的数据库状态

2.4. 数据库的写操作必须向服务器发出请求并获得写入锁才能修改数据,而读操作不需要任何类型的锁就可以查询数据,服务器要确保从查询开始到结束期间读操作读取到的是一致的数据视图

2.4.1. 版本控制

2.4.2. 在修改数据时存在耗时查询

2.4.3. Oracle Database采用

2.4.4. 反映报表开始生成时的数据库状态

2.5. MySQL则两者皆用,取决于存储引擎的选择

2.6. 锁的粒度

2.6.1. 数据表锁

2.6.1.1. 避免多个用户同时修改同一数据表中的数据

2.6.1.2. 锁定整个数据表只需要很少的簿记(bookkeeping)

2.6.1.3. 随着用户量的增加,该方法会迅速产生无法接受的等待时间

2.6.2. 页锁

2.6.2.1. 避免多个用户同时修改某数据表中同一页的数据

2.6.2.2. 一页通常是大小为2~16KB的内存段

2.6.3. 行锁

2.6.3.1. 避免多个用户同时修改某数据表中同一行的数据

2.6.3.2. 需要更多的簿记

2.6.3.3. 允许多个用户修改同一数据表,前提是被处理的是不同的行

2.6.3.4. Oracle Database只有行锁

2.6.3.5. Oracle Database不能提升锁

2.6.4. 锁选择

2.6.4.1. Microsoft SQL Server采用数据表锁、页锁和行锁

2.6.4.2. SQL Server可以将锁从行提升至页,再从页提升至数据表

2.6.4.3. MySQL则采用数据表锁、页锁或行锁(同样取决于存储引擎的选择)

3. 事务

3.1. 应用程序逻辑通常包含多个需要作为逻辑工作单元共同执行的SQL语句

3.2. 语句作为整体,要么执行成功,要么执行失败

3.2.1. 原子性

3.3. 数据库服务器上线前必须完成的任务之一就是查找服务器宕机前正在运行但未完成的事务并将其回滚。

3.4. 如果程序完成了事务并发出了commit命令,但还没有将变更应用于永久性存储(修改的数据仍然位于内存,尚未被写入磁盘)时服务器就宕机了,那么当服务器重启时,数据库服务器必须重新应用来自事务的变更

3.4.1. 持久性

3.5. 启动事务

3.5.1. 一个活跃的事务总是和数据库会话相关联,所以没有必要也没有方法显式地开始一个事务。当前事务结束后,服务器会自动为会话启动一个新的事务

3.5.1.1. Oracle Database采用

3.5.1.2. 哪怕只发出了一个SQL命令,如果对结果不满意或是改变了主意,也能够回滚变更

3.5.2. 除非显式地开始一个事务,否则各个SQL语句会被自动提交,彼此独立。要想启动事务,必须先发出一个命令

3.5.2.1. Microsoft SQL Server和MySQL采用

3.5.2.2. 一旦你按下Enter键,你的SQL语句所带来的变更将是永久性的

3.5.2.3. SQL:2003标准提供了start transaction命令,可用于显式地启动事务

3.5.2.3.1. MySQL遵守该标准
3.5.2.3.2. SQL Server用户必须使用替代命令begin transaction

3.5.2.4. MySQL允许关闭单个会话的自动提交模式

3.5.2.4.1. SET AUTOCOMMIT=0

3.5.2.5. SQL Server都允许关闭单个会话的自动提交模式

3.5.2.5.1. SET IMPLICIT_TRANSACTIONS ON

3.5.2.6. 建议每次登录时关闭自动提交模式,养成在事务内运行所有SQL语句的习惯

3.6. 结束事务

3.6.1. 必须显式地结束事务,可以通过commit命令来实现

3.6.1.1. 指示服务器将此变更标记为永久性的并释放事务中使用的任何资源(页锁或者行锁)

3.6.2. 如果打算撤销自事务启动后所做的所有变更,必须发出rollback命令

3.6.2.1. 指示服务器将数据恢复到事务之前的状态

3.6.2.2. rollback命令完成后,会话所使用的资源全都会被释放

3.6.3. 导致事务结束的其他场景

3.6.3.1. 服务器宕机,在这种情况下,事务会在服务器重启时被自动回滚

3.6.3.2. 发出SQL模式语句

3.6.3.2.1. 会使当前事务被提交并启动一个新的事务
3.6.3.2.2. 数据库的更改,无论是增加一个新数据表或新索引,还是删除数据表中的一列,都无法被回滚,因此,更改模式的命令必须出现在事务之外
3.6.3.2.3. 应该注意保护那些组成一个工作单元的语句不被服务器意外地拆分成多个事务
3.6.3.2.4. 服务器不会通知你到底发生了什么

3.6.3.3. 发出另一个start transaction命令,这会造成前一个事务被提交

3.6.3.4. 如果服务器检测到死锁并认定当前事务就是导致死锁的根源,服务器就会提前结束当前事务

3.6.3.4.1. 在这种情况下,该事务会被回滚,同时也会接收到错误消息
3.6.3.4.2. 如果检测到死锁,就要选择一个事务(任意选择或者根据某种标准)回滚,以便让其他事务得以继续
3.6.3.4.3. 被终止的事务可以重启,如果没有再遇到死锁,就可以顺利完成
3.6.3.4.4. 数据库服务器会显示错误消息,告知你由于检测到死锁,该事务已经被回滚
3.6.3.4.5. 重新尝试由于检测到死锁而被回滚的事务是一种合理的做法
3.6.3.4.6. 如果死锁出现得过于频繁,那么可能需要对访问数据库的应用程序进行修改,以降低死锁的可能性
3.6.3.4.7. 一个常用的策略是确保始终按照同样的顺序访问数据资源,比如总是在插入交易数据之前修改账户数据

3.7. 事务保存点

3.7.1. 可以在事务内创建一个或多个保存点,利用其回滚到事务内的特定位置,而不必一路回滚到事务的启动点

3.7.2. MySQL数据库服务器被设计成可以用多个存储引擎提供低层级数据库功能,包括资源锁定和事务管理

3.7.3. MySQL 8.0版

3.7.3.1. MyISAM

3.7.3.1.1. 采用数据表锁定的非事务引擎

3.7.3.2. MEMORY

3.7.3.2.1. 用于内存数据表(in-memory table)的非事务引擎

3.7.3.3. CSV

3.7.3.3.1. 在逗号分隔的文件(comma-separated file)中存储数据的事务引擎

3.7.3.4. InnoDB

3.7.3.4.1. 采用行级锁定的事务引擎

3.7.3.5. Merge

3.7.3.5.1. 使多个相同的MyISAM数据表以单表形式出现(也称为表分区)的专用引擎

3.7.3.6. Archive

3.7.3.6.1. 用于存储大量未索引数据的专用引擎,多作为归档之用

3.7.3.7. 允许以数据表为单位来选择存储引擎

3.7.4. 如果不知道数据表使用的是什么引擎,可以使用show table命令

3.7.5. sql

START TRANSACTION;
UPDATE product
SET date_retired = CURRENT_TIMESTAMP()
WHERE product_cd = 'XYZ';
SAVEPOINT before_close_accounts;
UPDATE account
SET status = 'CLOSED', close_date = CURRENT_TIMESTAMP(),
  last_activity_date = CURRENT_TIMESTAMP()
WHERE product_cd = 'XYZ';
ROLLBACK TO SAVEPOINT before_close_accounts;
COMMIT;

3.7.6. 创建保存点时,除了名称,什么都没有保存。要想使事务具有持久性,最终必须发出commit命令

3.7.6.1. SAVEPOINT my_savepoint;

3.7.7. 如果发出rollback命令的时候没有指定保存点名称,事务中的所有保存点都会被忽略,整个事务都会被撤销

3.7.7.1. ROLLBACK TO SAVEPOINT my_savepoint;

3.7.8. 如果使用的是SQL Server,需要使用专有命令save transaction创建一个保存点,使用rollback transaction命令回滚到某个保存点

3.7.8.1. 后跟保存点的名称

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/966051.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

T113-S3-ov5640摄像头调试

目录 前言 一、ov5640模组介绍 1. 图像传感器特性 2. 接口和控制 3. 图像处理能力 4. 应用领域 二、原理图连接 三、设备树配置 四、驱动配置 五、ov5640使用 六、异常记录 总结 前言 摄像头模块是嵌入式系统中常见的外设,用于捕获图像和视频。在本篇文章…

一不留神就掉坑

乘除顺序问题 在据卡特兰数[1]公式,解决leetcode-96 不同的二叉搜索树[2]时,遇到一个非常诡异的问题, package mainimport "fmt"func main() { for i : 0; i < 40; i { fmt.Printf("第%d个卡特兰数为:%d\n", i, numTrees(i)) }}func numTrees(n int) i…

【超详细~KVM】KVM概述、安装及简单操作-------从小白到大神之路之学习运维第91天

第四阶段提升 时 间&#xff1a;2023年8月30日 参加人&#xff1a;全班人员 内 容&#xff1a; KVM概述、安装及简单操作 目录 一、KVM 概述 二、KVM工作原理 三、KVM应用场景 四、centos7 下安装部署 五、新建虚拟机步骤 1、创建存储池并创建存储卷 2、点击号创建…

MPI之通信模式(标准,缓存,同步,就绪)

MPI缓冲区 由MPI自行维护的一块内存区域&#xff0c;也可由用户(MPI_Bsend)自行维护&#xff1b;发送方 维护一块发送缓冲区&#xff1b; 接收方 维护一块接收缓冲区。 数据收发过程&#xff1a; 当发送端将数据拷贝到自身的数据缓冲区后(注意这里是拷贝&#xff0c;即数据到…

Redis——认识Redis

简单介绍 Redis诞生于2009年&#xff0c;全称是Remote Dictionary Server&#xff0c;远程词典服务器&#xff0c;是一个基于内存的键值型NoSQL数据库。 特征 键值&#xff08;Key-value&#xff09;型&#xff0c;value支持多种不同数据结构&#xff0c;功能丰富单线程&…

多线程专栏------多线程的实现方式(三)

目录 1、使用线程池1.1、什么是线程池1.2、使用线程池的优点1.3、线程池的核心工作流程1.3、线程池的五种状态生命周期1.3.1、RUNNING1.3.2、SHUTDOWN1.3.3、STOP1.3.4、TIDYING1.3.5、TERMINATED 1.4、创建线程池的方式1.4.1、通过 ThreadPoolExecutor 创建1.4.1.1、线程池的核…

类的静态成员变量 static member

C自学精简教程 目录(必读) 类的静态成员 static member 变量全局只有一份副本&#xff0c;不会随着类对象的创建而产生副本。 static 静态成员 在类的成员变量前面增加static关键字&#xff0c;表示这个成员变量是类的静态成员变量。 #include <iostream> using name…

kaggle赛后总结

1. 宽表 2.缺失值的处理方法 最简单粗暴的就是删除&#xff0c;这种情况是凡是有缺失值行数很少。均值替代。缺失值的行数比较多一点儿的时候&#xff0c;直接删除会影响样本数量&#xff0c;那就均值替代&#xff0c;或者中位数替代等方法。还有复杂的方法&#xff0c;把有缺…

NMS(非极大值抑制)的 Python 实现

文章目录 1. NMS的步骤2. Python代码 非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;NMS&#xff09;是一种在目标检测中常用的技术。 NMS的目的是消除重叠区域中冗余的边界框&#xff0c;并选择最具代表性的目标作为最终结果。通过调整重叠阈值&#xff0c;可…

400电话号码怎么开通

开通400电话是企业提供客户服务的重要步骤。下面是一些步骤和注意事项&#xff0c;帮助您顺利开通400电话。 第一步&#xff1a;选择400电话服务提供商 选择一家可靠的400电话服务提供商非常重要。您可以通过搜索引擎、咨询行业内人士或者参考其他企业的经验来选择合适的服务提…

FLASH读写数据

目录 嵌入式 Flash大概了解 数据手册2.3.2章节 结构图f407 等待周期 Flash 控制寄存器解锁 编程/擦除并行位数 擦除 编程&#xff08;写入&#xff09; 工程程序 嵌入式 Flash大概了解 可以从flash区域启动程序&#xff1b;大概是程序区可以在flash&#xff0c;所以是可以…

python调用git出错:ImportError: Failed to initialize: Bad git executable.

报错信息 #报错信息 Traceback (most recent call last): File “”, line 1, in File “C:\Python27\lib\site-packages\git_init_.py”, line 85, in raise ImportError(‘Failed to initialize: {0}’.format(exc)) ImportError: Failed to initialize: Bad git executab…

春秋云镜 CVE-2018-16283

春秋云镜 CVE-2018-16283 WordPress Plugin Wechat Broadcast LFI 靶标介绍 WordPress Plugin Wechat Broadcast LFI 启动场景 漏洞利用 exp # Exploit Title: WordPress Plugin Wechat Broadcast 1.2.0 - Local File Inclusion # Author: Manuel Garcia Cardenas # Date:…

图论-01-图的基本表示-邻接矩阵和邻接表-Java

文章目录 邻接矩阵邻接表邻接表的问题和改进总结 邻接矩阵 import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Scanner;public class AdjMatrix {private int V;private int E;private int[][] adj;// 构造函数&#xff0c;从文…

利用网络流通过拆点判断图的路径存在性问题:abc318_g

https://atcoder.jp/contests/abc318/tasks/abc318_g 对于图上一类路径是否存在问题&#xff0c;可以考虑网络流。 Trick1 路径存在转网络流 题目转化为&#xff1a; 找出两条不交路径 B->A, B->C 对于已经找到的路径&#xff0c;我们不能再走。对于当前我们找到的某条…

Matlab图像处理-幂次变换

幂次变换 如下图所示的幂次变换函数曲线图&#xff1a; 当γ <1时&#xff0c;效果和对数变换相似&#xff0c;放大暗处细节&#xff0c;压缩亮处细节&#xff0c;随着数值减少&#xff0c;效果越强。 当γ >1时&#xff0c;放大亮处细节&#xff0c;压缩暗处细节&…

云备份——配置信息及获取配置信息类模块

一&#xff0c;配置信息 使用文件配置加载一些程序的运行关键信息&#xff0c;如ip&#xff0c;端口等&#xff0c;可以让程序的运行更加灵活 我们需要的配置信息如下 IP地址端口号热点判断时间&#xff0c;也就是非热点文件的时间要求文件下载的URL前缀路径&#xff0c;用于表…

Matlab图像处理-

对数变换 对数变换的一项主要应用是压缩动态范围。一些特别的图像在实际显示中&#xff0c;高灰度值部分较占优势&#xff0c;而低灰度值的可见细节部分丢失。通过计算对数&#xff0c;如10的动态范围会降至14左右[即 ln1013.8]&#xff0c;这样就更易于处理。 对数变换就是压…

Pygame中Trivia游戏解析6-4

3.3.3 显示题目选项 在显示题目选项时&#xff0c;有三种情况&#xff1a;分别是用户还未选择答案时&#xff1b;用户的答案是正确时&#xff1b;用户的答案是错误时。 &#xff08;1&#xff09;用户还未选择答案时 此时&#xff0c;用白色显示四个备选答案&#xff0c;如图…

数据工厂-生成接口通用用例

章节目录&#xff1a; 一、背景介绍二、前置准备三、设计思路四、代码具体实现五、执行效果六、其他说明七、结束语 一、背景介绍 有哪些用例是可以通用且固定的&#xff1f; 针对之前提到的接口用例设计思路&#xff0c;拆分为三个切入点&#xff1a; 举个例子&#xff1a; {…