mysql exgist_mysql笔记 - SELECT 语句

mysql笔记 - SELECT 语句

SELECT

[ALL | DISTINCT | DISTINCTROW ] *

[HIGH_PRIORITY]

[STRAIGHT_JOIN]

[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]

[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]

select_expr [, select_expr ...]

[FROM table_references

[WHERE where_condition]

[GROUP BY {col_name | expr | position}

[ASC | DESC], ... [WITH ROLLUP]]

[HAVING where_condition]

[ORDER BY {col_name | expr | position}

[ASC | DESC], ...]

[LIMIT {[offset,] row_count | row_count OFFSET offset}]

[PROCEDURE procedure_name(argument_list)]

[INTO OUTFILE 'file_name'

[CHARACTER SET charset_name]

export_options

| INTO DUMPFILE 'file_name'

| INTO var_name [, var_name]]

[FOR UPDATE | LOCK IN SHARE MODE]]

select * from employees; -- 取出所有数据

desc employees; -- 查看表信息

通常会要求开发人员不要使用select * 即使是select所有字段推荐写上所有的列名,虽然很麻烦但是规范上推荐这样做

一个重要的原因是alter table会对表结构进行修改的很多操作,alter table后select * from取出的字段可能比一开始涉及的要多,可能会遇到各种各样的问题。另外select alter table可以加列,可以加在某个字段中间的位置,那么程序那边取得的数据并不是一开始想要的数据,容易出错。所以建议select的时候建议把所有的列都带上

LIMIT 限制取出来的数据量

不加LIMIT限制会一次取出所有的数据,数据量大的时候可能是灾难性的事情,通常都会加一个LIMIT,图形化工具一般默认会加上一个LIMIT

select * from employees limit 0,3; -- 从第0条开始取出3条数据

select * from employees limit 1,3; -- 从第1条开始取出3条数据

LIMIT常用于分页,但是LIMIT offset数字越大性能越差,为什么呢?

select * from employees limit 30; -- 取30条,扫30条,查询快(0.00 sec)

select * from employees limit 1000000,30; -- 同样取30条,扫1000030行记录,查询变很慢(14.55 sec)

好的做法

select emp_no,birth_date,first_name,last_name,gender from employees limit 10;

+--------+------------+------------+-----------+--------+

| emp_no | birth_date | first_name | last_name | gender |

+--------+------------+------------+-----------+--------+

| 10001 | 1953-09-02 | Georgi | Facello | M |

| 10002 | 1964-06-02 | Bezalel | Simmel | F |

| 10003 | 1959-12-03 | Parto | Bamford | M |

| 10004 | 1954-05-01 | Chirstian | Koblick | M |

| 10005 | 1955-01-21 | Kyoichi | Maliniak | M |

| 10006 | 1953-04-20 | Anneke | Preusig | F |

| 10007 | 1957-05-23 | Tzvetan | Zielinski | F |

| 10008 | 1958-02-19 | Saniya | Kalloufi | M |

| 10009 | 1952-04-19 | Sumant | Peac | F |

| 10010 | 1963-06-01 | Duangkaew | Piveteau | F |

+--------+------------+------------+-----------+--------+

select emp_no,birth_date,first_name,last_name,gender from employees where emp_no > 10010 limit 10;

+--------+------------+------------+-------------+--------+

| emp_no | birth_date | first_name | last_name | gender |

+--------+------------+------------+-------------+--------+

| 10011 | 1953-11-07 | Mary | Sluis | F |

| 10012 | 1960-10-04 | Patricio | Bridgland | M |

| 10013 | 1963-06-07 | Eberhardt | Terkki | M |

| 10014 | 1956-02-12 | Berni | Genin | M |

| 10015 | 1959-08-19 | Guoxiang | Nooteboom | M |

| 10016 | 1961-05-02 | Kazuhito | Cappelletti | M |

| 10017 | 1958-07-06 | Cristinel | Bouloucos | F |

| 10018 | 1954-06-19 | Kazuhide | Peha | F |

| 10019 | 1953-01-23 | Lillian | Haddadi | M |

| 10020 | 1952-12-24 | Mayuko | Warwick | M |

+--------+------------+------------+-------------+--------+

从之前取得数据的最大值开始取数据,这样的一个好处是不管你的值多大,因为是通过大于等于来定位的,所以只是取了10条数据不会再去扫这么多的数据

下面的语句表示从mysql中随机取出3条数据,select语句不带order by表示随机取3条数据

select emp_no,first_name,last_name from employees limit 3;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 10001 | Georgi | Facello |

| 10002 | Bezalel | Simmel |

