MySQL SQL基础入门-你想要的我尽可能覆盖全

什么是SQL?

SQL(Structured Query Language) ,结构化查询语言。SQL是一种专用语言,用户关系型数据库管理系统或者在关系流数据管理系统中进行流处理。

  • SQL怎么读?两种读法:一个字母一个字母读或者连起来读。 一个字母一个字母读音标: /ˌɛsˌkjuːˈɛl/,连读音标: /ˈsiːkwəl/ 。

什么是关系型数据库?

关系型数据是一种数据库类型,关系型数据库存储和提供数据都是相互关联的。关系型数据库是通过关系模型来组织数据的。通常关系型数据通过多张表中的行和列来组织关系。表中的行也叫记录或者元组。列也叫属性。

什么是数据库?

数据库是在按照数据模型来组织,存储和管理数据的集合。数据库一般由很多表组成,这些表之间有直接或者间接的关系。我们可以把表定义为图中的节点,关系为他们之间的边。如下图:

什么是数据库管理系统?

数据库管理系统是数据库系统的核心部分,他复杂各种CRUD,没有它根本就不会存在数据库系统。

常见的数据库管理系统:MySQL, PostgreSQL, MariaDB, Oracle, SQL Server, DB2…一大堆。

什么是数据库系统?

数据库系统是一个生态系统,包括数据库,数据库管理系统和数据库的集成环境还有你的应用程序(我们都是数据库系统开发工程师。。。)。这是说首先你得有一个系统来CRUD你的数据库,然后你得有各种driver能连接数据库系统,或者有GUI的客户端来操作数据库。
比如: 你启动一个MySQL就是启动了一个数据库管理系统,你在MySQL中新建自己的数据库,使用MySQL的driver和dialect(方言)来操作MySQL数据库,那么这个整体就是数据库系统。

什么是关系流数据管理系统?

关系流数据管理系统是借鉴了数据库管理系统的一些概念和技术针对大量流数据进行快速响应的系统。其实我理解就是大数据哪些内容。

常见的关系流数据管理系统:Spark, Flink,Storm。

文章中所有的SQL都是基于MySQL 8.0数据库系统。

DDL, DQL, DCL 和DML

这些概念整的人头大。

  • DDL(Data Defination Language)数据定义语言
  • DQL(Data Query Language)数据查询语言:用来查询数据中的数据,专指查询:SELECT
  • DCL(Data Control Language)数据控制语言:用来控制权限:Authorization
  • DML(Data Manipulation Language) 数据操纵语言,指的就是CRUD

大家对DQL的认可度不高,DDL,DML和DCL听得比较多,我理解是因为SELECT专指SELECT xxxx,如果你使用FROM和WHERE,那么FROM和WHERE就属于DML了,感觉还是挺鸡肋的。

约束

约束就是限制一个数据只能为A不能为B。比如:你设置一个用户的用户名长度不能超过20个中文,性别在数据库中的类型只能为数字,那么数据库会遵守这些约束,当程序向数据库写入用户名超过20个中文时就会报错,性别写入字符串时也会报错。

当然我们对约束的需求还是比较多的,主要有以下几类:

  1. 主键
  2. 唯一键
  3. 外键
  4. check
  5. NOT NULL
  6. Default
  7. 数据类型
  8. 字段长度

我们上面列了这么多,MySQL可以通过下面的SQL来查询约束

SELECT * FROM information_schema.table_constraints;

COPY

数据类型

数据库提供了很多数据类型以满足我们的应用需求,虽然很多,但是分类一下还是很清晰的,看下数据类型图:

file

Numberic Types

Numeric TypeSigned RangeUnsigned Range
TINYINT-128 to 1270 to 255
SMALLINT-32768 to 327670 to 65535
MEDIUMINT-8388608 to 83886070 to 16777215
INT or INTEGER-2147483648 to 21474836470 to 4294967295
BIGINT-9223372036854775808 to 92233720368547758070 to 18446744073709551615
DECIMAL (M,D) or NUMERIC (M,D)Depends on M and D valuesDepends on M and D values
FLOAT (P)-3.402823466E+38 to -1.175494351E-38, 0, and 1.175494351E-38 to 3.402823466E+380 to 3.402823466E+38
DOUBLE (M,D)-1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and 2.2250738585072014E-308 to 1.7976931348623157E+3080 to 1.7976931348623157E+308
BIT (M)-2^(M-1) to 2^(M-1)-10 to 2^M-1
BOOLEANN/AN/A
SERIALN/A1 to 18446744073709551615

What is the size of column of int(11) in mysql in bytes?

String types

String TypeDescription
CHAR(M)A fixed-length string useful when all values are approximately the same length. Pad with space when saved. M can be from 0 to 255.
VARCHAR(M)A variable-length string useful when values are of varying length. M can be from 0 to 65535.
TINYTEXTA tiny text string holding a maximum length of 255 characters.
TEXTA text string holding a maximum length of 65,535 characters
MEDIUMTEXTA medium-sized text string which can hold a string with a maximum length of 16,777,215 characters
LONGTEXTA large text string which can hold a string with a maximum length of 4,294,967,295 characters
ENUM(‘value1′,’value2’,…)A string object with a value chosen from a list of permitted values
SET(‘value1′,’value2’,…)A string objects that can have zero or more values, each of which must be chosen from a list of permitted values
BINARY(M)Similar to CHAR, but stores binary byte strings. Pad with 0x00 bytes when saved.
VARBINARY(M)Similar to VARCHAR, but stores binary byte strings.
TINYBLOBA binary large object column with a maximum length of 255 bytes
BLOBA binary large object column with a maximum length of 65,535 bytes
MEDIUMBLOBA binary large object column with a maximum length of 16,777,215 bytes
LONGBLOBA binary large object column with a maximum length of 4,294,967,295 bytes

