├── 1-MySQL基本操作.md ├── 2-数据过滤.md ├── 3-数据处理、汇总和分组.md ├── 4-联结.md ├── 5-组合查询、全文本搜索.md ├── 6-其他操作.md ├── 7-管理.md ├── README.md ├── create.sql └── populate.sql /1-MySQL基本操作.md: -------------------------------------------------------------------------------- 1 | # MySQL基本操作 2 | ``` 3 | $ mysql -u root -p 4 | ``` 5 | `-u` 用户名,`-p` 输入密码, `-h` 主机名, `-P` 端口, 6 | `mysql --help` 命令行选项和参数列表 7 | 8 | 连接到数据库需要:主机名(本地为localhost)、端口(如果使用默认端口3306之外的端口)、合法的用户名、用户口令(如果需要) 9 | 10 | 可能需要root权限 11 | 12 | ## 使用MySQL 13 | 下载create.sql和populate.sql两个sql脚本文件,其中,create.sql包含创建6个数据库表的MySQL语句,populate.sql包含用来填充这些表的INSERT语句。执行下列操作: 14 | ``` 15 | --创建数据库 16 | CREATE DATABASE crashcourse; 17 | --使用数据库 18 | --必须先使用USE打开数据库,才能读取其中的数据。 19 | USE crashcourse; 20 | --执行sql脚本 21 | --需要指定文件的完整路径 22 | --先执行create.sql,再执行populate.sql 23 | SOURCE ~/create.sql; 24 | SOURCE ~/populate.sql 25 | ``` 26 | 以上为准备工作。 27 | 28 | ### 了解数据库和表 29 | ``` 30 | -- 显示可用的数据库列表 31 | SHOW DATABASES; 32 | 33 | -- 获得一个数据库内的表的列表 34 | SHOW TABLES; 35 | 36 | -- 用来显示表列 37 | SHOW COLUMNS FROM customers; 38 | DESCRIBE customers; 39 | 40 | -- 其他SHOW语句 41 | SHOW STATUS -- 用于显示广泛的服务器状态信息 42 | SHOW CREATE DATABASE -- 显示创建特定数据库的MySQL语句 43 | SHOW CREATE TABLE -- 显示创建特定表的语句 44 | SHOW GRANTS -- 显示授予用户(所有用户或特定用户)的安全权限 45 | SHOW ERRORS -- 显示服务器错误 46 | SHOW WARNINGS -- 警告信息 47 | ``` 48 | 49 | ## 检索数据 50 | ### SELECT语句 51 | ``` 52 | -- 检索单个列 53 | -- 检索products表中的prod_name列 54 | SELECT prod_name FROM products; 55 | 56 | -- 检索多个列 57 | -- 检索products表中的prod_id,prod_name和prod_price列 58 | SELECT prod_id, prod_name, prod_price FROM products; 59 | 60 | -- 检索所有列 61 | --检索products表中的所有列 62 | SELECT * FROM products; 63 | 64 | -- 检索不同的行 65 | -- DISTINCT关键字必须直接放在列名的前面,不能部分使用DISTINCT,DISTINCT关键字应用于所有列而不仅是前置它的列。 66 | SELECT DISTINCT vend_id FROM products; 67 | 68 | -- 限制结果,指定返回前几行 69 | -- 返回不多于5行 70 | SELECT prod_name FROM products LIMIT 5; 71 | -- 返回从第5行开始的5行 72 | SELECT prod_name FROM products LIMIT 5,5; 73 | ``` 74 | **检索出来的第一行为行0**,因此`LIMIT 1,1`检索出来的是第二行而不是第一行 75 | MySQL 5 支持LIMIT的另一种替代语法 76 | `LIMIT 4 OFFSET 3`为从行3开始取4行,同`LIMIT 3,4` 77 | 78 | ``` 79 | -- 使用完全限定的表名 80 | SELECT products.prod_name FROM products; 81 | SELECT products.prod_name FROM crashcoures.products; 82 | ``` 83 | ## 排序检索数据 84 | ``` 85 | -- 排序数据 86 | SELECT prod_name 87 | FROM products 88 | ORDER BY prod_name; 89 | -- 按多个列排序 90 | SELECT prod_id, prod_price, prod_name 91 | FROM products 92 | ORDER BY prod_price, prod_name; 93 | ``` 94 | 对于上述例子中的输出,仅在多个行具有相同的prod_price 值时才对产品按prod_name进行排序。如果prod_price列中所有的值都是唯一的,则不会按prod_name排序。 95 | 96 | ``` 97 | -- 指定排序方向 98 | -- 默认升序排序,降序使用DESC关键字 99 | SELECT prod_id, prod_price, prod_name 100 | FROM products 101 | ORDER BY prod_price DESC; 102 | 103 | SELECT prod_id, prod_price, prod_name 104 | FROM products 105 | ORDER BY prod_price DESC, prod_name; 106 | ``` 107 | DESC关键字只应用到直接位于其前面的列名。上例中,只对prod_price列指定DESC,对prod_name列不指定。 108 | 升序关键字ASC,可省略 109 | 110 | ### 找出一列中最高或最低的值 111 | ``` 112 | SELECT prod_proce FROM products 113 | ORDER BY prod_price DESC LIMIT 1; 114 | ``` 115 | 给出ORDER BY句子时,应保证位于FROM句子之后,如果使用LIMIT,应位于ORDER BY之后。 116 | 117 | ## 过滤数据 118 | ### 使用WHERE子句 119 | ``` 120 | -- 返回prod_price为2.50的行 121 | SELECT prod_name, prod_price FROM products WHERE prod_price = 2.50 122 | ``` 123 | ### WHERE子句操作符 124 | 125 | | 符号 | 说明 | 126 | |--------|--------| 127 | |=|等于| 128 | |<>|不等于| 129 | |!=|不等于| 130 | |<|小于| 131 | |<=|小于等于| 132 | |>|大于| 133 | |>=|大于等于| 134 | |BETWEEN|在指定的两个值之间| 135 | 136 | ``` 137 | -- 检查单个值 138 | -- 返回prod_name为Fuses的一行(匹配时默认不区分大小写) 139 | SELECT prod_name, prod_price FROM products WHERE prod_name = 'fuses'; 140 | -- 列出小于10美元的所有产品 141 | SELECT prod_name, prod_price FROM products WHERE prod_price < 10; 142 | -- 列出小于等于10美元的所有产品 143 | SELECT prod_name, prod_price FROM products WHERE prod_price <= 10; 144 | 145 | -- 不匹配检查 146 | -- 列出不是1003的所有产品 147 | SELECT vend_id, prod_name FROM products WHERE vend_id <> 1003; 148 | SELECT vend_id, prod_name FROM products WHERE vend_id != 1003; 149 | 150 | -- 范围值检查 151 | -- 检索价格在5-10美元之间的所有产品 152 | SELECT prod_name, prod_price FROM products 153 | WHERE prod_price BETWEEN 5 AND 10; 154 | 155 | -- 空值检查 156 | -- 返回价格为空的所有产品 157 | SELECT prod_name FROM products WHERE prod_price IS NULL; 158 | ``` 159 | 160 | 161 | ## 创建和操纵表 162 | 163 | ### 创建表 164 | #### 创建表基础 165 | CREATE TABLE 166 | - 新表的名字,在关键字CREATE TABLE之后给出 167 | - 表列的名字和定义,用逗号分隔。 168 | 例: 169 | ``` 170 | CREATE TABLE customers 171 | ( 172 | cust_id int NOT NULL AUTO_INCREMENT, 173 | cust_name char(50) NOT NULL, 174 | cust_address char(50) NULL, 175 | cust_city char(50) NULL, 176 | cust_state char(5) NULL, 177 | cust_zip char(10) NULL, 178 | cust_country char(50) NULL, 179 | cust_contact char(50) NULL, 180 | cust_email char(255) NULL, 181 | PRIMARY KEY (cust_id) 182 | ) ENGINE=InnoDB; 183 | ``` 184 | 185 | 在创建新表时,指定的表名必须不存在,否则将出错。如果要防止意外覆盖已有的表,SQL要求首先手工删除该表,然后再重建它,而不是简单地用创建表语句覆盖它。 186 | 187 | 如果你仅想在一个表不存在时创建它,应该在表名后给出IF NOT EXISTS。这样做不检查已有表的模式是否与你打算创建的表模式相匹配。它只是查看表名是否存在,并且仅在表名不存在时创建它。 188 | 189 | #### 使用NULL值 190 | 每个表列或者是NULL列或者是NOT NULL列,这种状态在创建时由表的定义规定 191 | 例: 192 | ``` 193 | CREATE TABLE orders 194 | ( 195 | order_num int NOT NULL AUTO_INCREMENT, 196 | order_date datetime NOT NULL, 197 | cust_id int NOT NULL, 198 | PRIMARY KEY (order_num) 199 | ) ENGINE-InnoDB; 200 | ``` 201 | 202 | 例:混合了NULL和NOT NULL列的表 203 | ``` 204 | CREATE TABLE vendors 205 | ( 206 | vend_id int NOT NULL AUTO_INCREMENT, 207 | vend_name char(50) NOT NULL, 208 | vend_address char(50) NULL, 209 | vend_city char(50) NULL, 210 | vend_state char(5) NULL, 211 | vend_zip char(10) NULL, 212 | vend_country char(50) NULL, 213 | PRIMARY KEY(vend_id) 214 | ) ENGINE = InnoDB; 215 | ``` 216 | 217 | #### 主键 218 | 主键值必须唯一。如果主键使用单个列,则它的值必须唯一。如果使用多个列,则这些列的组合值必须唯一。 219 | 220 | 例:创建多个列组成的主键 221 | ``` 222 | CREATE TABLE orderitems 223 | ( 224 | order_num int NOT NULL, 225 | order_item int NOT NULL, 226 | prod_id char(10) NOT NULL, 227 | quantity int NOT NULL, 228 | item_price decimal(8,2) NOT NULL, 229 | PRIMARY KEY (order_num, order_item) 230 | )ENGiNE = InnoDB; 231 | ``` 232 | 233 | #### 使用AUTO_INCREMENT 234 | AUTO_INCREMENT告诉MySQL,本列每当增加一行时自动增量。每次 执行一个INSERT操作时,MySQL自动对该列增量(从而才有这个关键字AUTO_INCREMENT),给该列赋予下一个可用的值。这样给每个行分配一个唯一的cust_id,从而可以用作主键值。 235 | 236 | 覆盖AUTO_INCREMENT:如果一个列被指定为AUTO_INCREMENT,则它需要使用特殊的值吗?你可以简单地INSERT语句中指定一个值,只要它是唯一的(至今尚未使用过)即可,该值将被用来替代自动生成的值。后续的增量将开始使用该手工插入的值。 237 | 238 | #### 指定默认值 239 | ``` 240 | CREATE TABLE orderitems 241 | ( 242 | order_num int NOT NUL, 243 | order_item int NOT NULL, 244 | prod_id char(10) NOT NULL, 245 | quantity int NOT NULL DEFAULT 1, 246 | item_price decimal(8,2) NOT NULL, 247 | PRIMARY KEY (order_num,order_item) 248 | ) ENGINE = InnoDB; 249 | ``` 250 | MySQL不允许使用函数作为默认值,只支持常量 251 | 252 | #### 引擎类型 253 | - InnoDB是一个可靠的事务处理引擎,它不支持全文本搜索; 254 | - MEMORY在功能等同于MyISAM,但由于数据存储在内存(不是磁盘) 中,速度很快(特别适合于临时表); 255 | - MyISAM是一个性能极高的引擎,它支持全文本搜索,但不支持事务处理。 256 | 引擎类型可以混用。 257 | 外键不能跨引擎 混用引擎类型有一个大缺陷。外键(用于强制实施引用完整性)不能跨引擎,即使用一个引擎的表不能引用具有使用不同引擎的表的外键。 258 | 259 | ### 更新表 260 | 使用ALTER TABLE更改表的结构,必须给出以下信息: 261 | - 在ALTER TABLE之后给出要更改的表名(该表必须存在,否则将出错); 262 | - 所做更改的列表。 263 | 264 | 例: 265 | ``` 266 | ALTER TABLE vendors 267 | ADD vend_phone CHAR(20); 268 | ``` 269 | 例:删除刚增加的列 270 | ``` 271 | ALTER TABLE vendors 272 | DROP COLUMN vend_phone; 273 | ``` 274 | 275 | 276 | 为了对单个表进行多个更改,可以使用单条ALTER TABLE语句,每个更改用逗号分隔 277 | 278 | 复杂的表结构更改一般需要手动删除过程,它涉及以下步骤: 279 | - 用新的列布局创建一个新表; 280 | - 使用INSERT SELECT语句从旧表复制数据到新表。如果有必要,可使用转换函数和计算字段; 281 | - 检验包含所需数据的新表; 282 | - 重命名旧表(如果确定,可以删除它); 283 | - 用旧表原来的名字重命名新表; 284 | - 根据需要,重新创建触发器、存储过程、索引和外键。 285 | 286 | 使用ALTER TABLE要极为小心,应该在进行改动前做一个完整的备份(模式和数据的备份)。数据库表的更改不能撤销,如果增加了不需要的列,可能不能删除它们。类似地,如果删除了不应该删除的列,可能会丢失该列中的所有数据。 287 | 288 | ### 删除表 289 | ``` 290 | DROP TABLE customers2; 291 | ``` 292 | 293 | ### 重命名表 294 | ``` 295 | RENAME TABLE customers2 TO customers; 296 | 297 | #对多个表重命名 298 | RENAME TABLE backup_customers TO customers, 299 | backup_vendors TO vendors, 300 | backup_products TO products; 301 | ``` 302 | 303 | ## 插入数据 304 | INSERT 305 | - 插入完整的行 306 | - 插入行的一部分 307 | - 插入多行 308 | - 插入某些查询的结果 309 | 310 | ### 插入完整的行 311 | 312 | ``` 313 | INSERT INTO Customers 314 | VALUES(NULL, 315 | 'Pep E. LaPew', 316 | '100 Main Street', 317 | 'Los Angles', 318 | 'CA', 319 | '90046', 320 | 'USA', 321 | NULL, 322 | NULL); 323 | ``` 324 | 语法简单但不安全。更安全的方法为: 325 | ``` 326 | INSERT INTO customers(cust_name, 327 | cust_address, 328 | cust_city, 329 | cust_state, 330 | cust_zip, 331 | cust_country, 332 | cust_contact, 333 | cust_email) 334 | VALUES('Pep E. LaPew', 335 | '100 Main Street', 336 | 'Los Angeles', 337 | 'CA', 338 | '90046' 339 | 'USA' 340 | NULL, 341 | NULL); 342 | #下面的INSERT语句填充所有列(与前面的一样),但以一种不同的次序填充。 343 | #因为给出了列名,所以插入结果仍然正确: 344 | INSERT INTO customers(cust_name, 345 | cust_contact, 346 | cust_email, 347 | cust_address, 348 | cust_city, 349 | cust_state, 350 | cust_zip, 351 | cust_country) 352 | VALUES('Pep E. LaPew', 353 | NULL, 354 | NULL, 355 | '100 Main Street', 356 | 'Los Angles', 357 | 'CA', 358 | '90046', 359 | 'USA'); 360 | ``` 361 | 不管哪种INSSERT语法,都必须给出VALUES的正确数目,如果不提供列名,则必须给每个表提供一个值。如果提供列名,则必须对每个列出的列值给出一个值。 362 | 363 | 列名被明确列出时,可以省略列,如果表的定义允许则可以省略列 364 | 365 | - 该列定义为允许NULL值(无值或空值) 366 | - 在表定义中给出默认值。 367 | 368 | ### 插入多个行 369 | ``` 370 | INSERT INTO customers(cust_name, 371 | cust_address, 372 | cust_city, 373 | cust_state, 374 | cust_zip, 375 | cust_country) 376 | VALUES('Pep E. LaPew', 377 | '100 Main Street' 378 | 'Los Angeles', 379 | 'CA', 380 | '90046', 381 | 'USA'); 382 | INSERT INTO customers(cust_name, 383 | cust_address, 384 | cust_city, 385 | cust_state, 386 | cust_zip, 387 | cust_country) 388 | VALUES('M. Martian', 389 | '42 Galaxy Way' 390 | 'New York', 391 | 'NY', 392 | '11213', 393 | 'USA'); 394 | 395 | #使用组合句 396 | INSERT INTO customers(cust_name, 397 | cust_address, 398 | cust_city, 399 | cust_state, 400 | cust_zip, 401 | cust_country) 402 | VALUES('Pep E. LaPew', 403 | '100 Main Street' 404 | 'Los Angeles', 405 | 'CA', 406 | '90046', 407 | 'USA'), 408 | 409 | ('M. Martian', 410 | '42 Galaxy Way' 411 | 'New York', 412 | 'NY', 413 | '11213', 414 | 'USA'); 415 | 416 | #单条INSERT语句有多组值,每组值用一对圆括号括起来,用逗号分隔。 417 | ``` 418 | ### 插入检索出的数据 419 | 420 | ``` 421 | INSERT INTO customers(cust_id, 422 | cust_contact, 423 | cust_email, 424 | cust_name, 425 | cust_address, 426 | cust_city, 427 | cust_state, 428 | cust_zip, 429 | cust_country) 430 | SELECT cust_id, 431 | cust_contact, 432 | cust_email, 433 | cust_name, 434 | cust_address, 435 | cust_city, 436 | cust_state, 437 | cust_zip, 438 | cust_country 439 | FROM custnew; 440 | ``` 441 | 442 | ## 更新和删除数据 443 | ### 更新数据 444 | UPDATE 445 | - 更新表中特定行 446 | - 更新表中所有行 447 | 例:客户10005更新电子邮件 448 | ``` 449 | UPDATE customers 450 | SET cust_email = 'elmer@fudd.com' 451 | WHERE cust_id = 10005; 452 | ``` 453 | 例:更新多个列 454 | ``` 455 | UPDARTE customers 456 | SET cust_name = 'The Fudds', 457 | cust_email = 'elmer@fudd.com' 458 | WHERE cust_id = 10005; 459 | ``` 460 | 在更新多个列时,只需要使用单个SET命令,每个“列=值”对之间 用逗号分隔(最后一列之后不用逗号)。在此例子中,更新客户10005的cust_name和cust_email列。 461 | 462 | IGNORE关键字:如果用UPDATE语句更新多行,并且在更新这些 行中的一行或多行时出一个现错误,则整个UPDATE操作被取消 (错误发生前更新的所有行被恢复到它们原来的值)。为即使是发生错误,也继续进行更新,可使用IGNORE关键字,如下所示:`UPDATE IGNORE customers…` 463 | 464 | 为了删除某列的值,可以设置为NULL 465 | ``` 466 | UPDATE customers 467 | SET cust_email = NULL 468 | WHERE cust_id = 10005; 469 | ``` 470 | 471 | ### 删除数据 472 | 使用DELETE语句 473 | - 从表中删除特定的行 474 | - 从表中删除所有的行 475 | 476 | ``` 477 | DELETE FROM customers 478 | WHERE cust_id = 10006; 479 | ``` 480 | ### 更新和删除的指导原则 481 | 下面是许多SQL程序员使用UPDATE或DELETE时所遵循的习惯。 482 | - 除非确实打算更新和删除每一行,否则绝对不要使用不带WHERE子句的UPDATE或DELETE语句。 483 | - 保证每个表都有主键,尽可能像WHERE子句那样使用它(可以指定各主键、多个值或值的范围)。 484 | - 在对UPDATE或DELETE语句使用WHERE子句前,应该先用SELECT进行测试,保证它过滤的是正确的记录,以防编写的WHERE子句不正确。 485 | - 使用强制实施引用完整性的数据库,这样MySQL将不允许删除具有与其他表相关联的数据的行。 486 | 487 | ## 补充 488 | MySQL的注释方法 489 | 一共有三种,分别为 490 | ``` 491 | #单行注释可以使用"#" 492 | -- 单行注释也可以使用"--",注意与注释之间有空格 493 | /* 494 | 用于多行注释 495 | */ 496 | ``` 497 | -------------------------------------------------------------------------------- /2-数据过滤.md: -------------------------------------------------------------------------------- 1 | ## 数据过滤 2 | 3 | ``` 4 | -- AND操作符 5 | -- 检索由1003制造且价格小于等于10美元的所有产品的名称和价格 6 | SELECT prod_id, prod_price, prod_name FROM products 7 | WHERE vend_id = 1003 AND prod_price <= 10; 8 | 9 | -- OR操作符 10 | -- 检索由1002和1003制造的产品的名称和价格 11 | SELECT prod_name, prod_price FROM products 12 | WHERE vend_id = 1002 or vend_id = 1003; 13 | 14 | -- 计算次序 15 | -- AND的优先级高于OR 16 | SELECT prod_name, prod_price FROM products 17 | WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price >= 10; 18 | 19 | -- IN操作符 20 | -- 用来指定条件范围,取合法值的由逗号分隔的清单全部在圆括号中。 21 | -- IN比OR执行更快,最大的优点是可以包含其他SELECT语句,能够更动态地建立WHERE子句 22 | SELECT prod_name, prod_price FROM products WHERE vend_id IN (1002, 1003) 23 | ORDER BY prod_name; 24 | 25 | -- NOT操作符 26 | -- 列出除1002,1003之外所有供应商供应的产品 27 | SELECT prod_name, prod_price FROM products WHERE vend_id NOT IN (1002, 1003) 28 | ORDER BY prod_name; 29 | ``` 30 | 31 | ## 用通配符进行过滤 32 | ### LIKE操作符 33 | LIKE指示MYSQL,后跟的搜索模式利用通配符匹配而不是直接相等匹配进行比较。 34 | 35 | ``` 36 | -- 百分号(%)通配符 37 | -- 表示任何字符出现任意次数 38 | -- 例:找出所有jet起头的产品 39 | SELECT prod_id, prod_name FROM products WHERE prod_name LIKE 'jet%'; 40 | -- 例:使用多个通配符,匹配任何位置包含anvil的值,不论它之前或之后出现什么字符 41 | SELECT prod_id, prod_name FROM products WHERE prod_name LIKE '%anvil%'; 42 | -- 例:找出s起头e结尾的所有产品 43 | SELECT prod_name FROM products WHERE prod_name LIKE 's%e'; 44 | ``` 45 | %可以匹配0个字符,%代表搜索模式中给定位置的0个、1个或多个字符 46 | 尾空格可能会干扰通配符,例如,在保存词anvil时,如果它后面有一个或多个空格,则子句`WHERE prod_name LIKE '%anvil'`将不会匹配它们,因为在最后的l后有多余的字符。解决这个问题的一个简单的办法是在搜索模 式最后附加一个%。一个更好的办法是使用函数去掉首尾空格。 47 | ``` 48 | -- 下划线(_)通配符 49 | -- 只匹配单个字符而不是多个字符 50 | SELECT prod_id, prod_name FROM products WHERE prod_name LIKE '_ ton anvil'; 51 | ``` 52 | ### 使用技巧 53 | - 不要过度使用通配符,如果其他操作符能够达到目的应该使用其他操作符 54 | - 在确实需要使用通配符时,除非绝对有必要,否则不要把它们用在搜索的开始处。 55 | 把通配符置于搜索模式的开始处搜索起来是最慢的。 56 | - 仔细注意通配符的位置 57 | 58 | ## 用正则表达式进行搜索 59 | ### 使用MySQL正则表达式 60 | #### 基本字符匹配 61 | ``` 62 | -- 例:检索prod_name包含文本1000的所有行 63 | -- REGEXP后所跟的东西作为正则表达式处理 64 | SELECT prod_name FROM products WHERE prod_name REGEXP '1000' 65 | ORDER BY prod_name; 66 | 67 | -- `.`表示匹配任意一个字符 68 | SELECT prod_name FROM products WHERE prod_name REGEXP '.000' 69 | ORDER BY prod_name; 70 | ``` 71 | - LIKE和REGEXP的区别: 72 | LIKE '1000'匹配整个列值,等于'1000'时才会返回相应行,而REGEXP '1000'在列值内进行匹配,如果包含'1000'则会返回相应行。 73 | 74 | ``` 75 | -- 区分大小写 76 | -- 使用关键字BINARY,例如 77 | WHERE prod_name REGEXP BINARY 'JetPack .000'; 78 | ``` 79 | 80 | #### 进行OR匹配 81 | ``` 82 | -- `|`为正则表达式的OR操作符,表示匹配其中之一 83 | SELECT prod_name FROM products WHERE prod_name REGEXP '1000|2000' 84 | ORDER BY prod_name; 85 | -- 可以给出两个以上的OR条件 86 | `1000|2000|3000` 87 | ``` 88 | #### 匹配几个字符之一 89 | ``` 90 | -- `[]`表示匹配[]中的任意一个字符,例如`[123]`是`[1|2|3]`的缩写 91 | SELECT prod_name FROM products WHERE prod_name REGEXP '[123] Ton' 92 | ORDER BY prod_name; 93 | -- output 94 | +-------------+ 95 | | prod_name | 96 | +-------------+ 97 | |1 ton anvil | 98 | |2 ton anvil | 99 | +-------------+ 100 | 101 | -- 和直接使用OR的区别: 102 | SELECT prod_name FROM products WHERE prod_name REGEXP '1|2|3 Ton' 103 | ORDER BY prod_name 104 | -- 匹配的是1 OR 2 OR 3 Ton,应该使用'[1|2|3] Ton' 105 | -- output 106 | +-------------+ 107 | | prod_name | 108 | +-------------+ 109 | |1 ton anvil | 110 | |2 ton anvil | 111 | |JetPack 1000 | 112 | |JetPack 2000 | 113 | |TNT (1 stick)| 114 | +-------------+ 115 | ``` 116 | 字符集合也可以被否定,为否定一个字集,在集合的开始处放置`^`,例如`[^123]`匹配除这些字符的任何东西 117 | 118 | #### 匹配范围 119 | ``` 120 | -- `[0123456789]`可以写成`[0-9]`,其他范围如`[a-z]` 121 | SELECT prod_name FROM products WHERE prod_name REGEXP '[1-5] Ton' 122 | ORDER BY prod_name 123 | -- output 124 | +-------------+ 125 | | prod_name | 126 | +-------------+ 127 | | .5 ton anvil| 128 | | 1 ton anvil | 129 | | 2 ton anvil | 130 | +-------------+ 131 | ``` 132 | #### 匹配特殊字符 133 | ``` 134 | -- 匹配'.'字符,如果使用 135 | SELECT vend_name FROM vendors WHERE vend_name REGEXP '.' 136 | ORDER BY vend_name; 137 | -- output 138 | +---------------+ 139 | | vend_name | 140 | +---------------+ 141 | | ACME | 142 | | Anvils R Us | 143 | | Furball Inc. | 144 | | Jet Set | 145 | | Jouets Et Ours| 146 | | LT Supplies | 147 | +---------------+ 148 | 149 | -- 因为'.'为匹配任意字符,因此匹配特殊字符,必须用'\\'为前导 150 | 151 | SELECT vend_name FROM vendors WHERE vend_name REGEXP '\\.' 152 | ORDER BY vend_name; 153 | -- output 154 | +---------------+ 155 | | vend_name | 156 | +---------------+ 157 | | Furball Inc. | 158 | +---------------+ 159 | ``` 160 | 正则表达式中具有特殊意义的所有字符都要通过这种方式转义 161 | `\\`也用来引用元字符 162 | 163 | |元字符|说明| 164 | |--------|--------| 165 | |`\\f`|换页| 166 | |`\\n`|换行| 167 | |`\\r`|回车| 168 | |`\\t`|制表| 169 | |`\\v`|纵向制表| 170 | 为了匹配`\`本身,需要使用`\\\` 171 | 172 | #### 匹配字符类 173 | | 类 | 说明 | 174 | |----|----| 175 | |[:alnum:]|任意字母和数字(同[a-zA-Z0-9])| 176 | |[:alpha:]|任意字符(同[a-zA-Z])| 177 | |[:cntrl:]|空格和制表(同[\\\t])| 178 | |[:digit:]|ASCII控制字符(ASCII)0到31和127| 179 | |[:graph:]|任意数字(同[0-9])| 180 | |[:lower:]|任意小写字母(同[a-z])| 181 | |[:print:]|任意可打印字符| 182 | |[:punct:]|既不在[:alnum:]又不在[:cntrl:]中的任意字符| 183 | |[:space:]|包括空格在内的任意空白字符(同[\\\f\\\n\\\r\\\t\\\v])| 184 | |[:upper:]|任意大写字母(同[A-Z])| 185 | |[:xdigit:]|任意十六进制数字(同[a-fA-F0-9])| 186 | 187 | #### 匹配多个实例 188 | | 元字符 | 说明 | 189 | |-------|-----| 190 | |*|0个或多个匹配| 191 | |+|1个或多个匹配(等于{1,})| 192 | |?|0个或1个匹配(等于{0,1})| 193 | |{n}|指定数目的匹配| 194 | |{n,}|不少于指定数目的匹配| 195 | |{n.m}|匹配数目的范围(m不超过255)| 196 | 197 | 例: 198 | ``` 199 | SELECT prod_name FROM products WHERE prod_name REGEXP '\\([0-9] sticks?\\)' 200 | ORDER BY prod_name 201 | -- output 202 | +---------------+ 203 | | prod_name | 204 | +---------------+ 205 | | TNT (1 stick) | 206 | | TNT (5 sticks)| 207 | +---------------+ 208 | 209 | -- '\\('匹配'(' 210 | '[0-9]'匹配任意数字 211 | 'stick?'匹配'stick'和'sticks' 212 | '\\)'匹配')' 213 | ``` 214 | 例:匹配连在一起的4位数字 215 | ``` 216 | SELECT prod_name FROM products WHERE prod_name REGEXP '[[:digit:]]{4}' 217 | ORDER BY prod_name; 218 | -- output 219 | +---------------+ 220 | | prod_name | 221 | +---------------+ 222 | | JetPack 1000 | 223 | | JetPack 2000 | 224 | +---------------+ 225 | -- 也可以写成 '[0-9][0-9][0-9][0-9]' 226 | ``` 227 | #### 定位符 228 | | 元字符 | 说明 | 229 | |-------|-----| 230 | |^|文本的开始| 231 | |$|文本的结尾| 232 | |[:<:]|词的开始| 233 | |[:>:]|词的结尾| 234 | 例:找出以一个数(包括小数点开头)开始的所有产品 235 | ``` 236 | SELECT prod_name FROM products WHERE prod_name REGEXP '^[0-9\\.]' 237 | ORDER BY prod_name; 238 | -- output 239 | +---------------+ 240 | | prod_name | 241 | +---------------+ 242 | | .5 ton anvil | 243 | | 1 ton anvil | 244 | | 2 ton anvil | 245 | +---------------+ 246 | ``` 247 | -------------------------------------------------------------------------------- /3-数据处理、汇总和分组.md: -------------------------------------------------------------------------------- 1 | ## 创建计算字段 2 | ### 计算字段 3 | 应用程序需要的数据需要通过从数据库中检索出转换、计算或格式化过的数据,而不是检索出数据,然后再在客户机应用程序或报告程序中重新格式化。 4 | 5 | 字段:基本上与列的意思相同,经常互换使用,不过数据库一般称为列,而属于字段通常用在计算字段的连接上。 6 | 7 | ### 拼接字段 8 | 拼接:将值联结到一起构成单个值 9 | 10 | 在SELECT语句中,可使用Concat()函数来拼接两个列。Concat()函数需要一个或多个指定的串,各个串之间用逗号分隔。 11 | ``` 12 | SELECT Concat(vend_name, ' (',vend_country,')') FROM vendors 13 | ORDER BY vend_name; 14 | #output 15 | +-----------------------------------------+ 16 | | Concat(vendname,' (',vend_country,')') | 17 | +-----------------------------------------+ 18 | | ACME (USA) | 19 | | Anvils R Us (USA) | 20 | | Furball Inc. (USA) | 21 | | Jet Set (England) | 22 | | Jouets Et Ours (France) | 23 | | LT Supplies (USA) | 24 | +-----------------------------------------+ 25 | ``` 26 | 27 | 使用 RTrim()函数可以删除右侧多余的空格来整理数据,例: 28 | ``` 29 | SELECT Concat(RTrim(vend_name),' (',RTrim(vend_country), ')') 30 | FROM vendors 31 | ORDER BY vend_name; 32 | ``` 33 | 34 | | 函数 | 说明 | 35 | |-----|------| 36 | |Trim()|去掉两边的空格| 37 | |LTrim()|去掉左边的空格| 38 | |RTrim()|去掉右边的空格| 39 | 40 | #### 使用别名 41 | 拼接的结果只是一个值,未命名。可以用AS关键字赋予别名 42 | 43 | 常见的用途包括在实际的表列名包含不符合规定的字符(如空格)时重新命名它,在原来的名字含混或容易误解时扩充它等等。 44 | 别名有时也称为导出列(derived column) 45 | ``` 46 | SELECT Concat(RTrim(vend_name),' (',RTrim(vend_country), ')') AS vend_title 47 | FROM vendors 48 | ORDER BY vend_name; 49 | #output 50 | +----------------------------+ 51 | | vend_name | 52 | +----------------------------+ 53 | | ACME (USA) | 54 | | Anvils R Us (USA) | 55 | | Furball Inc. (USA) | 56 | | Jet Set (England) | 57 | | Jouets Et Ours (France) | 58 | | LT Supplies (USA) | 59 | +----------------------------+ 60 | #指示SQL创建一个包含指定计算的名为vend_title的计算字段 61 | ``` 62 | ### 执行算术计算 63 | 例:汇总物品的价格(单价乘以订购数量) 64 | ``` 65 | SELECT prod_id, 66 | quantity, 67 | item_price, 68 | quantity * item_price AS expanded_price 69 | FROM orderitems 70 | WHERE order_num = 20005; 71 | #output 72 | +---------+----------+------------+----------------+ 73 | | prod_id | quantity | item_price | expanded_price | 74 | +---------+----------+------------+----------------+ 75 | | ANV01 | 10 | 5.99 | 59.90 | 76 | | ANV02 | 3 | 9.99 | 29.97 | 77 | | TNT2 | 5 | 10.00 | 50.00 | 78 | | FB | 1 | 10.00 | 10.00 | 79 | +---------+----------+------------+----------------+ 80 | ``` 81 | 82 | | 操作符 | 说明 | 83 | |------|----| 84 | | + | 加 | 85 | | - | 减 | 86 | | * | 乘 | 87 | | / | 除 | 88 | 89 | SELECT Now() 利用 Now()函数返回当前日期和时间 90 | 91 | ## 使用数据处理函数 92 | 93 | *函数没有SQL的可移植性强* 94 | ### 使用函数 95 | 大多数SQL实现支持以下类型的函数 96 | - 用于处理文本串的文本函数 97 | - 在数值数据上进行算术操作的数值函数 98 | - 处理日期和时间值并从这些值中提取特定成分的日期和时间函数 99 | - 返回DBMS正是用的特殊信息的系统函数 100 | 101 | #### 文本处理函数 102 | 常用的文本处理函数 103 | 104 | | 函数 | 说明 | 105 | |-----|-----| 106 | | Left() | 返回串左边的字符 | 107 | | Length() | 返回串的长度 | 108 | | Locate() | 找出串的一个子串 | 109 | | Lower() | 将串转换为小写 | 110 | | LTrim() | 去掉串左边的空格 | 111 | | Right() | 返回串右边的字符 | 112 | | RTrim() | 去掉串右边的空格 | 113 | | Soundex() | 返回串的SOUNDEX值 | 114 | | SubString() | 返回子串的字符 | 115 | | Upper() | 将串转换为大写 | 116 | 117 | SOUNDEX是一个将任何文本转换为描述其语音表示的字母数字模式的算法,使得能对串进行发音比较而不是字母比较。MySQL提供对SOUNDEX的支持。 118 | 119 | 例:联系人Y.Lie输入错误为Y.Lee,使用SOUNDEX检索,匹配发音类似于Y.Lie的联系名 120 | ``` 121 | SELECT cust_name, cust_contact FROM customers 122 | WHERE Soundex(cust_contact)= Soundex('Y Lie'); 123 | #output 124 | +-------------+--------------+ 125 | | cust_name | cust_contact | 126 | +-------------+--------------+ 127 | | Coyote Inc. | Y Lee | 128 | +-------------+--------------+ 129 | ``` 130 | 131 | #### 日期和时间处理函数 132 | 133 | | 函数 | 说明 | 134 | |-----|-----| 135 | |AddDate()|增加一个日期(天、周等)| 136 | |AddTime()|增加一个时间(时、分等)| 137 | |CurDate()|返回当前日期| 138 | |CurTime()|返回当前时间| 139 | |Date()|返回日期时间的日期部分| 140 | |DateDiff()|计算两个日期之差| 141 | |Date_Add()|高度灵活的日期计算函数| 142 | |Date_Format()|返回一个格式化的日期或时间串| 143 | |Day()|返回一个日期的天数部分| 144 | |DayOfWeek()|对于一个日期,返回对应的星期几| 145 | |Hour()|返回一个时间的小时部分| 146 | |Minute()|返回一个时间的分钟部分| 147 | |Month()|返回一个日期的月份部分| 148 | |Now()|返回当前日期和时间| 149 | |Second()|返回一个时间的秒部分| 150 | |Time()|返回一个日期时间的时间部分| 151 | |Year()|返回一个日期的年份部分| 152 | 153 | ##### MySQL使用的日期格式 154 | 日期必须为格式yyyy-mm-dd 155 | 支持2位数字的年份,MySQL处理00-69为2000-2069,70-99为1970-1999,但使用4为数字年份更可靠。 156 | 例: 157 | ``` 158 | SELECT cust_id, order_num FROM orders 159 | WHERE order_date = '2005-09-01'; 160 | ``` 161 | order_date类型为datetime,样例表中的值全部具有时间值00:00:00,但是如果order_date的值为2005-09-01 11:30:05则上面的WHERE order_date = '2005-09-11'不会检索出这一行,因此必须使用Date()函数。 162 | ``` 163 | SELECT cust_id, order_num FROM orders 164 | WHERE Date(order_date) = '2005-09-01'; 165 | ``` 166 | 167 | 例:检索出2005年9月下的所有订单 168 | ``` 169 | SELECT cust_id, order_num FROM orders 170 | WHERE Date(order_date) BETWEEN '2005-09-01' AND '2005-09-30'; 171 | #BETWEEN把2005-09-01和2005-09-30定义为一个要匹配的日期范围。 172 | #另一种方法 173 | SELECT cust_id, order_num FROM orders 174 | WHERE Year(roder_date) = 2005 AND Month(order_date) = 9; 175 | ``` 176 | 177 | #### 数值处理函数 178 | | 函数 | 说明 | 179 | |---|---| 180 | |Abs()|返回一个数的绝对值| 181 | |Cos()|返回一个角度的余弦| 182 | |Exp()|返回一个数的指数值| 183 | |Mod()|返回除操作的余数| 184 | |Pi() |返回圆周率| 185 | |Rand()|返回一个随机数| 186 | |Sin()|返回一个角度的正弦| 187 | |Sqrt()|返回一个数的平方根| 188 | |Tan()|返回一个角度的正切| 189 | 190 | ## 汇总数据 191 | | 函数 | 说明 | 192 | |-----|-----| 193 | |AVG()|返回某列的平均值| 194 | |COUNT()|返回某列的行数| 195 | |MAX()|返回某列的最大值| 196 | |MIN()|返回某列的最小值| 197 | |SUM()|返回某列值之和| 198 | 199 | #### AVG()函数 200 | 例:返回products表中所有产品的平均价格 201 | ``` 202 | SELECT AVG(prod_price) AS avg_price FROM products; 203 | ``` 204 | 例:返回特定供应商所提供产品的平均价格 205 | ``` 206 | SELECT AVG(prod_price) AS avg_price 207 | FROM products 208 | WHERE vend_id = 1003; 209 | ``` 210 | 211 | #### COUNT()函数 212 | 例:返回customer表中客户的总数 213 | ``` 214 | SELECT COUNT(*) AS num_cust FROM customers; 215 | ``` 216 | 例:只对具有电子邮件地址的客户计数 217 | ``` 218 | SELECT COUNT(cust_email) AS num_cust 219 | FROM customers; 220 | ``` 221 | 222 | #### MAX()函数 223 | 例:返回products表中最贵的物品价格 224 | ``` 225 | SELECT MAX(prod_price) AS max_price 226 | FROM products; 227 | ``` 228 | 229 | 对非数值数据使用MAX() 230 | MySQL允许将它用来返回任意列中的最大值,包括返回文本列中的最大值。在用于文本数据时,如果数据按相应的列排序,则MAX()返回最后一行。MAX()函数忽略列值为NULL的行。 231 | 232 | #### MIN()函数 233 | 例: 234 | ``` 235 | SELECT MIN(prod_price) AS min_price FROM products; 236 | ``` 237 | 238 | #### SUM()函数 239 | 返回指定列值的和(总计) 240 | 例:检索所订购物品的总数 241 | ``` 242 | SELECT SUM(quantity) AS items_ordered 243 | FROM orderitems 244 | WHERE order_num = 20005; 245 | ``` 246 | 247 | 例:合计计算值,合计每项物品item_price*quantity,得出订单总金额 248 | ``` 249 | SELECT SUM(item_price*quantity) AS total_price 250 | FORM orderitems 251 | WHERE order_num = 20005; 252 | ``` 253 | 254 | ### 聚集不同值(适用于5.0.3后的版本) 255 | 上述五个聚集函数都可以如下使用: 256 | - 对所有的行执行计算,指定ALL参数或不给参数(ALL为默认) 257 | - 只包含不同的值,指定DISTINCT参数 258 | 259 | 例: 260 | ``` 261 | SELECT AVG(DISTINCT prod_price) AS avg_price 262 | FROM products 263 | WHERE vend_id = 1003; 264 | ``` 265 | 注意:如果指定列名,则DISTINCT只能用于COUNT()。DISTINCT不能用于COUNT(*),因此不允许使用COUNT(DISTINCT), 否则会产生错误。类似地,DISTINCT必须使用列名,不能用于计算或表达式。 266 | 267 | ### 组合聚集函数 268 | SELECT语句可根据需要包含多个聚集函数 269 | ``` 270 | SELECT COUNT(*) AS num_items; 271 | MIN(prod_price) AS price_min, 272 | MAX(prod_price) AS price_max, 273 | AVG(prod_price) AS price_avg 274 | FROM products; 275 | #output 276 | +-----------+-----------+-----------+-----------+ 277 | | num_items | price_min | price_max | price_avg | 278 | +-----------+-----------+-----------+-----------+ 279 | | 14 | 2.50 | 55.50 | 16.133571 | 280 | +-----------+-----------+-----------+-----------+ 281 | ``` 282 | 283 | ## 分组数据 284 | ### 创建分组 285 | 例:根据vend_id分组,对每个分组分别计算总数 286 | ``` 287 | SELECT vend_id, COUNT(*) AS num_prods 288 | FROM products 289 | GROUP BY vend_id; 290 | #output 291 | +---------+-----------+ 292 | | vend_id | num_prods | 293 | +---------+-----------+ 294 | | 1001 | 3 | 295 | | 1002 | 2 | 296 | | 1003 | 7 | 297 | | 1005 | 2 | 298 | +---------+-----------+ 299 | ``` 300 | 301 | - GROUP BY 子句可以包含任意数目的列,使得能对分组进行嵌套,为数据分组提供更细致的控制 302 | - 如果GROUP BY子句中中嵌套了分组,数据将在最后规定的分组上进行汇总。换句话说,在建立分组时,指定的所有列都一起计算(所以不能从个别的列取回数据)。 303 | - GROUP BY子句中列出的每个列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在SELECT中使用表达式,则必须在GROUP BY子句中指定相同的表达式。不能使用别名。 304 | - 除聚集计算语句外,SELECT语句中的每个列都必须在GROUP BY子句中给出。 305 | - 如果分组列中具有NULL值,则NULL将作为一个分组返回。如果列中有多行NULL值,它们将分为一组。 306 | - GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。 307 | 308 | 使用WITH ROLLUP关键字,可以得到每个分组以及每个分组汇总级别(针对每个分组)的值,如下所示: 309 | ``` 310 | SELECT vend_id, COUNT(*) AS num_prods 311 | FROM products 312 | GROUP BY vend_id WITH ROLLUP; 313 | ``` 314 | 315 | ### 过滤分组 316 | WHERE指定的是行,不是分组,WHERE没有分组的概念 317 | 318 | 使用HAVING过滤分组 319 | ``` 320 | SELECT cust_id, COUNT(*) AS orders 321 | FROM orders 322 | GROUP BY cust_id 323 | HAVING COUNT(*) >= 2; 324 | #output 325 | +---------+--------+ 326 | | cust_id | orders | 327 | +---------+--------+ 328 | | 10001 | 2 | 329 | +---------+--------+ 330 | ``` 331 | WHERE不起作用,因为过滤是基于分组聚集值而不是特定行值的。 332 | 333 | 例:列出具有2个(含)以上、价格为10(含)以上的产品的供应商 334 | ``` 335 | SELECT vend_id, COUNT(*) AS num_prods 336 | FROM products 337 | WHERE prod_price >= 10 338 | GROUP BY vend_id 339 | HAVING COUNT(*) >=2 340 | #output 341 | +---------+-----------+ 342 | | vend_id | num_prods | 343 | +---------+-----------+ 344 | | 1003 | 4 | 345 | | 1005 | 2 | 346 | +---------+-----------+ 347 | ``` 348 | 349 | ### 分组和排序 350 | 351 | 例:检索总计订单价格大于等于50的订单的订单号和总计订单价格 352 | ``` 353 | SELECT order_num, SUM(quantity*item_price) AS ordertotal 354 | FROM orderitems 355 | GROUP BY order_num 356 | HAVING SUM(quantity*item_price) >= 50 357 | ORDER BY ordertital; 358 | ``` 359 | ### SELECT子句顺序 360 | SELECT子句及其顺序 361 | 362 | | 子句 | 说明 |是否必须使用 | 363 | |--------|------|---------| 364 | |SELECT|要返回的列或表达式|是 365 | |WHERE| 从中检索数据的表| 仅在从表选择数据时使用| 366 | |GROUP BY| 分组说明 | 尽在按组计算聚集是使用 367 | |HAVING|组级过滤|否| 368 | |ORDER BY|输出排序顺序|否| 369 | |LIMIT|要检索的行数|否| 370 | 371 | 上述子句使用时必须遵循该顺序 372 | 373 | ## 使用子查询 374 | **要求4.1以上版本** 375 | 376 | 例:列出订购物品TNT2的所有客户 377 | 378 | 1. 检索包含物品TNT2的所有订单的编号 379 | 2. 检索具有前一步骤列出的订单编号的所有客户的ID 380 | 3. 检索前一步骤返回的所有客户ID的客户信息 381 | 382 | ``` 383 | #(1) 384 | SELECT order_num FROM orderitems WHERE prod_id = 'TNT2'; 385 | #output 386 | +-----------+ 387 | | order_num | 388 | +-----------+ 389 | | 20005 | 390 | | 20007 | 391 | +-----------+ 392 | #(2) 393 | SELECT cust_id FROM orders WHERE order_num IN (20005,20007); 394 | +-----------+ 395 | | cust_id | 396 | +-----------+ 397 | | 10001 | 398 | | 10004 | 399 | +-----------+ 400 | 401 | #(1)+(2) 402 | SELECT cust_id 403 | FROM orders 404 | WHERE order_num IN (SELECT order_num FROM orderitems WHERE prod_id = 'TNT2'); 405 | 406 | +-----------+ 407 | | order_num | 408 | +-----------+ 409 | | 20005 | 410 | | 20007 | 411 | +-----------+ 412 | 413 | #(3) 414 | SELECT clust_name. cust_contact FROM customers WHERE cust_id IN (10001, 10004) 415 | 416 | #(1)+(2)+(3) 417 | SELECT cust_name, cust_contact FROM customers 418 | WHERE cust_id IN(SELECT cust_id FROM orders 419 | WHERE order_name IN(SELECT order_num FROM orderitems 420 | WHERE prod_id ='TNT2')); 421 | #output 422 | +----------------+--------------+ 423 | | cust_name | cust_contact | 424 | +----------------+--------------+ 425 | | Coyote Inc. | Y Lee | 426 | | Yosemite Place | Y Sam | 427 | +----------------+--------------+ 428 | ``` 429 | 在WHERE子句中使用子查询应保证SELECT语句有与WHERE子句中相同数目的列。 430 | 431 | ### 作为计算字段使用子查询 432 | 需要显示customers表中每个客户的订单总数,订单与相应的客户ID存储在orders表中 433 | 1. 从customers表中检索客户列表 434 | 2. 对于检索出的每个客户,统计其在orders表中的订单数目 435 | 436 | ``` 437 | # 对客户10001的订单进行计数 438 | SELECT COUNT (*) AS orders FROM orders WHERE cust_id = 10001; 439 | # 为了对每个客户执行COUNT(*)计算,应该将COUNT(*)作为一个子查询 440 | SELECT cust_name, cust_state, (SELECT COUNT(*) FROM orders 441 | WHERE orders.cust_id = customers.cust_id) AS orders 442 | FROM customers ORDER BY cust_name; 443 | ``` 444 | **相关子查询**:涉及外部查询的子查询 445 | 在任何时候只要列明可能有多义性,就必须使用这种语法(表明和列名由一个句点分隔) 446 | -------------------------------------------------------------------------------- /4-联结.md: -------------------------------------------------------------------------------- 1 | ## 联结表 2 | ### 连结 3 | #### 关系表 4 | 例如:两个表,一个存储供应商信息,另一个存储产品信息。vendors表包含所有供应商信息,每个供应商占一行,每个供应商应具有唯一的标识,称为主键(primary key)。products表只存储产品信息,除了存储供应商ID之外不存储其他的供应商信息。vendors表的主键又叫products的外键,它将vendors表与products表关联,利用供应商ID能从vendors表中找出相应供应商的详细信息。 5 | 6 | 外键:外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。 7 | 8 | 例:定义外键 9 | ``` 10 | ALTER TABLE orderitems 11 | ADD CONSTRAINT fk_irderitems_orders 12 | FOREIGN KEY (order_num) REFERENCES orders(order_num); 13 | 14 | ALTER TABLE orderitems 15 | ADD CONSTRAINT fk_irderitems_products 16 | FOREIGN KEY (prod_id) REFERENCES products(prod_id); 17 | 18 | ALTER TABLE orders 19 | ADD CONSTRAINT fk_irderitems_customers 20 | FOREIGN KEY (cust_id) REFERENCES customers(cust_id); 21 | 22 | ALTER TABLE products 23 | ADD CONSTRAINT fk_irderitems_vendors 24 | FOREIGN KEY (vend_id) REFERENCES vendors(vend_id); 25 | ``` 26 | 27 | 在使用关系表时,仅在关系列中插入合法的数据非常重要,如果在products表中插入拥有非法供应商ID(即没有在vendors表中出现)的供应商生产的产品,则这些产品是不可访问的,因为它们没有关联到某个供应商。 28 | 29 | ### 创建连结 30 | ``` 31 | SELECT vend_name, prod_name, prod_price FROM vendors, products 32 | WHERE vendors.vend_id = products.vend_id 33 | ORDER BY vend_name, prod_name; 34 | ``` 35 | 两个表用WHERE子句联结 36 | 37 | 笛卡儿积:由没有连结条件的表关系返回的结果为笛卡尔积,检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。 38 | 39 | 40 | #### 内部连结 41 | 等值联结:基于两个表之间的相等测试,也叫内部连结 42 | 43 | 可以使用另一种语法来明确指定联结的类型 44 | ``` 45 | SELECT vend_name, prod_name, prod_price 46 | FROM vendors INNER JOIN products 47 | ON vendors.vend_id = products.vend_id; 48 | ``` 49 | FROM 子句的组成部分,以INNER JOIN指定,联结条件用ON子句 50 | 51 | #### 联结多个表 52 | ``` 53 | SELECT prod_name, vend_name, prod_price, quantity 54 | FROM orderitems, products, vendors 55 | WHERE products.vend_id = vendors.vend_id 56 | AND orderitems.prod_id = products.prod_id 57 | AND order_num = 20005; 58 | #显示编号为20005的订单中的物品。订单物品存储在orderitems表中,按每个产品的ID存储。 59 | 它引用products表中的产品。这些产品通过供应商ID联结到vendors表中相应的供应商 60 | ``` 61 | 62 | 例:返回订购产品INT2的客户列表 63 | ``` 64 | SELECT cust_name,cust_contact 65 | FROM customers, orders, orderitems 66 | WHERE customers.cust_id = orders.cust_id 67 | AND orderitems.order_num = orders.order_num 68 | AND prod_id = 'TNT2'; 69 | ``` 70 | 71 | ## 创建高级联结 72 | ### 使用表别名 73 | ``` 74 | SELECT Concat(RTrim(vend_name),'('Rtrim(vend_country),')') AS vend_title 75 | FROM vendors ORDER BY vend_name; 76 | ``` 77 | 别名除了用于列名和计算字段外,SQL还允许给表起别名,这样做有两个主要理由: 78 | 79 | - 缩短SQL语句 80 | - 允许在单条SELECT语句中多次使用相同的表。 81 | 82 | 例: 83 | ``` 84 | SELECT cust_name, cust_contact 85 | FROM customers AS c, orders AS o, orderitems AS oi 86 | WHERE c.cust_id = o.cust_id 87 | AND oi.order_num = o.order_num 88 | AND prod_id = 'TNT2'; 89 | ``` 90 | 91 | ### 使用不同类型的联结 92 | #### 自联结 93 | 例:如果某物品(ID为DTNTR)存在问题,因此想知道生产该物品的供应商生产的其他物品是否也存在这些问题。此查询要求首先找到ID为DTNTR的物品的供应商,然后找出这个供应商生产的其他物品。 94 | ``` 95 | #子查询 96 | SELECT prod_id, prod_name 97 | FROM products 98 | WHERE vend_id = (SELECT vend_id FROM products WHERE prod_id = 'DTNTR'); 99 | #output 100 | +---------+----------------+ 101 | | prod_id | prod_name | 102 | +---------+----------------+ 103 | | DTNTR | Detonator | 104 | | FB | Bird seed | 105 | | FC | Carrots | 106 | | SAFE | Safe | 107 | | SLING | Sling | 108 | | TNT1 | TNT (1 stick) | 109 | | TNT2 | TNT (5 sticks) | 110 | +---------+----------------+ 111 | #使用自联结 112 | SELECT p1.prod_id, p1.prod_name 113 | FROM products AS p1, products AS p2 114 | WHERE p1.vend_id = p2.vend_id 115 | AND p2.prod_id = 'DTNTR'; 116 | ``` 117 | #### 自然联结 118 | 119 | 无论何时对表进行联结,应该至少有一个列出现在不止一个表中(被联结的列)。标准的联结(内部联结)返回所有数据,甚至相同的列多次出现,自然联结排除多次出现,使每个列只返回一次。 120 | 121 | 自然联结是这样一种联结,其中你只能选择那些唯一的列,这一版是通过使用通配符,对所有其他表的列使用明确的字集来完成的。 122 | ``` 123 | SELECT c.*, o.order_num, o.order_date, oi.prod_id, oi.quantity, oi.item_price 124 | FROM customers AS c, orders AS o, orderitems AS oi 125 | WHERE c.cust_id = o.cust_id 126 | AND oi.order_num = o.order_num 127 | AND prod_id = 'FB'; 128 | ``` 129 | 这个例子中,通配符只对第一个表使用所有其他列明确列出,所以没有重复的列被检索出来。 130 | 131 | #### 外部联结 132 | 有时候需要包含没有关联的那些行 133 | 134 | 例:需要联结来完成以下工作 135 | 136 | - 对每个客户下了多少订单进行计数,包括那些至今尚未下订单的客户 137 | - 列出所有产品及订购数量,包括没人订购的产品 138 | - 计算平均销售规模,包括那些至今尚未下订单的客户 139 | 140 | ``` 141 | #内部联结 142 | SELECT customers.cust_id, orders.order_num 143 | FROM customers INNER JOIN orders 144 | ON customers.cust_id = orders.cust_id; 145 | 146 | #外部联结 147 | SELECT customers.cust_id, orders.order_num 148 | FROM customers LEFT OUTER JOIN orders 149 | ON customers.cust_id = orders.cust_id; 150 | ``` 151 | 外部联结语法类似。可以检索所有客户,包括没有订单的客户。 152 | 153 | 在使用OUTER JOIN语法时,必须使用RIGHT或LEFT关键字指定包括其所有行的表。(RIGHT指出的是OUTER JOIN右边的表,而LEFT指出的是OUTER JOIN左边的表。)上面的例子中从customers中选择所有的行。 154 | 155 | ``` 156 | SELECT customers.cust_id, orders.order_num 157 | FROM customers RIGHT OUTER JOIN orders 158 | ON orders.cust_id = customers.cust_id; 159 | ``` 160 | 161 | ### 使用带聚集函数的联结 162 | 例:检索所有客户及每个客户所下的订单数 163 | ``` 164 | SELECT customers.cust_name, customers.cust_id, COUNT(orders.order_num) AS num_ord 165 | FROM customers INNER JOIN orders 166 | ON customers.cust_id = orders.cust_id 167 | GROUP BY customers.cust_id; 168 | ``` 169 | 此SELECT语句使用INNER JOIN将customers和orders表互相关联。GROUP BY子句按客户分组数据,因此,函数调用COUNT(orders.order_num)对每个客户的订单计数,将它作为num_ord返回。 170 | 171 | 聚集函数也可以方便地与其他联结一起使用。例: 172 | ``` 173 | SELECT customers.cust_name, customers.cust_id, COUNT(orders.order_num) AS num_ord 174 | FROM customers LEFT OUTER JOIN orders 175 | ON customers.cust_id = orders.cust_id 176 | GROUP BY customers.cust_id; 177 | ``` 178 | 这个例子使用左外部联结来包含所有客户,甚至包含那些没有任何下订单的客户。 179 | 180 | ### 使用联结和联结条件 181 | 182 | - 注意所使用的联结类型,一般我们使用内部联结,但使用外部联结也是有效的。 183 | - 保证使用正确的联结条件,否则将返回不正确的数据。 184 | - 应该总是提供联结条件,否则会得出笛卡尔积。 185 | - 在一个联结中可以包含多个表,甚至对于每个联结可以采用不同的联结类型。应该在一起测试他们前分别测试每个联结。 186 | -------------------------------------------------------------------------------- /5-组合查询、全文本搜索.md: -------------------------------------------------------------------------------- 1 | ## 组合查询 2 | ### 组合查询 3 | MySQL允许执行多个查询并将结果作为单个查询结果返回。 4 | 两种情况: 5 | - 在单个查询中从不同的表返回类似结构的数据 6 | - 对单个表执行多个查询,按单个查询返回数据 7 | 8 | ### 创建组合查询 9 | #### 使用UNION 10 | 11 | 给出每条SELECT语句,在各条语句之间放上关键字UNION 12 | 例:需要价格小于等于5的所有物品的一个列表,并且包含供应商1001和1002生产的所有物品 13 | ``` 14 | #单条语句 15 | SELECT vend_id, prod_id, prod_price 16 | FROM products 17 | WHERE prod_price <= 5; 18 | 19 | SELECT vend_id, prod_id, prod_price 20 | FROM products 21 | WHERE vend_id IN (1001,1002); 22 | 23 | #组合上述语句 24 | SELECT vend_id, prod_id, prod_price 25 | FROM products 26 | WHERE prod_price <= 5 27 | UNION 28 | SELECT vend_id, prod_id, prod_price 29 | FROM products 30 | WHERE vend_id IN (1001, 1002); 31 | 32 | #等于 33 | SELECT vend_id, prod_id, prod_price 34 | FROM products 35 | WHERE prod_price <= 5 36 | OR vend_id IN (1001, 1002); 37 | ``` 38 | 39 | #### UNION规则 40 | - UNION 必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔 41 | - UNION中的每个查询必须包含先沟通的列、表达式或聚集函数 42 | - 列数据类型必须兼容:类型不必完全向东,但必须是DBMS可以隐含地转换的类型 43 | 44 | #### 包含或取消重复的行 45 | UNION从查询结果集中自动去除了重复的行,如果需要返回所有行,可以使用UNION ALL 46 | #### 对组合查询结果排序 47 | 使用ORDER BY子句排序,只能使用一条ORDER BY子句,必须在最后一条SELECT语句之后。 48 | 49 | ## 全文本搜索 50 | **并非所有引擎都支持全文本搜索** 51 | MyISAM支持,InnoDB不支持 52 | 53 | LIKE、正则表达式的限制 54 | - 性能:由于被搜索行不断增加,这些搜索可能非常耗时 55 | - 明确控制:很难明确控制匹配什么和不匹配什么 56 | - 智能化的结果:不能提供一种智能化的选择结果的方法,例如:一个特殊词的搜索将会返回包含该词的所有行而不区分包含单个匹配的行和多个匹配的行。 57 | 58 | ### 使用全文本搜索 59 | 为了进行全文本搜索,必须索引被搜索的列,而且要随着数据的改变不断地重新索引。在对表列进行适当设计后,MySQL会自动进行所有的索引和重新索引。 60 | 在索引之后,SELECT可与Match()和Against()一起使用以实际执行搜索。 61 | 62 | #### 启用全文本搜索支持 63 | 一般在创建表时启用全文本搜索 64 | CREATE TABLE语句接收FULLTEXT子句,它给出被索引列的一个逗号分隔的列表 65 | 例: 66 | ``` 67 | CREATE TABLE productnotes 68 | ( 69 | note_id int NOT NULL AUTO_INCREMENT, 70 | pord_id char(10) NOT NULL, 71 | note_date datetime NOT NULL. 72 | note_text text NULL, 73 | PRIMARY KEY(note_id), 74 | FULLTEXT(note_text) 75 | )ENGINE=MyISAM; 76 | ``` 77 | 可以在创建表时指定FULLTEXT或在稍后指定(所有数据必须立即索引) 78 | **不要在导入数据时使用FULLTEXT** 应线导入所有数据再修改表,定义FULLTEXT 79 | 80 | #### 进行全文本搜索 81 | Match() 指定被搜索的列 82 | Against() 指定要使用的搜索表达式 83 | ``` 84 | SELECT note_text 85 | FROM productnotes 86 | WHERE Match(note_text) Against('rabbit'); 87 | ``` 88 | 传递个Match()的值必须与FULLTEXT()定义中的相同。 89 | 90 | 除非使用BINARY方式,否则全文本搜索不区分大小写 91 | 92 | 全文本搜索对结果排序,具有较高等级的行先返回 93 | ``` 94 | SELECT note_text, 95 | Match(note_text) Against('rabbit') AS rank 96 | FROM productnotes; 97 | ``` 98 | 显示所有行及他们的等级 99 | 100 | #### 使用扩展查询 101 | (MySQL 4.1.1及更高版本) 102 | 例如找出所有提到anvils的注释,和与搜索有关的其他行,即使它们不包含这个词 103 | ``` 104 | #不使用扩展查询 105 | SELECT note_text 106 | FROM productnotes 107 | WHERE Match(note_text) Against('anvils'); 108 | +------------------------------------------------------------------------------+ 109 | | note_text | 110 | +------------------------------------------------------------------------------+ 111 | | Multiple custoer returns, anvils failing to drop fast enough or falling | 112 | | backwords on purchaser, Recomend that customer considers using havier | 113 | | anvils. | 114 | +------------------------------------------------------------------------------+ 115 | #使用扩展查询 116 | SELECT note_text 117 | FROM productnotes 118 | WHERE Match(note_text) Against('anvils' WITH QUERY EXPANSION); 119 | +------------------------------------------------------------------------------+ 120 | | note_text | 121 | +------------------------------------------------------------------------------+ 122 | | Multiple custoer returns, anvils failing to drop fast enough or falling | 123 | | backwords on purchaser, Recomend that customer considers using havier | 124 | | anvils. | 125 | | Customer complaint: Sticks not individually wrapped, too easy to mistakenly | 126 | | detonate all at once. Recommend individual wrapping. | 127 | | Customer compliant: Not heavy enouth to generate flying stars around heardof | 128 | | victim. If veing purchased for dropping, recommend ANV02 or ANV03 instead. | 129 | | Please note that no returns will be accepted if safe opened using explosives.| 130 | | Customer complaint: rabbit has been able to detect trap, food apparently | 131 | | less effective now. | 132 | | Customer complaint: Circular hole in safe floor can apparently be easily cut | 133 | | with handsaw. | 134 | | Matches not include, recomend purchase of matches or detonator (item DTNTR) | 135 | +------------------------------------------------------------------------------+ 136 | ``` 137 | 返回7行,第一行包含anvils,因此等级最高,第二行与anvils无关,但包含第一行中的两个次,因此被检索出来。 138 | 139 | #### 布尔文本搜索 140 | 可以提供如下内容的细节: 141 | - 要匹配的词 142 | - 要排斥的次 143 | - 排列提示 144 | - 表达式分组 145 | - 另外一些内容 146 | 147 | ``` 148 | SELECT note_text 149 | FROM productnotes 150 | WHERE Match(note_text) Against('heavy' IN BOOLEAN MODE); 151 | ``` 152 | 例:匹配包含heavy但不包含任意以rope开始的词的行 153 | ``` 154 | SELECT note_text 155 | FROM productnotes 156 | WHERE Match(note_text) Against('heavy -rope*' IN BOOLEAN MODE); 157 | ``` 158 | **MySQL4.x版本中使用-ropes而不是-rope* ** 159 | 160 | 全文本布尔操作符 161 | 162 | | 布尔操作符 | 说明 | 163 | |----|----| 164 | |+|包含,词必须存在| 165 | |-|排除,词必须不出现| 166 | |>|包含,而且增加等级值| 167 | |<|包含,且减少等级| 168 | |()|把词组成子表达式(允许这些子表达式作为一个组被包含、排除、排列等)| 169 | |~|取消一个词的排序值| 170 | |*|取消一个词的排序值| 171 | |""|定义一个短语(与单个词的列表不一样,它匹配整个短语以便包含或排除这个短语)| 172 | 173 | 例: 174 | ``` 175 | SELECT note_text 176 | FROM productnotes 177 | WHERE Match(note_text) Against('+rabbit +bait' IN BOOLEAN MODE); 178 | #匹配包含词rabbit和bait的行。 179 | 180 | SELECT note_text 181 | FROM productnotes 182 | WHERE Match(note_text) Against('rabbit bait' IN BOOLEAN MODE); 183 | #匹配包含rabbit和bait中的至少一个词的行 184 | 185 | SELECT note_text 186 | FROM productnotes 187 | WHERE Match(note_text) Against('"rabbit bait"' IN BOOLEAN MODE); 188 | #匹配rabbit bait而不是匹配两个词 189 | 190 | SELECT note_text 191 | FROM productnotes 192 | WHERE Match(note_text) Against('>rabbit