| 10003 | Parto | Bamford |

+--------+------------+-----------+

数据量小的时候不容易看出来,数据量大的时候容易发现这个问题,或者alter table一下后再试

alter table employees add index idx_name(first_name,last_name);

select emp_no,first_name,last_name from employees limit 3;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 69256 | Aamer | Anger |

| 486584 | Aamer | Armand |

| 237165 | Aamer | Azevdeo |

+--------+------------+-----------+

order by 表示根据哪个字段进行排序,可以按照物理数据顺序取出数据

select emp_no,first_name,last_name from employees order by emp_no limit 3;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 10001 | Georgi | Facello |

| 10002 | Bezalel | Simmel |

| 10003 | Parto | Bamford |

+--------+------------+-----------+

查询分区表

select * from titles partition (p19) limit 1;

+--------+--------------+------------+------------+

| emp_no | title | from_date | to_date |

+--------+--------------+------------+------------+

| 10052 | Senior Staff | 2002-01-31 | 9999-01-01 |

+--------+--------------+------------+------------+

ORDER BY 对取出来的数据做排序

order by根据某个字段进行分页

select emp_no,first_name,last_name from employees order by last_name limit 10;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 11761 | Bartek | Aamodt |

| 15427 | Aluzio | Aamodt |

| 18182 | Dekang | Aamodt |

| 16572 | Matt | Aamodt |

| 12791 | Mokhtar | Aamodt |

| 12516 | Sreenivas | Aamodt |

| 12982 | Sachem | Aamodt |

| 17400 | Basim | Aamodt |

| 19898 | Vidar | Aamodt |

| 17885 | Takanari | Aamodt |

+--------+------------+-----------+

WHERE 过滤数据

select emp_no,first_name,last_name from employees where emp_no = 10001;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 10001 | Georgi | Facello |

+--------+------------+-----------+

select emp_no,first_name,last_name from employees where emp_no >= 10001 limit 10;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 10001 | Georgi | Facello |

| 10002 | Bezalel | Simmel |

| 10003 | Parto | Bamford |

| 10004 | Chirstian | Koblick |

| 10005 | Kyoichi | Maliniak |

| 10006 | Anneke | Preusig |

| 10007 | Tzvetan | Zielinski |

| 10008 | Saniya | Kalloufi |

| 10009 | Sumant | Peac |

| 10010 | Duangkaew | Piveteau |

+--------+------------+-----------+

select emp_no,first_name,last_name from employees where emp_no >= 20001 and first_name like 'Am%' limit 10;

+--------+------------+---------------+

| emp_no | first_name | last_name |

+--------+------------+---------------+

| 100860 | Amabile | Aamodt |

| 285669 | Amabile | Akiyama |

| 276002 | Amabile | Albarhamtoshy |

| 454340 | Amabile | Alencar |

| 86625 | Amabile | Anger |

| 416143 | Amabile | Antonisse |

| 491486 | Amabile | Antonisse |

| 451988 | Amabile | Apsitis |

| 409363 | Amabile | Atchley |

| 208844 | Amabile | Baar |

+--------+------------+---------------+

select emp_no,first_name,last_name from employees where emp_no >= 20001 and first_name like '%Am%' limit 10;

+--------+------------+--------------+

| emp_no | first_name | last_name |

+--------+------------+--------------+

| 20044 | Kiam | Gist |

| 20062 | Uinam | Heuser |

| 20114 | Ramalingam | Zyda |

| 20118 | Mohammed | Schneeberger |

| 20142 | Arumugam | Emmart |

| 20159 | Isamu | Valiente |

| 20167 | Stamatina | Kobara |

| 20217 | Tamiya | Ruemmler |

| 20265 | Amalendu | Willoner |

| 20285 | Pramod | Escriba |

+--------+------------+--------------+

select emp_no,first_name,last_name from employees where emp_no >= 20001 or first_name like '%Am%' limit 10;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 69256 | Aamer | Anger |

| 486584 | Aamer | Armand |

| 237165 | Aamer | Azevdeo |

| 413688 | Aamer | Azuma |

| 281363 | Aamer | Baak |

| 242368 | Aamer | Baaleh |

| 206549 | Aamer | Baar |

| 259089 | Aamer | Baba |

| 60922 | Aamer | Bahl |

| 283280 | Aamer | Bahl |

+--------+------------+-----------+

where 1 = 1表示没有任何条件全部成立,因为不知道第一个where应该怎么写,如果提前把where 1 = 1写好,这样程序那边拼接后面的条件就比较方便

select emp_no,first_name,last_name from employees where 1 = 1 limit 10;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 69256 | Aamer | Anger |