Date types

Date/Time TypeDescription
DATEA date. The supported range is ‘1000-01-01’ to ‘9999-12-31’.
DATETIMEA date and time combination. The supported range is ‘1000-01-01 00:00:00’ to ‘9999-12-31 23:59:59’.
TIMESTAMPA timestamp. The range is ‘1970-01-01 00:00:01’ UTC to ‘2038-01-19 03:14:07’ UTC.
TIMEA time. The range is ‘-838:59:59’ to ‘838:59:59’.
YEARA year in four-digit format. Values in the range 1901 to 2155 and 0000.

有这么数据类型可以供我们选择。
怎么选择?一般就是根据我们的业务需求,然后满足一定的扩展性的情况下选择存储小的类型。存储小的类型占用的磁盘空间小,数据在一个数据叶中的存储就会更多,增删查改I/O就会更少。

字符集和排序规则

字符集(Character Set)和排序规则(Collation)是与字符编码和字符串比较相关的重要概念。

字符集(Character Set):字符集是一个符号集合。在电脑中,每一个符号是由若干位表示的,每个符号对应唯一的位模式。简单的说,字符集就是一个符号与位模式之间的对应关系。
在MySQL中,它支持多种字符集,如utf8(可以用1到3个字节存储字符),utf8mb4(可以用1到4个字节存储字符),latin1(用1个字节存储字符)等等。选择合适的字符集可以确保您的数据库正确存储和显示各种语言的文本。

排序规则(Collation):排序规则决定了字符串之间的比较规则。比如在英文中,字母“a”和“A”是相同的。但在计算机中,“a”的ASCII码是97,“A”的ASCII码是65,它们在计算机看来是不同的。因此,我们需要一个规则来指导计算机在比较时应如何处理这种情况,这个规则就是所谓的排序规则。
不同的语言和地区,排序规则是不一样的。

但是我从来没有修改过排序规则,我只为能够存储emoji表情把utf-8字符集修改为了utf8mb4。但是前提是业务系统能容忍不区分大小写。

DDL语句

DDL语句帮助我们对数据库从0到1的建设。

数据库CRUD

创建数据库

我们得先创建数据库,在MySQL中创建数据库的语法如下:

CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
    [create_option] ...

create_option: [DEFAULT] {
    CHARACTER SET [=] charset_name
  | COLLATE [=] collation_name
  | ENCRYPTION [=] {'Y' | 'N'}
}

COPY

修改数据库

MySQL ALTER DATABASE无法修改数据库名的,看他的alter_option就可以知道了。

ALTER {DATABASE | SCHEMA} [db_name]
    alter_option ...

alter_option: {
    [DEFAULT] CHARACTER SET [=] charset_name
  | [DEFAULT] COLLATE [=] collation_name
  | [DEFAULT] ENCRYPTION [=] {'Y' | 'N'}
  | READ ONLY [=] {DEFAULT | 0 | 1}
}

COPY

删除数据库

删除数据库的SQL语句是非常简单的,但是却是个高危操作,可能有不少人因为这个SQL而失业。

DROP {DATABASE | SCHEMA} [IF EXISTS] db_name

COPY

查询数据库

MySQL查询数据库有多种方式:

  1. show
  2. 数据字典
-- show查询数据库
SHOW DATABASES;
SHOW DATABASES like '%trade%';

-- 数据字典查询数据库
SELECT `SCHEMA_NAME` 
FROM `INFORMATION_SCHEMA`.`SCHEMATA`;

SELECT `SCHEMA_NAME`
FROM `INFORMATION_SCHEMA`.`SCHEMATA` WHERE SCHEMA_NAME LIKE '%trade%';

COPY

创建表

MySQL创建表的SQL特别长,我们这儿只给一部分吧,大家平时使用工具自己生成就好了:


CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
    (create_definition,...)
    [table_options]
    [partition_options]

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
    [(create_definition,...)]
    [table_options]
    [partition_options]
    [IGNORE | REPLACE]
    [AS] query_expression

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
    { LIKE old_tbl_name | (LIKE old_tbl_name) }

create_definition: {
    col_name column_definition
  | {INDEX | KEY} [index_name] [index_type] (key_part,...)
      [index_option] ...
  | {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name] (key_part,...)
      [index_option] ...
  | [CONSTRAINT [symbol]] PRIMARY KEY
      [index_type] (key_part,...)
      [index_option] ...
  | [CONSTRAINT [symbol]] UNIQUE [INDEX | KEY]
      [index_name] [index_type] (key_part,...)
      [index_option] ...
  | [CONSTRAINT [symbol]] FOREIGN KEY
      [index_name] (col_name,...)
      reference_definition
  | check_constraint_definition
}

COPY

这儿给出一个示例吧,创建一个trade_user表,这张表有以下字段:


+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | bigint unsigned  | NO   | PRI | NULL    | auto_increment |
| name       | varchar(20)      | NO   | MUL | NULL    |                |
| email      | longtext         | YES  |     | NULL    |                |
| age        | tinyint unsigned | YES  |     | NULL    |                |
| birthday   | datetime         | YES  |     | NULL    |                |
| created_at | datetime         | YES  |     | NULL    |                |
| updated_at | datetime         | YES  |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+

COPY

对应的建表语句如下:


CREATE TABLE trade_user (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL,
    email LONGTEXT,
    age TINYINT UNSIGNED,
    birthday DATETIME,
    created_at DATETIME,
    updated_at DATETIME,
    PRIMARY KEY (id),
    INDEX idx_name (name) 
);

COPY

我们再新建一张trade_order表表示用户的订单方便我们后面进行joinSQL测试。
表结构如下所示:


+--------------+-----------------+------+-----+---------+----------------+
| Field        | Type            | Null | Key | Default | Extra          |
+--------------+-----------------+------+-----+---------+----------------+
| id           | bigint unsigned | NO   | PRI | NULL    | auto_increment |
| user_id      | bigint unsigned | YES  |     | NULL    |                |
| order_no     | bigint unsigned | YES  |     | NULL    |                |
| created_at   | datetime        | YES  |     | NULL    |                |
| updated_at   | datetime        | YES  |     | NULL    |                |
| total_amount | bigint unsigned | YES  |     | NULL    |                |
| paied_amount | bigint unsigned | YES  |     | NULL    |                |
+--------------+-----------------+------+-----+---------+----------------+

COPY

建表SQL:


CREATE TABLE trade_order (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    order_no BIGINT UNSIGNED NOT NULL,
    created_at DATETIME NOT NULL,
    updated_at DATETIME NOT NULL,
    total_amount BIGINT UNSIGNED NOT NULL,
    paied_amount BIGINT UNSIGNED NOT NULL
);

COPY

建表避坑

有些数据库比如MySQL有一种语法:create tables as select,表示既创建表又从另一张表拷贝数据到新表,但是会有一个问题就会所有的约束和索引会丢失。举个例子:

file

正确姿势

file

在使用不熟悉的SQL之前要确保理解了它的所有特性。

修改表

修改表使用alter table语句, MySQL alter table语句如下:

alter table的语法也有点多啊,如果要修改大家还是用工具吧,先修改然后生成SQL。


ALTER TABLE tbl_name
    [alter_option [, alter_option] ...]
    [partition_options]