| 486584 | Aamer | Armand |

| 237165 | Aamer | Azevdeo |

| 413688 | Aamer | Azuma |

| 281363 | Aamer | Baak |

| 242368 | Aamer | Baaleh |

| 206549 | Aamer | Baar |

| 259089 | Aamer | Baba |

| 60922 | Aamer | Bahl |

| 283280 | Aamer | Bahl |

+--------+------------+-----------+

比如

select emp_no,first_name,last_name from employees where 1 = 1 and emp_no = 20000;

+--------+------------+-----------+

| emp_no | first_name | last_name |

+--------+------------+-----------+

| 20000 | Jenwei | Matzke |

+--------+------------+-----------+

GROUP BY

3d40edca4cb6812034b326b9c2a7b12a.png

分组就是根据某个字段当中相同的值来进行分组,并且分组完之后是要加一个聚集(统计)函数

use test;

create table t (a int auto_increment primary key, b int, c int);

insert into t select NULL,1,1;

insert into t select NULL,1,7;

insert into t select NULL,1,8;

insert into t select NULL,2,8;

insert into t select NULL,2,10;

insert into t select NULL,3,8;

select * from t;

+---+------+------+

| a | b | c |

+---+------+------+

| 1 | 1 | 1 |

| 2 | 1 | 7 |

| 3 | 1 | 8 |

| 4 | 2 | 8 |

| 5 | 2 | 10 |

| 6 | 3 | 8 |

+---+------+------+

select b,sum(b),avg(b) from t group by b;

+------+--------+--------+

| b | sum(b) | avg(b) |

+------+--------+--------+

| 1 | 3 | 1.0000 |

| 2 | 4 | 2.0000 |

| 3 | 3 | 3.0000 |

+------+--------+--------+

HAVING

HAVING表示对group by中的聚合函数进行过滤

select b,sum(b),avg(b) from t where avb(b)>2 group by b; -- 这样是会报错的

ERROR 1305 (42000): FUNCTION test.avb does not exist

分组中需要对分组的条件进行过滤需要使用HAVING

select b,sum(b),avg(b) from t group by b having avg(b) > 2;

+------+--------+--------+

| b | sum(b) | avg(b) |

+------+--------+--------+

| 3 | 3 | 3.0000 |

+------+--------+--------+

不是说使用了group by就代表不能使用where了,这样也是可以使用where进行过滤的,只不过where过滤的是非聚合的结果(对查询的记录进行过滤),而having是用来过滤聚合的结果

select b,sum(b),avg(b) from t where b < 3 group by b;

+------+--------+--------+

| b | sum(b) | avg(b) |

+------+--------+--------+

| 1 | 3 | 1.0000 |

| 2 | 4 | 2.0000 |

+------+--------+--------+

双重过滤

select b,sum(b),avg(b) from t where b < 3 group by b having avg(b) < 2;

+------+--------+--------+

| b | sum(b) | avg(b) |

+------+--------+--------+

| 1 | 3 | 1.0000 |

+------+--------+--------+

所以where是一开始选数据的时候就开始过滤了,having是在聚集结果出来之后才开始过滤

9a180ba9a4678b9338fe659f385403ce.png

having也可以过滤指定的一列而不是聚集函数,但是这样写没有什么意义,使用where可以获得更好的性能,所以having一般都跟聚集函数搭配使用

select b,sum(b),avg(b) from t where b < 3 group by b having b < 2;

+------+--------+--------+

| b | sum(b) | avg(b) |

+------+--------+--------+

| 1 | 3 | 1.0000 |

+------+--------+--------+

select字段中没有选择的字段,having中使用会报错

select b,sum(b),avg(b) from t where b < 3 group by b having a < 2;

ERROR 1054 (42S22): Unknown column 'a' in 'having clause'

这样的语句没有意义,因为取出来的a字段数据不一定是146,mysql会随机取a字段的数据,而且这种写法在orcal中好像不支持

select a,b,sum(b) from t group by b;

+---+------+--------+

| a | b | sum(b) |

+---+------+--------+

| 1 | 1 | 3 |

| 4 | 2 | 4 |

| 6 | 3 | 3 |

+---+------+--------+

JOIN

基本的多表关联查询

SELECT

concat(first_name,' ',last_name), dept_name -- concat连接了2个字段的字符串

FROM

employees e, -- e是别名,下面3个雷同

dept_emp de,

departments d

WHERE

e.emp_no = de.emp_no -- 关联条件

AND de.dept_no = d.dept_no -- 再一次关联条件

LIMIT 3;

+----------------------------------+------------------+

| concat(first_name,' ',last_name) | dept_name |

+----------------------------------+------------------+

| Mary Sluis | Customer Service |

| Huan Lortz | Customer Service |

| Basil Tramer | Customer Service |

+----------------------------------+------------------+

新建一些例子

create table a (a int);

create table b (a int);

insert into a select 1;

insert into a select 2;

insert into a select 3;

insert into b select 1;

insert into b select 2;

select * from a;select * from b;

+------+

| a |

+------+

| 1 |

| 2 |

| 3 |

+------+

3 rows in set (0.00 sec)

+------+

| a |

+------+

| 1 |

| 2 |

+------+

2 rows in set (0.03 sec)

最简单的两表关联,并不是2张表关联就会做笛卡尔积,如果关联的时候没有写where关联条件就会产生笛卡尔积

select * from a,b where a.a = b.a;

+------+------+

| a | a |

+------+------+

| 1 | 1 |

| 2 | 2 |

+------+------+

select * from a,b; -- 这样会产生笛卡尔积

+------+------+

| a | a |

+------+------+

| 1 | 1 |

| 1 | 2 |

| 2 | 1 |

| 2 | 2 |

| 3 | 1 |

| 3 | 2 |

+------+------+

三张表只有1个过滤条件会出现图示效果,会产生笛卡尔积的效果,出现了4条记录

create table c (a int);

insert into c select 2;

insert into c select 3;

select * from a,b,c where a.a = b.a; -- 这样会产生笛卡尔积

+------+------+------+

| a | a | a |

+------+------+------+

| 1 | 1 | 2 |

| 2 | 2 | 2 |

| 1 | 1 | 3 |

| 2 | 2 | 3 |

+------+------+------+

还可以这样写,关联条件可以非等值

select * from a,b where a.a < b.a;

+------+------+

| a | a |

+------+------+

| 1 | 2 |

+------+------+

修改一下字段名先。。。

alter table a change a x int;

alter table b change a y int;

alter table c change a z int;

INNER JOIN

这3个写法没有区别

select * from a inner join b on a.x = b.y;

select * from a join b on a.x = b.y;

select * from a,b where a.x = b.y;

-- 都返回

+------+------+

| x | y |

+------+------+

| 1 | 1 |

| 2 | 2 |

+------+------+

以下语句的关联,通常来说会选择关联过滤度高的条件进行关联,这里b.y > 1的关联度会高一些。

select * from a,b where a.x = b.y and b.y > 1;

+------+------+

| x | y |

+------+------+

| 2 | 2 |

+------+------+

假设a表100w记录b表100w记录,其中的x,y一一对应,优化机通畅都会先从b>100 and b<200条件先进行过滤,这样就只需要100w和100条记录进行join就可以了,如果先从a.x = b.y进行过滤,那就是100w和100w的记录进行join,所以一般优化机是会把b>100 and b<200的过滤条件设置得更高

30cbd55d82c38568269d0736fff4d740.png

INNER JOIN的时候过滤条件即可写在on里面也可以写在where条件里面,对结果来说是没有区别的

select * from a inner join b on (a.x = b.y and b.y > 1);

select * from a inner join b on a.x = b.y where b.y > 1;

都返回

+------+------+

| x | y |

+------+------+

| 2 | 2 |

+------+------+

OUTER JOIN

outer join 分成left/right两种,outer可以关键词可以省略,left/right join代表左/右外连接

左连接中左边的表为保留表,保留表中所有的字段都是要出现的,如果关联条件存在的话就是一对一的情况,右表中关联条件不存在则关联出来的结果就是NULL值

130db1457d91c1318c334a9f86223152.png

select * from a left join b on a.x = b.y;

+------+------+

| x | y |

+------+------+

| 1 | 1 |

| 2 | 2 |

| 3 | NULL |

+------+------+

select * from a right join b on a.x = b.y;

+------+------+

| x | y |

+------+------+

| 1 | 1 |

| 2 | 2 |

+------+------+

a表中存在的但是b表中不存在的数据,左连接实现

select * from a left join b on a.x = b.y where b.y is NULL;

+------+------+

| x | y |

+------+------+

| 3 | NULL |

+------+------+

a表中但是不在b表中,不使用left join 使用not in + 子查询,不过会存在一些性能上的问题

select * from a where a.x not in (select y from b);

+------+

| x |

+------+

| 3 |

+------+

这样的查询b.y会全部返回NULL,因为a.x = b.y and b.y is NULL不成立,所以b.y全部用NULL填充

select * from a left join b on a.x = b.y and b.y is NULL;

+------+------+

| x | y |

+------+------+

| 1 | NULL |