alter_option: {
    table_options
  | ADD [COLUMN] col_name column_definition
        [FIRST | AFTER col_name]
  | ADD [COLUMN] (col_name column_definition,...)
  | ADD {INDEX | KEY} [index_name]
        [index_type] (key_part,...) [index_option] ...
  | ADD {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name]
        (key_part,...) [index_option] ...
  | ADD [CONSTRAINT [symbol]] PRIMARY KEY
        [index_type] (key_part,...)
        [index_option] ...
  | ADD [CONSTRAINT [symbol]] UNIQUE [INDEX | KEY]
        [index_name] [index_type] (key_part,...)
        [index_option] ...
  | ADD [CONSTRAINT [symbol]] FOREIGN KEY
        [index_name] (col_name,...)
        reference_definition
  | ADD [CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED]
  | DROP {CHECK | CONSTRAINT} symbol
  | ALTER {CHECK | CONSTRAINT} symbol [NOT] ENFORCED
  | ALGORITHM [=] {DEFAULT | INSTANT | INPLACE | COPY}
  | ALTER [COLUMN] col_name {
        SET DEFAULT {literal | (expr)}
      | SET {VISIBLE | INVISIBLE}
      | DROP DEFAULT
    }

COPY

修改表指南

MySQL 8.0版本支持了Instant方式修改表结构,但是只能最多64次,64次以后就回退回重建表的方式,然后有有了64次Instant机会。
具体可以参考:

  1. MySQL 8 INSTANT 添加或者删除列(是否可以不用担心大表添加/删除列问题了?)
  2. 【翻译】MySQL 8.0中的“即时增加和删除列”功能

删除表

MySQL删除表还是很简单的但是执行要慎重,SQL语句如下:


DROP [TEMPORARY] TABLE [IF EXISTS]
    tbl_name [, tbl_name] ...
    [RESTRICT | CASCADE]

COPY

注意

重要的事情说三遍:
删除之前要完整备份。
删除之前要完整备份。
删除之前要完整备份。

查询数据库中的表

MySQL查询表和查询数据库一样有两种方式:

  1. SHOW TABLES
  2. 查询数据字典

SQL如下:


-- show tables方式
show tables in 数据库名

-- 查询数据字典方式
SELECT `TABLE_NAME` 
FROM `INFORMATION_SCHEMA`.`TABLES`
WHERE `TABLE_SCHEMA`='数据库名';

COPY

INSERT 插入数据

INSERT向表插入新数据,MySQL中一般有三种形式

  1. INSERT INTO tbl_name (col_name[, col_name]…) VALUES (value[, value]…); 这种形式是最基本的形式,用于向指定列插入一行或多行数据
  2. INSERT INTO tbl_name SET col_name=value[, col_name=value]…; 这种形式也可用于插入数据,数据的列名和值是通过“列名=值”的形式一一对应的
  3. INSERT INTO tbl_name (col_name[, col_name]…) SELECT … ; 这种形式是把查询出的数据直接插入到表中

额外说明下:INSERT INTO SELECT可以适用于复制表,数据转换或者聚合完解决查询新表等场景。INSERT INTO 性能特别高。但是有索引会响应一些性能。

插入数据

我们插入数据都到上面定义的两张表:

-- 普通INSERT
INSERT INTO trade_user (name, email, age, birthday, created_at, updated_at) VALUES
('Alice', 'alice@example.com', 30, '1992-01-10 08:30:00', NOW(), NOW()),
('Bob', 'bob@example.com', 35, '1987-05-20 14:45:00', NOW(), NOW()),
('Charlie', 'charlie@example.com', 25, '1997-08-15 09:00:00', NOW(), NOW()),
('Diana', 'diana@example.com', 28, '1994-04-16 17:30:00', NOW(), NOW()),
('Eva', 'eva@example.com', 32, '1990-11-11 12:00:00', NOW(), NOW()),
('Frank', 'frank@example.com', 40, '1982-12-03 19:15:00', NOW(), NOW());

INSERT INTO trade_order (user_id, order_no, created_at, updated_at, total_amount, paied_amount) VALUES
(1, 1, NOW(), NOW(), 2500, 2500),
(2, 2, NOW(), NOW(), 4500, 4500),
(3, 3, NOW(), NOW(), 3000, 1500),
(4, 4, NOW(), NOW(), 1500, 1500),
(5, 5, NOW(), NOW(), 5200, 5200),
(6, 6, NOW(), NOW(), 1300, 1300);

-- INSERT INTO SELECT
INSERT INTO trade_order (user_id, order_no, created_at, updated_at, total_amount, paied_amount)
SELECT 1000 AS user_id, 1000001 AS order_no, NOW() AS created_at, NOW() AS updated_at, 2500 AS total_amount, 2500 AS paied_amount
UNION ALL
SELECT 1001, 1000002, NOW(), NOW(), 3000, 3000
UNION ALL
SELECT 1002, 1000003, NOW(), NOW(), 4000, 4000
UNION ALL
SELECT 1003, 1000004, NOW(), NOW(), 5000, 5000
UNION ALL
SELECT 1004, 1000005, NOW(), NOW(), 6000, 6000
UNION ALL
SELECT 1005, 1000006, NOW(), NOW(), 7000, 7000;

COPY

删除数据

删除数据使用DELETE,使用非常简单,执行要慎重,否则影响绩效(说是影响绩效是因为误删除还可以通过binlog恢复)。

MySQL的delete语法:


DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name [[AS] tbl_alias]
    [PARTITION (partition_name [, partition_name] ...)]
    [WHERE where_condition]
    [ORDER BY ...]
    [LIMIT row_count]

COPY

删除数据避坑

  1. 生产环境SQL不要使用select查询出来的数据作为条件,就让DevOps查出来所有满足数据的ID,然后使用ID删除,一批删除不完就分多批,删除之前一定要备份数据,可以整表备份,可以增量备份。
  2. 线上删除整表数据不要直接delete from xxx,你的binlog会受不了,你的下游复制会受不了,DevOps要骂人了,用户要骂人了;备份,备份,备份,重要事情说三遍;使用truncate table;不能用truncate table就分批delete,删除完以后optimize一下。

查询SELECT

我们大部分时间都在select, 同时select也是变换莫测,目前我也没玩出什么花。

简单查询


select * from trade_user;

select username from trade_user;

select username as uname from trade_user;

select distinct username from trade_user;

COPY

where 字段

比较运算符:


select * from trade_user where id>10;

where id < 10;

where id=10;

where id != 10;

where id <> 10;

COPY

逻辑运算符:


select * from trade_user where id=1 and username='';

where id=1 or id=2;

where not id=1;

where not id=1 and not id=2;

where not id=1 or not id=2;

where not (id=1 or id=2);

COPY

逻辑运算符小提示

又使用and又使用or的算是复杂查询了,尽可能使用一个区分度比较高的索引,否则就是index range和Index merge了。

in 查询


select * from trade_user where age in (12, 13);

select * from trade_user where age in (select age from trade_user);

select * from trade_user where (name, age) in (select name, age from trade_user);

select * from trade_user where (name, age) in (select name,age from trade_user where id=1);

select * from trade_user where (name, age) in (('b', 12));

COPY

in 避坑

in中有null。

插入一条数据:


insert into trade_user (name, email, age, birthday, created_at, updated_at) values('fofcn.tech', 'fofcn@fofcn.tech', null, '1989-05-25 00:00:00', now(), now());

COPY

查询一下刚插入的数据:


select * from trade_user where age in (null);

COPY

查不出来,emo~

file

换个姿势,再来一次:


select * from trade_user where age is null;

COPY

查询出来了,完美~

file

为什么?
在 SQL 中,NULL 不是一个具体的值,而是代表一个“未知”的状态。如果你使用 IN 操作符来查询 NULL,可能会出现问题。
这是因为在 SQL 中, NULL 与任何其他值(包括另一个 NULL)的比较操作都会返回 NULL,而不是布尔值 TRUE 或 FALSE。

使用Go的小伙伴有没有试过nil == nil?

like模糊查询


select * from trade_user where `name` like '%al%';

select * from trade_user where `name` like '%al';

select * from trade_user where `name` like 'al%';

COPY

听说like左边%不会走索引?你说了可不算

这都得视情况而定,不是%在左边就一定不走索引。举个例子:还是我们的trade_user表。
我们的表有50多万数据,索引有3个:主键,idx_name,idx_age。

file

第一条SQL: 查询用户的所有信息,条件是用户名中有51的用户信息


explain select * from trade_user where name like '%51';

COPY

结果是真没走索引,全表扫描。

file

第二条SQL:查询用户的name,条件不变


explain select name from trade_user where name like '%51';

COPY

结果是走了索引,而且是覆盖索引。

file

第三条SQL:查询用户的id和name, 条件不变


explain select id,name from trade_user where name like '%51';

COPY

结果和第二条SQL一模一样。

file

第四条SQL:查询用户的id,name和birthday,查询条件不变


explain select id,name,birthday from trade_user where name like '%51';

COPY

结果和第一条一模一样,全表扫描

file

Limit M OFFSET N


select * from trade_user limit 10 offset 20000000

COPY

Limit M OFFSET N闭坑

参考:MySQL limit N offset M 速度慢?来实际体验下

聚合查询

计数


// 全表计数
select count(*) from trade_user;

// 去重计数
select count(distinct name) from trade_user;

// 全表计数
select count(1) from trade_user;

// 字段计数,id如果有值为null,不会计入总数
select count(id) from trade_user;

// 分组计数
select count(*) from trade_user group by email;

COPY

又来讨论下count(*), count(1), count(column)了

count()计数:只有有行就+1,不管列值
count(1):
count(column): 列不为空+1,否则+0
结果:count(
)=count(1)=count(id)=count(非空column) !=count(可空列)

什么?我说的有毛病?那你还是看看Stackoverflow的说法?:What is better in MYSQL count(*) or count(1)?
看看MySQL官网的说法:MySQL Aggregation Function

*InnoDB handles SELECT COUNT() and SELECT COUNT(1) operations in the same way. There is no performance difference.MyISAM COUNT(1) is only subject to the same optimization if the first column is defined as NOT NULL.**

求和


select sum(paid_amount) from trade_order;

COPY

求和避坑

sum()会返回空值,这时候需要应用层判断或者使用数据库函数处理:


select COALESCE(sum(paid_amount), 0) from trade_order;
select IFNULL(sum(paid_amount), 0) from trade_order;

COPY

sum()可能会溢出,不过一般不用担心,一般处理钱的问题都是bigint并且以分为单位,如果溢出了说明你家公司效益逆天,扶摇直上九万里。

sum()可能会有性能问题,万一你的条件是全表扫描并且数据很多,那就GG了,所以可以用替代方案,比如:redis 的incr一直加或者减就可以替代某一类的sum。

取平均值


select avg(paid_amount) from trade_order;

COPY

最大值


select max(paid_amount) from trade_order;

COPY

最小值


select min(paid_amount) from trade_order;

COPY

连接(join)

内连接(inner join)

匹配条件左右两边都有值时才会返回记录:


select * from trade_user u inner join trade_order r on r.user_id=u.id;

COPY

inner join在一对多关系下会返回重复记录,记得去重。

左连接(left join)

左边的表有值但右边没有匹配,返回的结果集右表为空:


select * from trade_user u left join trade_order r on r.user_id=u.id;

COPY

那个是左表?trade_user因为它在JOIN的右边。

left join在一对多关系下会返回重复记录,记得去重。

右连接(right join)

左边的表有值但右边没有匹配,返回的结果集右表为空:


select * from trade_user u right join trade_order r on r.user_id=u.id;

COPY

那个是右表?trade_order因为它在JOIN的右边。

right join在一对多关系下会返回重复记录,记得去重。

笛卡尔积(product)

-- 第一种笛卡尔积方式
select * from trade_user, trade_order;

-- 第二种笛卡尔积方式
select * from trade_user join trade_order;

COPY

子查询(subqueries)

子查询是一个嵌套在另一个SQL语句中的SELECT语句。其目的通常是处理复杂的业务逻辑,处理后的结果可以作为外部查询条件使用。子查询可以被嵌套在 SELECT、INSERT、UPDATE、DELETE 语句以及另一个子查询中。
子查询分类:

  1. 相关子查询(correlated subquery)非相关子查询是一个独立的查询,子查询的结果不依赖于外层查询。他们通常在执行时首先被解析和执行,然后将结果集返回给外部查询以供使用。
  2. 非相关子查询(non-correlated subquery) 相关子查询依赖于外层查询,并且每一行外层查询的结果都需要执行一次子查询。相关子查询在执行时,对外层查询的每一行记录都进行一次子查询操作。

非相关子查询

select id from trade_user where id > 100000不依赖外层的查询。


select * from trade_order where user_id in (select id from trade_user where id > 100000);

COPY

相关子查询

子查询select 1 from trade_order o where o.user_id=u.id依赖了外层查询。


select u.id, u.name from trade_user u where u.id=1 and exists (select 1 from trade_order o where o.user_id=u.id) ;

COPY

排序(ordering)


order by age;

order by age desc;

order by age desc, id asc;

order by age + id;

order by date(created_at);

order by alias_age;

COPY

排序避坑

order by age + id;这条SQL在遇到NULL值的时候会有问题,比如我们的SQL如下:


select * from trade_user order by age + id asc limit 10;

COPY

结果输出:


+--------+------------+------------------+------+---------------------+---------------------+---------------------+
| id     | name       | email            | age  | birthday            | created_at          | updated_at          |
+--------+------------+------------------+------+---------------------+---------------------+---------------------+
| 525592 | fofcn.tech | fofcn@fofcn.tech | NULL | 1989-05-25 00:00:00 | 2024-04-10 09:31:24 | 2024-04-10 09:31:24 |
|      1 | b          | user9@test.com   |   12 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
|      4 | User 12    | user12@test.com  |   15 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
|      8 | User 16    | user16@test.com  |   11 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
|      3 | User 11    | user11@test.com  |   19 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
|      5 | User 13    | user13@test.com  |   18 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
|     15 | User 5     | user5@test.com   |    8 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
|     16 | User 6     | user6@test.com   |    7 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
|     10 | User 18    | user18@test.com  |   13 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
|      6 | User 14    | user14@test.com  |   28 | 2008-11-13 04:29:47 | 2024-03-19 13:31:11 | 2024-03-19 13:31:11 |
+--------+------------+------------------+------+---------------------+---------------------+---------------------+
10 rows in set (0.27 sec)

COPY

这很疑惑啊,id=525592出来凑什么热闹?分析下?这条数据和其他的区别就在于age=NULL,再翻翻数据库的文档:对NULL列做运算则结果是NULL,NULL在排序中被视为最小值。举个例子:


mysql> select 1+null;
+--------+
| 1+null |
+--------+
|   NULL |
+--------+
1 row in set (0.00 sec)

select * from (
select 1+null as id
union all select 2 as id
union all select 3 as id
union all select 4 as id
) t order by id asc limit 2;

+------+
| id   |
+------+
| NULL |
|    2 |
+------+
2 rows in set (0.00 sec)

select * from (
select 1+null as id
union all select 2 as id
union all select 3 as id
union all select 4 as id
) t order by id desc limit 2;

+------+
| id   |
+------+
|    4 |
|    3 |
+------+
2 rows in set (0.00 sec)

COPY

分组(grouping)

分组(grouping)可能遇到的问题

  1. this is incompatible with sql_mode=only_full_group_by
    原因:

     COPY

SQL-92及更早版本的标准不允许在查询中,选择列表,HAVING条件或ORDER BY列表引用非聚合列,且这些列在GROUP BY子句中未被命名。例如,以下查询在SQL-92标准中是非法的,因为select列表中的非聚合列name未出现在GROUP BY中:

SELECT o.custid, c.name, MAX(o.payment)
FROM orders AS o, customers AS c
WHERE o.custid = c.custid
GROUP BY o.custid;

要使此查询在SQL-92中合法,必须从select列表中省略name列,或者在GROUP BY子句中命名它。

SQL:1999及以后的版本允许这种非聚合列存在于可选功能T301中,前提是它们在功能上依赖于GROUP BY列:如果name和custid之间存在这样的关系,查询就是合法的。例如,如果custid是customers的主键,那么这种情况就会发生。


解决方式:
```sql
SELECT @@session.sql_mode;

set @@session.sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
set @@global.sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
  1. NULL值

union/union all

UNION 命令用于合并两个或两个以上 SELECT 语句的结果集。同时,UNION 会删除结果集中重复的行。

UNION 通过比较结果集中所有的行来去除重复行,这一过程对应的是一个完全的行比较,只有两行数据中的所有字段都完全一致,系统才会识别这两行为重复并进行去重。

需要注意的是,UNION 命令只会删除完全一样(所有列的值都一样)的行。
当使用 UNION 时,每个 SELECT 语句必须具有相同数量的列,列也必须具有相像的数据类型,且顺序也必须相同。

如果你不想去除结果集中的重复行,可以使用 UNION ALL。相比 UNION,UNION ALL 不会去除重复行,所以它的执行速度比 UNION 更快。


select 1 as id
union 
select 1 as id
union 
select 2 as id;

select 1 as id
union all
select 1 as id
union all
select 2 as id;

COPY

union 和 union all 区别?

区别UNIONUNION ALL
去重UNION 会自动去除所有重复的行UNION ALL 不会去除重复的行
性能因为去重的操作,UNION 的性能可能比 UNION ALL 的性能较低UNION ALL 的性能一般会优于 UNION
结果集大小UNION 返回的结果集可能小于或等于两个查询结果集的和UNION ALL 返回的结果集大小恰好等于两个查询结果集的和
操作UNION 对两个结果集执行集合并(set union)UNION ALL 合并两个结果集,包括所有重复的行

select 1 as id
union 
select 1 as id
union 
select 2 as id;

+----+
| id |
+----+
|  1 |
|  2 |
+----+
2 rows in set (0.00 sec)

select 1 as id
union all
select 1 as id
union all
select 2 as id;

+----+
| id |
+----+
|  1 |
|  1 |
|  2 |
+----+
3 rows in set (0.00 sec)

select 1 as id, 2 as name
union 
select 1 as id, 3 as name 
union 
select 2 as id, 1 as name;

+----+------+
| id | name |
+----+------+
|  1 |    2 |
|  1 |    3 |
|  2 |    1 |
+----+------+
3 rows in set (0.00 sec)

select 1 as id, 2 as name
union 
select 1 as id, 2 as name 
union 
select 2 as id, 1 as name;

+----+------+
| id | name |
+----+------+
|  1 |    2 |
|  2 |    1 |
+----+------+
2 rows in set (0.00 sec)

COPY

窗口函数(window function)

窗口函数与Group By比较

空值处理 (null handling)

以下是上述内容的口语化版本:

  1. 表示不知道或缺失的信息: NULL 可以理解为某条数据中一种"我不知道"或者"信息缺失"的状态。

  2. 无法用常规方式比较: NULL 不等于任何值,包括它自己。我们不能用 =, <,或者 <> 这样的运算符来检查一个值是否是 NULL,但可以使用 IS NULL 或 IS NOT NULL

  3. 在排序时的特别位置:在排序时,比如使用 ORDER BYNULL 通常会被看作是最小的值。如果你是升序排序,NULL 会排在最前;如果是降序排序,NULL 会放在最后。

  4. 参与运算结果还是未知:在做数学运算时,无论是和什么数运算,只要有 NULL 参与,结果就是 NULL

  5. 在统计运算中被忽视:在做统计运算时,大多数时候 NULL 会被忽略掉,但如果我们计算所有的行数,比如用 COUNT(*)NULL 就会被计进去。

  6. 在逻辑判断中为假: 在 MySQL 中,NULL 和零都被看作是假,而其他的非零和非 NULL 的值都被视为真。

  7. 可以被加索引:如果你使用的是 MyISAM,InnoDB 或者 MEMORY 这些存储引擎,你可以在可能有 NULL 值的列上加索引。

  8. 在分组中被认为是一样的: 当我们做分组计算的时候,所有的 NULL 都被认为是一样的。

  9. 适用于所有数据类型: 几乎所有的数据类型都可以包含 NULLNULL 本身并没有数据类型。

  10. 不阻止插入零或空字符串:即使列被设定为 NOT NULL 也完全可以插入零或空字符串,因为真正被阻止的是 NULL,而零和空字符串都是具体的值。

  11. 无法通过 in 运算符查询:例如,语句 select * from trade_user where age in (null) 不会返回任何结果,因为 NULL 不能被直接查询,如果需要查询 NULL 值,应使用 IS NULL 语句。

更新

删除

索引(indexing)

调优(tuning)

  1. Difference Between Explain And Explain Analyze

事务(transaction)

关于事务和事务隔离级别可以参考:

  1. MySQL 事务与事务隔离级别
  2. 快分清MySQL当前读、快照读和幻读关系
  3. 当前读,快照读,脏读,幻读和不可重复读

锁 (locks)

关于锁可以参考:

  1. MySQL 锁合集与事务隔离级别

internals

join

关于join可以参考:

  1. MySQL 排序的那些事儿
  2. MySQL高效连接查询秘籍:掌握这四种Join算法

事务(Transaction)

  1. MySQL 牺牲性能产出的ACID

MVVC

  1. Multiversion concurrency control

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PostgreSQL入门到实战-第二十二弹

PostgreSQL入门到实战 PostgreSQL中表连接操作(六)官网地址PostgreSQL概述PostgreSQL中self-join命令理论PostgreSQL中self-join命令实战更新计划 PostgreSQL中表连接操作(六) 使用PostgreSQL自联接技术来比较同一表中的行 官网地址 声明: 由于操作系统, 版本更新等原因, 文…

springCloud项目打包 ,maven package或install打包报错

解决思路一&#xff1a; <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.7.RELEASE</version></plugin><plugin>&…

新天龙八部3永恒经典之江山策仿官方_源码架设教程

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了 一. 效果演示 新天龙…

opencv 多线程读取和显示摄像头【python源码】

在Python中&#xff0c;使用OpenCV库实现多线程读取和显示摄像头通常涉及创建多个线程&#xff0c;每个线程负责从摄像头捕获视频帧并显示它们。但是&#xff0c;请注意&#xff0c;OpenCV本身并不直接支持多线程显示&#xff0c;因为cv2.imshow通常是在主线程中运行的。然而&a…

【C++ STL序列容器】deque 双端队列

文章目录 【 1. 基本原理 】【 1. deque 的创建 】1.1 创建一个空的 deque1.2 创建一个 n 个默认值的 deque1.3 创建一个 n 个指定值的 deque1.4 通过一个 deque 初始化另一个 deque1.5 通过基础容器来初始化 queue 容器适配器 【 3. deque 支持的成员函数 】 【 1. 基本原理 】…

什么是HW,企业如何进行HW保障?

文章目录 一、什么是HW二、HW行动具体采取了哪些攻防演练措施三、攻击方一般的攻击流程和方法四、企业HW保障方案1.建意识2.摸家底3.固城池4.配神器5.增值守 一、什么是HW 网络安全形势近年出现新变化&#xff0c;网络安全态势变得越来越复杂&#xff0c;黑客攻击入侵、勒索病…

【C语言】双向链表详解

文章目录 关于双向链表双向链表的初始化双向链表的打印双向链表方法调用 - 尾删为例双向链表的查找 - 指定位置之后插入为例双向链表结束 - 链表的销毁小结及整体代码实现 关于双向链表 首先链表有8种基本分法 其中在笔者之前文章种详细介绍的 单链表 是不带头单项不循环链表…

Leetcode算法训练日记 | day24

一、组合问题 1.题目 Leetcode&#xff1a;第 77 题 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4…

别人起诉你你不服,到底是写上诉状还是反诉状?李秘书讲写作讲给你听!

别人起诉你你不服&#xff0c;到底是写上诉状还是反诉状&#xff1f;李秘书讲写作讲给你听&#xff01; 别人向法院告了你&#xff0c;你不服气&#xff0c;这时你可能想到要申辩或“报复”&#xff0c;但又不知是写上诉状呢还是写反诉状呢&#xff1f;#李秘书讲写作#这节就讲…

深度学习入门(2)

一。Matplotlib模块添加 Matplotlib是用于绘制图形的库&#xff0c;使用 Matplotlib 可以轻松地绘制图形和实现数据的可视化。 pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple 二、绘制简单图形 import numpy as np import matplotlib.pyplot as plt #…

【电控笔记7】速度回路+系统延迟

2.3.1速度回路pi控制器设计 Tl:负载转矩

ubuntu16.04安装Eclipse C/C++

1.安装 JDK 官网源码安装 首先打开JDK官网&#xff0c;JDK1.8的下载网址为&#xff1a;https://www.oracle.com/cn/java/technologies/downloads/#java8-windows&#xff0c;进入到网址如下图所示&#xff1a; 向下滑动到 JDK1.8的下载界面&#xff0c;如下图所示&#xff1a…

面试题:重写equals(),为什么还要重写hashcode()

认识equals(): Object类中的equals; public boolean equals(Object obj) {return (this obj);}当我们没有重写equals&#xff08;&#xff09;&#xff0c;我们是调用父类object中的方法&#xff0c;比较的是对象的内存地址 重写equals后&#xff0c; public class Student…

【群智能算法改进】一种改进的鹦鹉优化算法 改进鹦鹉优化器 IPO算法【Matlab代码#73】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始鹦鹉优化算法PO2. 改进后的IPO算法2.1 自适应切换因子2.2 混合柯西和高斯变异 3. 部分代码展示4. 仿真结果展示5. 资源获取 【获取资源请见文章第5节&#xff1a;资源获取】 1. 原始鹦鹉优化算法PO 鹦鹉优化算法…

java程序 .exe启动nginx防止重复启动,已解决

java代码生成好的.exe启动nginx服务程序 根据nginx占用端口来解决nginx服务重复启动问题&#xff08;下面代码了解代码逻辑后根据自己的业务需求修改即可&#xff09; 代码&#xff1a; package org.example;import javax.swing.*; import java.awt.*; import java.io.*; …

6.Burp Suite 入门篇 —— Burp Scanner 漏洞扫描

目录 前言 扫描网站 打开扫描启动页面 填写目标网站地址 配置扫描参数 开始扫描 查看网站结构 查看扫描结果 生成漏洞扫描报告 选择扫描结果 配置报告选项 生成并保存报告 查看或分享报告 前言 Burp Scanner 既可以是独立的全自动扫描器&#xff0c;也可以在手动…

约瑟夫问题---C++

今天来讲一道饶有名气的题目&#xff0c;约瑟夫问题 约瑟夫问题 这道题目有许多大佬用队列、递归、链表来解这道题目而这题的难度也确实非同小可&#xff01; 可是你们难道没有想过&#xff1f;用数组去解决吗&#xff1f;没错一维数组&#xff01;为了想出解决办法我掉了23根头…

福建单航次最大批量汽车“出海”

3月12日这一天&#xff0c;在福州海关的严密监管下&#xff0c;共有4000辆上汽名爵品牌的汽车被高效有序地装载到“安吉智慧”号滚装船上&#xff0c;这批车辆即将启程前往荷兰、埃及、英国等多个海外市场。在这批出口汽车中&#xff0c;新能源车型占据了显著的比例&#xff0c…

飞书api增加权限

1&#xff0c;进入飞书开发者后台&#xff1a;飞书开放平台 给应用增加权限 2&#xff0c;进入飞书管理后台 https://fw5slkpbyb3.feishu.cn/admin/appCenter/audit 审核最新发布的版本 如果还是不行&#xff0c;则需要修改数据权限&#xff0c;修改为全部成员可修改。 改完…

【避坑NOC】关于举办2023-2024学年全国中小学信息技术创新与实践大赛的通知

01-关于2024年赛事信息 2024年NOC比赛已经可以开始报名了,今年的报名比去年提前了接近2个月,这项白名单赛事去年问题比较多,推荐指数不高,当然,如果愿意参加的,下面老师还有一些避坑信息,参加的同学和机构老师必须要了解。先来看一下完整的通知: 02-比赛类别 共设有三…