| 2 | NULL |

| 3 | NULL |

+------+------+

过滤条件写在on后还是where后是有讲究的,推荐使用on来进行两张表之间的关联,where用来进行数据的过滤。

外连接中where不能写在on前面,内连接则可以,left join是用到比较多的一个方式

多次left join

select * from a left join b on a.x = b.y left join c on a.x = c.z;

+------+------+------+

| x | y | z |

+------+------+------+

| 2 | 2 | 2 |

| 1 | 1 | NULL |

| 3 | NULL | 3 |

+------+------+------+

select * from a left join b on a.x = b.y left join c on b.y = c.z order by a.x;

+------+------+------+

| x | y | z |

+------+------+------+

| 1 | 1 | NULL |

| 2 | 2 | 2 |

| 3 | NULL | NULL |

+------+------+------+

87a52f305aced5a2561e52475ad40e21.png

第二次join使用等值连接尝试

select * from a left join b on a.x = b.y inner join c on b.y = c.z order by a.x;

+------+------+------+

| x | y | z |

+------+------+------+

| 2 | 2 | 2 |

+------+------+------+

一些例子

查询不是经理的员工

SELECT

e.emp_no

FROM

employees e

LEFT JOIN

dept_manager dm ON e.emp_no = dm.emp_no

WHERE

dm.emp_no IS NULL

LIMIT 3;

分页技巧

使用where而不是使用limit 10000,10 因为limit会扫10000前面的1w条数据

select * from employees where emp_no > 10000 limit 10;

根据生日分页

alter table employees add index idx_birth(birth_date,emp_no); -- 建立联合索引

select * from employees where birth_date > '1952-02-02' order by birth_date limit 10;

+--------+------------+------------+-----------+--------+------------+

| emp_no | birth_date | first_name | last_name | gender | hire_date |

+--------+------------+------------+-----------+--------+------------+

| 16093 | 1952-02-03 | Luise | Tramer | M | 1992-02-28 |

| 16447 | 1952-02-03 | Zhiguo | Savasere | F | 1987-04-28 |

| 23857 | 1952-02-03 | Kristen | Frijda | M | 1986-05-31 |

| 27259 | 1952-02-03 | Michaela | Lipner | M | 1997-09-05 |

| 32094 | 1952-02-03 | Mohd | Buchter | F | 1986-03-16 |

| 32641 | 1952-02-03 | Tua | Tetzlaff | F | 1988-12-01 |

| 41374 | 1952-02-03 | JiYoung | Schurmann | M | 1988-01-25 |

| 43737 | 1952-02-03 | Debatosh | Beerel | F | 1987-12-07 |

| 56225 | 1952-02-03 | Miquel | Rusmann | F | 1990-06-17 |

| 59577 | 1952-02-03 | Adel | Swiler | F | 1986-11-27 |

+--------+------------+------------+-----------+--------+------------+

-- 错误的结果,日期直接跳到了02-03

select * from employees where (birth_date,emp_no) > ('1952-02-02', 10010) order by birth_date limit 10;

+--------+------------+------------+-------------+--------+------------+

| emp_no | birth_date | first_name | last_name | gender | hire_date |

+--------+------------+------------+-------------+--------+------------+

| 12282 | 1952-02-02 | Tadahiro | Delgrange | M | 1997-01-09 |

| 13944 | 1952-02-02 | Takahito | Maierhofer | M | 1989-01-18 |

| 22614 | 1952-02-02 | Dung | Madeira | M | 1989-01-24 |

| 29456 | 1952-02-02 | Barun | Krohm | F | 1992-11-23 |

| 33131 | 1952-02-02 | Reinhold | Savasere | M | 1998-01-30 |

| 40660 | 1952-02-02 | Piyush | Erbe | F | 1988-04-04 |

| 48910 | 1952-02-02 | Zhongwei | DuBourdieux | M | 1999-12-19 |

| 51486 | 1952-02-02 | Jianwen | Sigstam | F | 1989-07-20 |

| 59884 | 1952-02-02 | Fan | Przulj | M | 1991-09-25 |

| 61382 | 1952-02-02 | Kristof | Ranft | M | 1989-04-21 |

+--------+------------+------------+-------------+--------+------------+

-- 正确的结果

select o_orderkey,o_orderstatus,o_totalprice from orders where(o_orderdate,o_orderkey) > ('1992-01-01',88199) order by o_orderdate limit 10;

select o_orderkey,o_orderstatus,o_totalprice from orders order by o_orderdate limit 10,10;

-- 结果一致

当然分页推荐使用redis来做,用数据库实现其实意义不大

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值