MySQL学习笔记

闲来无事把之前的MySQL笔记给整理了。
可以结合B站尚硅谷李玉婷老师的视频以及黑马程序员的视频使用。
李玉婷:MySQL_基础+高级篇
黑马:黑马程序员 MySQL数据库入门到精通,从mysql安装到mysql高级、mysql优化全囊括

以下内容,【 】表示可省略,/和|表示或;
由于李玉婷老师授课时所用MySQL版本为5.5,现今已经更新到了8.0版本以上了,有些语句可能有所不同,我基本会标注出来。
如果其中有什么错误或者有什么疑问可以在评论区说出,有需要相关课件或者笔记的小伙伴也可以私信我。

建表语句

/*
SQLyog Ultimate v10.00 Beta1
MySQL - 5.5.15 : Database - myemployees
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`myemployees` /*!40100 DEFAULT CHARACTER SET gb2312 */;

USE `myemployees`;

/*Table structure for table `departments` */

DROP TABLE IF EXISTS `departments`;

CREATE TABLE `departments` (
  `department_id` int(4) NOT NULL AUTO_INCREMENT,
  `department_name` varchar(3) DEFAULT NULL,
  `manager_id` int(6) DEFAULT NULL,
  `location_id` int(4) DEFAULT NULL,
  PRIMARY KEY (`department_id`),
  KEY `loc_id_fk` (`location_id`),
  CONSTRAINT `loc_id_fk` FOREIGN KEY (`location_id`) REFERENCES `locations` (`location_id`)
) ENGINE=InnoDB AUTO_INCREMENT=271 DEFAULT CHARSET=gb2312;

/*Data for the table `departments` */

insert  into `departments`(`department_id`,`department_name`,`manager_id`,`location_id`) values (10,'Adm',200,1700),(20,'Mar',201,1800),(30,'Pur',114,1700),(40,'Hum',203,2400),(50,'Shi',121,1500),(60,'IT',103,1400),(70,'Pub',204,2700),(80,'Sal',145,2500),(90,'Exe',100,1700),(100,'Fin',108,1700),(110,'Acc',205,1700),(120,'Tre',NULL,1700),(130,'Cor',NULL,1700),(140,'Con',NULL,1700),(150,'Sha',NULL,1700),(160,'Ben',NULL,1700),(170,'Man',NULL,1700),(180,'Con',NULL,1700),(190,'Con',NULL,1700),(200,'Ope',NULL,1700),(210,'IT ',NULL,1700),(220,'NOC',NULL,1700),(230,'IT ',NULL,1700),(240,'Gov',NULL,1700),(250,'Ret',NULL,1700),(260,'Rec',NULL,1700),(270,'Pay',NULL,1700);

/*Table structure for table `employees` */

DROP TABLE IF EXISTS `employees`;

CREATE TABLE `employees` (
  `employee_id` int(6) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(20) DEFAULT NULL,
  `last_name` varchar(25) DEFAULT NULL,
  `email` varchar(25) DEFAULT NULL,
  `phone_number` varchar(20) DEFAULT NULL,
  `job_id` varchar(10) DEFAULT NULL,
  `salary` double(10,2) DEFAULT NULL,
  `commission_pct` double(4,2) DEFAULT NULL,
  `manager_id` int(6) DEFAULT NULL,
  `department_id` int(4) DEFAULT NULL,
  `hiredate` datetime DEFAULT NULL,
  PRIMARY KEY (`employee_id`),
  KEY `dept_id_fk` (`department_id`),
  KEY `job_id_fk` (`job_id`),
  CONSTRAINT `dept_id_fk` FOREIGN KEY (`department_id`) REFERENCES `departments` (`department_id`),
  CONSTRAINT `job_id_fk` FOREIGN KEY (`job_id`) REFERENCES `jobs` (`job_id`)
) ENGINE=InnoDB AUTO_INCREMENT=207 DEFAULT CHARSET=gb2312;

/*Data for the table `employees` */

insert  into `employees`(`employee_id`,`first_name`,`last_name`,`email`,`phone_number`,`job_id`,`salary`,`commission_pct`,`manager_id`,`department_id`,`hiredate`) values
(100,'Steven','K_ing','SKING','515.123.4567','AD_PRES',24000.00,NULL,NULL,90,'1992-04-03 00:00:00'),
(101,'Neena','Kochhar','NKOCHHAR','515.123.4568','AD_VP',17000.00,NULL,100,90,'1992-04-03 00:00:00'),
(102,'Lex','De Haan','LDEHAAN','515.123.4569','AD_VP',17000.00,NULL,100,90,'1992-04-03 00:00:00'),
(103,'Alexander','Hunold','AHUNOLD','590.423.4567','IT_PROG',9000.00,NULL,102,60,'1992-04-03 00:00:00'),
(104,'Bruce','Ernst','BERNST','590.423.4568','IT_PROG',6000.00,NULL,103,60,'1992-04-03 00:00:00'),
(105,'David','Austin','DAUSTIN','590.423.4569','IT_PROG',4800.00,NULL,103,60,'1998-03-03 00:00:00'),
(106,'Valli','Pataballa','VPATABAL','590.423.4560','IT_PROG',4800.00,NULL,103,60,'1998-03-03 00:00:00'),
(107,'Diana','Lorentz','DLORENTZ','590.423.5567','IT_PROG',4200.00,NULL,103,60,'1998-03-03 00:00:00'),
(108,'Nancy','Greenberg','NGREENBE','515.124.4569','FI_MGR',12000.00,NULL,101,100,'1998-03-03 00:00:00'),
(109,'Daniel','Faviet','DFAVIET','515.124.4169','FI_ACCOUNT',9000.00,NULL,108,100,'1998-03-03 00:00:00'),
(110,'John','Chen','JCHEN','515.124.4269','FI_ACCOUNT',8200.00,NULL,108,100,'2000-09-09 00:00:00'),
(111,'Ismael','Sciarra','ISCIARRA','515.124.4369','FI_ACCOUNT',7700.00,NULL,108,100,'2000-09-09 00:00:00'),
(112,'Jose Manuel','Urman','JMURMAN','515.124.4469','FI_ACCOUNT',7800.00,NULL,108,100,'2000-09-09 00:00:00'),
(113,'Luis','Popp','LPOPP','515.124.4567','FI_ACCOUNT',6900.00,NULL,108,100,'2000-09-09 00:00:00'),
(114,'Den','Raphaely','DRAPHEAL','515.127.4561','PU_MAN',11000.00,NULL,100,30,'2000-09-09 00:00:00'),
(115,'Alexander','Khoo','AKHOO','515.127.4562','PU_CLERK',3100.00,NULL,114,30,'2000-09-09 00:00:00'),
(116,'Shelli','Baida','SBAIDA','515.127.4563','PU_CLERK',2900.00,NULL,114,30,'2000-09-09 00:00:00'),
(117,'Sigal','Tobias','STOBIAS','515.127.4564','PU_CLERK',2800.00,NULL,114,30,'2000-09-09 00:00:00'),
(118,'Guy','Himuro','GHIMURO','515.127.4565','PU_CLERK',2600.00,NULL,114,30,'2000-09-09 00:00:00'),
(119,'Karen','Colmenares','KCOLMENA','515.127.4566','PU_CLERK',2500.00,NULL,114,30,'2000-09-09 00:00:00'),
(120,'Matthew','Weiss','MWEISS','650.123.1234','ST_MAN',8000.00,NULL,100,50,'2004-02-06 00:00:00'),
(121,'Adam','Fripp','AFRIPP','650.123.2234','ST_MAN',8200.00,NULL,100,50,'2004-02-06 00:00:00'),
(122,'Payam','Kaufling','PKAUFLIN','650.123.3234','ST_MAN',7900.00,NULL,100,50,'2004-02-06 00:00:00'),
(123,'Shanta','Vollman','SVOLLMAN','650.123.4234','ST_MAN',6500.00,NULL,100,50,'2004-02-06 00:00:00'),
(124,'Kevin','Mourgos','KMOURGOS','650.123.5234','ST_MAN',5800.00,NULL,100,50,'2004-02-06 00:00:00'),
(125,'Julia','Nayer','JNAYER','650.124.1214','ST_CLERK',3200.00,NULL,120,50,'2004-02-06 00:00:00'),
(126,'Irene','Mikkilineni','IMIKKILI','650.124.1224','ST_CLERK',2700.00,NULL,120,50,'2004-02-06 00:00:00'),
(127,'James','Landry','JLANDRY','650.124.1334','ST_CLERK',2400.00,NULL,120,50,'2004-02-06 00:00:00'),
(128,'Steven','Markle','SMARKLE','650.124.1434','ST_CLERK',2200.00,NULL,120,50,'2004-02-06 00:00:00'),
(129,'Laura','Bissot','LBISSOT','650.124.5234','ST_CLERK',3300.00,NULL,121,50,'2004-02-06 00:00:00'),
(130,'Mozhe','Atkinson','MATKINSO','650.124.6234','ST_CLERK',2800.00,NULL,121,50,'2004-02-06 00:00:00'),
(131,'James','Marlow','JAMRLOW','650.124.7234','ST_CLERK',2500.00,NULL,121,50,'2004-02-06 00:00:00'),
(132,'TJ','Olson','TJOLSON','650.124.8234','ST_CLERK',2100.00,NULL,121,50,'2004-02-06 00:00:00'),
(133,'Jason','Mallin','JMALLIN','650.127.1934','ST_CLERK',3300.00,NULL,122,50,'2004-02-06 00:00:00'),
(134,'Michael','Rogers','MROGERS','650.127.1834','ST_CLERK',2900.00,NULL,122,50,'2002-12-23 00:00:00'),
(135,'Ki','Gee','KGEE','650.127.1734','ST_CLERK',2400.00,NULL,122,50,'2002-12-23 00:00:00'),
(136,'Hazel','Philtanker','HPHILTAN','650.127.1634','ST_CLERK',2200.00,NULL,122,50,'2002-12-23 00:00:00'),
(137,'Renske','Ladwig','RLADWIG','650.121.1234','ST_CLERK',3600.00,NULL,123,50,'2002-12-23 00:00:00'),
(138,'Stephen','Stiles','SSTILES','650.121.2034','ST_CLERK',3200.00,NULL,123,50,'2002-12-23 00:00:00'),
(139,'John','Seo','JSEO','650.121.2019','ST_CLERK',2700.00,NULL,123,50,'2002-12-23 00:00:00'),
(140,'Joshua','Patel','JPATEL','650.121.1834','ST_CLERK',2500.00,NULL,123,50,'2002-12-23 00:00:00'),
(141,'Trenna','Rajs','TRAJS','650.121.8009','ST_CLERK',3500.00,NULL,124,50,'2002-12-23 00:00:00'),
(142,'Curtis','Davies','CDAVIES','650.121.2994','ST_CLERK',3100.00,NULL,124,50,'2002-12-23 00:00:00'),
(143,'Randall','Matos','RMATOS','650.121.2874','ST_CLERK',2600.00,NULL,124,50,'2002-12-23 00:00:00'),
(144,'Peter','Vargas','PVARGAS','650.121.2004','ST_CLERK',2500.00,NULL,124,50,'2002-12-23 00:00:00'),
(145,'John','Russell','JRUSSEL','011.44.1344.429268','SA_MAN',14000.00,0.40,100,80,'2002-12-23 00:00:00'),
(146,'Karen','Partners','KPARTNER','011.44.1344.467268','SA_MAN',13500.00,0.30,100,80,'2002-12-23 00:00:00'),
(147,'Alberto','Errazuriz','AERRAZUR','011.44.1344.429278','SA_MAN',12000.00,0.30,100,80,'2002-12-23 00:00:00'),
(148,'Gerald','Cambrault','GCAMBRAU','011.44.1344.619268','SA_MAN',11000.00,0.30,100,80,'2002-12-23 00:00:00'),
(149,'Eleni','Zlotkey','EZLOTKEY','011.44.1344.429018','SA_MAN',10500.00,0.20,100,80,'2002-12-23 00:00:00'),
(150,'Peter','Tucker','PTUCKER','011.44.1344.129268','SA_REP',10000.00,0.30,145,80,'2014-03-05 00:00:00'),
(151,'David','Bernstein','DBERNSTE','011.44.1344.345268','SA_REP',9500.00,0.25,145,80,'2014-03-05 00:00:00'),
(152,'Peter','Hall','PHALL','011.44.1344.478968','SA_REP',9000.00,0.25,145,80,'2014-03-05 00:00:00'),
(153,'Christopher','Olsen','COLSEN','011.44.1344.498718','SA_REP',8000.00,0.20,145,80,'2014-03-05 00:00:00'),
(154,'Nanette','Cambrault','NCAMBRAU','011.44.1344.987668','SA_REP',7500.00,0.20,145,80,'2014-03-05 00:00:00'),
(155,'Oliver','Tuvault','OTUVAULT','011.44.1344.486508','SA_REP',7000.00,0.15,145,80,'2014-03-05 00:00:00'),
(156,'Janette','K_ing','JKING','011.44.1345.429268','SA_REP',10000.00,0.35,146,80,'2014-03-05 00:00:00'),
(157,'Patrick','Sully','PSULLY','011.44.1345.929268','SA_REP',9500.00,0.35,146,80,'2014-03-05 00:00:00'),
(158,'Allan','McEwen','AMCEWEN','011.44.1345.829268','SA_REP',9000.00,0.35,146,80,'2014-03-05 00:00:00'),
(159,'Lindsey','Smith','LSMITH','011.44.1345.729268','SA_REP',8000.00,0.30,146,80,'2014-03-05 00:00:00'),
(160,'Louise','Doran','LDORAN','011.44.1345.629268','SA_REP',7500.00,0.30,146,80,'2014-03-05 00:00:00'),
(161,'Sarath','Sewall','SSEWALL','011.44.1345.529268','SA_REP',7000.00,0.25,146,80,'2014-03-05 00:00:00'),
(162,'Clara','Vishney','CVISHNEY','011.44.1346.129268','SA_REP',10500.00,0.25,147,80,'2014-03-05 00:00:00'),
(163,'Danielle','Greene','DGREENE','011.44.1346.229268','SA_REP',9500.00,0.15,147,80,'2014-03-05 00:00:00'),
(164,'Mattea','Marvins','MMARVINS','011.44.1346.329268','SA_REP',7200.00,0.10,147,80,'2014-03-05 00:00:00'),
(165,'David','Lee','DLEE','011.44.1346.529268','SA_REP',6800.00,0.10,147,80,'2014-03-05 00:00:00'),
(166,'Sundar','Ande','SANDE','011.44.1346.629268','SA_REP',6400.00,0.10,147,80,'2014-03-05 00:00:00'),
(167,'Amit','Banda','ABANDA','011.44.1346.729268','SA_REP',6200.00,0.10,147,80,'2014-03-05 00:00:00'),
(168,'Lisa','Ozer','LOZER','011.44.1343.929268','SA_REP',11500.00,0.25,148,80,'2014-03-05 00:00:00'),
(169,'Harrison','Bloom','HBLOOM','011.44.1343.829268','SA_REP',10000.00,0.20,148,80,'2014-03-05 00:00:00'),
(170,'Tayler','Fox','TFOX','011.44.1343.729268','SA_REP',9600.00,0.20,148,80,'2014-03-05 00:00:00'),
(171,'William','Smith','WSMITH','011.44.1343.629268','SA_REP',7400.00,0.15,148,80,'2014-03-05 00:00:00'),
(172,'Elizabeth','Bates','EBATES','011.44.1343.529268','SA_REP',7300.00,0.15,148,80,'2014-03-05 00:00:00'),
(173,'Sundita','Kumar','SKUMAR','011.44.1343.329268','SA_REP',6100.00,0.10,148,80,'2014-03-05 00:00:00'),
(174,'Ellen','Abel','EABEL','011.44.1644.429267','SA_REP',11000.00,0.30,149,80,'2014-03-05 00:00:00'),
(175,'Alyssa','Hutton','AHUTTON','011.44.1644.429266','SA_REP',8800.00,0.25,149,80,'2014-03-05 00:00:00'),
(176,'Jonathon','Taylor','JTAYLOR','011.44.1644.429265','SA_REP',8600.00,0.20,149,80,'2014-03-05 00:00:00'),
(177,'Jack','Livingston','JLIVINGS','011.44.1644.429264','SA_REP',8400.00,0.20,149,80,'2014-03-05 00:00:00'),
(178,'Kimberely','Grant','KGRANT','011.44.1644.429263','SA_REP',7000.00,0.15,149,NULL,'2014-03-05 00:00:00'),
(179,'Charles','Johnson','CJOHNSON','011.44.1644.429262','SA_REP',6200.00,0.10,149,80,'2014-03-05 00:00:00'),
(180,'Winston','Taylor','WTAYLOR','650.507.9876','SH_CLERK',3200.00,NULL,120,50,'2014-03-05 00:00:00'),
(181,'Jean','Fleaur','JFLEAUR','650.507.9877','SH_CLERK',3100.00,NULL,120,50,'2014-03-05 00:00:00'),
(182,'Martha','Sullivan','MSULLIVA','650.507.9878','SH_CLERK',2500.00,NULL,120,50,'2014-03-05 00:00:00'),
(183,'Girard','Geoni','GGEONI','650.507.9879','SH_CLERK',2800.00,NULL,120,50,'2014-03-05 00:00:00'),
(184,'Nandita','Sarchand','NSARCHAN','650.509.1876','SH_CLERK',4200.00,NULL,121,50,'2014-03-05 00:00:00'),
(185,'Alexis','Bull','ABULL','650.509.2876','SH_CLERK',4100.00,NULL,121,50,'2014-03-05 00:00:00'),
(186,'Julia','Dellinger','JDELLING','650.509.3876','SH_CLERK',3400.00,NULL,121,50,'2014-03-05 00:00:00'),
(187,'Anthony','Cabrio','ACABRIO','650.509.4876','SH_CLERK',3000.00,NULL,121,50,'2014-03-05 00:00:00'),
(188,'Kelly','Chung','KCHUNG','650.505.1876','SH_CLERK',3800.00,NULL,122,50,'2014-03-05 00:00:00'),
(189,'Jennifer','Dilly','JDILLY','650.505.2876','SH_CLERK',3600.00,NULL,122,50,'2014-03-05 00:00:00'),
(190,'Timothy','Gates','TGATES','650.505.3876','SH_CLERK',2900.00,NULL,122,50,'2014-03-05 00:00:00'),
(191,'Randall','Perkins','RPERKINS','650.505.4876','SH_CLERK',2500.00,NULL,122,50,'2014-03-05 00:00:00'),
(192,'Sarah','Bell','SBELL','650.501.1876','SH_CLERK',4000.00,NULL,123,50,'2014-03-05 00:00:00'),
(193,'Britney','Everett','BEVERETT','650.501.2876','SH_CLERK',3900.00,NULL,123,50,'2014-03-05 00:00:00'),
(194,'Samuel','McCain','SMCCAIN','650.501.3876','SH_CLERK',3200.00,NULL,123,50,'2014-03-05 00:00:00'),
(195,'Vance','Jones','VJONES','650.501.4876','SH_CLERK',2800.00,NULL,123,50,'2014-03-05 00:00:00'),
(196,'Alana','Walsh','AWALSH','650.507.9811','SH_CLERK',3100.00,NULL,124,50,'2014-03-05 00:00:00'),
(197,'Kevin','Feeney','KFEENEY','650.507.9822','SH_CLERK',3000.00,NULL,124,50,'2014-03-05 00:00:00'),
(198,'Donald','OConnell','DOCONNEL','650.507.9833','SH_CLERK',2600.00,NULL,124,50,'2014-03-05 00:00:00'),
(199,'Douglas','Grant','DGRANT','650.507.9844','SH_CLERK',2600.00,NULL,124,50,'2014-03-05 00:00:00'),
(200,'Jennifer','Whalen','JWHALEN','515.123.4444','AD_ASST',4400.00,NULL,101,10,'2016-03-03 00:00:00'),
(201,'Michael','Hartstein','MHARTSTE','515.123.5555','MK_MAN',13000.00,NULL,100,20,'2016-03-03 00:00:00'),
(202,'Pat','Fay','PFAY','603.123.6666','MK_REP',6000.00,NULL,201,20,'2016-03-03 00:00:00'),
(203,'Susan','Mavris','SMAVRIS','515.123.7777','HR_REP',6500.00,NULL,101,40,'2016-03-03 00:00:00'),
(204,'Hermann','Baer','HBAER','515.123.8888','PR_REP',10000.00,NULL,101,70,'2016-03-03 00:00:00'),
(205,'Shelley','Higgins','SHIGGINS','515.123.8080','AC_MGR',12000.00,NULL,101,110,'2016-03-03 00:00:00'),
(206,'William','Gietz','WGIETZ','515.123.8181','AC_ACCOUNT',8300.00,NULL,205,110,'2016-03-03 00:00:00');

/*Table structure for table `jobs` */

DROP TABLE IF EXISTS `jobs`;

CREATE TABLE `jobs` (
  `job_id` varchar(10) NOT NULL,
  `job_title` varchar(35) DEFAULT NULL,
  `min_salary` int(6) DEFAULT NULL,
  `max_salary` int(6) DEFAULT NULL,
  PRIMARY KEY (`job_id`)
) ENGINE=InnoDB DEFAULT CHARSET=gb2312;

/*Data for the table `jobs` */

insert  into `jobs`(`job_id`,`job_title`,`min_salary`,`max_salary`) values 
('AC_ACCOUNT','Public Accountant',4200,9000),
('AC_MGR','Accounting Manager',8200,16000),
('AD_ASST','Administration Assistant',3000,6000),
('AD_PRES','President',20000,40000),
('AD_VP','Administration Vice President',15000,30000),
('FI_ACCOUNT','Accountant',4200,9000),
('FI_MGR','Finance Manager',8200,16000),
('HR_REP','Human Resources Representative',4000,9000),
('IT_PROG','Programmer',4000,10000),
('MK_MAN','Marketing Manager',9000,15000),
('MK_REP','Marketing Representative',4000,9000),
('PR_REP','Public Relations Representative',4500,10500),
('PU_CLERK','Purchasing Clerk',2500,5500),
('PU_MAN','Purchasing Manager',8000,15000),
('SA_MAN','Sales Manager',10000,20000),
('SA_REP','Sales Representative',6000,12000),
('SH_CLERK','Shipping Clerk',2500,5500),
('ST_CLERK','Stock Clerk',2000,5000),
('ST_MAN','Stock Manager',5500,8500);

/*Table structure for table `locations` */

DROP TABLE IF EXISTS `locations`;

CREATE TABLE `locations` (
  `location_id` int(11) NOT NULL AUTO_INCREMENT,
  `street_address` varchar(40) DEFAULT NULL,
  `postal_code` varchar(12) DEFAULT NULL,
  `city` varchar(30) DEFAULT NULL,
  `state_province` varchar(25) DEFAULT NULL,
  `country_id` varchar(2) DEFAULT NULL,
  PRIMARY KEY (`location_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3201 DEFAULT CHARSET=gb2312;

/*Data for the table `locations` */

insert  into `locations`(`location_id`,`street_address`,`postal_code`,`city`,`state_province`,`country_id`) values 
(1000,'1297 Via Cola di Rie','00989','Roma',NULL,'IT'),
(1100,'93091 Calle della Testa','10934','Venice',NULL,'IT'),
(1200,'2017 Shinjuku-ku','1689','Tokyo','Tokyo Prefecture','JP'),
(1300,'9450 Kamiya-cho','6823','Hiroshima',NULL,'JP'),
(1400,'2014 Jabberwocky Rd','26192','Southlake','Texas','US'),
(1500,'2011 Interiors Blvd','99236','South San Francisco','California','US'),
(1600,'2007 Zagora St','50090','South Brunswick','New Jersey','US'),
(1700,'2004 Charade Rd','98199','Seattle','Washington','US'),
(1800,'147 Spadina Ave','M5V 2L7','Toronto','Ontario','CA'),
(1900,'6092 Boxwood St','YSW 9T2','Whitehorse','Yukon','CA'),
(2000,'40-5-12 Laogianggen','190518','Beijing',NULL,'CN'),
(2100,'1298 Vileparle (E)','490231','Bombay','Maharashtra','IN'),
(2200,'12-98 Victoria Street','2901','Sydney','New South Wales','AU'),
(2300,'198 Clementi North','540198','Singapore',NULL,'SG'),
(2400,'8204 Arthur St',NULL,'London',NULL,'UK'),
(2500,'Magdalen Centre, The Oxford Science Park','OX9 9ZB','Oxford','Oxford','UK'),
(2600,'9702 Chester Road','09629850293','Stretford','Manchester','UK'),
(2700,'Schwanthalerstr. 7031','80925','Munich','Bavaria','DE'),
(2800,'Rua Frei Caneca 1360 ','01307-002','Sao Paulo','Sao Paulo','BR'),
(2900,'20 Rue des Corps-Saints','1730','Geneva','Geneve','CH'),
(3000,'Murtenstrasse 921','3095','Bern','BE','CH'),
(3100,'Pieter Breughelstraat 837','3029SK','Utrecht','Utrecht','NL'),
(3200,'Mariano Escobedo 9991','11932','Mexico City','Distrito Federal,','MX');

/*Table structure for table `job_grades` */

CREATE TABLE job_grades(
	grade_level VARCHAR(3),
	lowest_sal  INT,
	highest_sal INT
);

/*Data for the table `job_grades` */

insert  into job_grades valus 
('A', 1000, 2999),
('B', 3000, 5999),
('C', 6000, 9999),
('D', 10000, 14999),
('E', 15000, 24999),
('F', 25000, 40000);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*
SQLyog Ultimate v10.00 Beta1
MySQL - 5.7.18-log : Database - girls
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`girls` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `girls`;

/*Table structure for table `admin` */

DROP TABLE IF EXISTS `admin`;

CREATE TABLE `admin` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(10) NOT NULL,
  `password` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `admin` */

insert  into `admin`(`id`,`username`,`password`) values (1,'john','8888'),(2,'lyt','6666');

/*Table structure for table `beauty` */

DROP TABLE IF EXISTS `beauty`;

CREATE TABLE `beauty` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `sex` char(1) DEFAULT '女',
  `borndate` datetime DEFAULT '1987-01-01 00:00:00',
  `phone` varchar(11) NOT NULL,
  `photo` blob,
  `boyfriend_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

/*Data for the table `beauty` */

insert  into `beauty`(`id`,`name`,`sex`,`borndate`,`phone`,`photo`,`boyfriend_id`) values 
(1,'柳岩','女','1988-02-03 00:00:00','18209876577',NULL,8),
(2,'苍老师','女','1987-12-30 00:00:00','18219876577',NULL,9),
(3,'Angelababy','女','1989-02-03 00:00:00','18209876567',NULL,3),
(4,'热巴','女','1993-02-03 00:00:00','18209876579',NULL,2),
(5,'周冬雨','女','1992-02-03 00:00:00','18209179577',NULL,9),
(6,'周芷若','女','1988-02-03 00:00:00','18209876577',NULL,1),
(7,'岳灵珊','女','1987-12-30 00:00:00','18219876577',NULL,9),
(8,'小昭','女','1989-02-03 00:00:00','18209876567',NULL,1),
(9,'双儿','女','1993-02-03 00:00:00','18209876579',NULL,9),
(10,'王语嫣','女','1992-02-03 00:00:00','18209179577',NULL,4),
(11,'夏雪','女','1993-02-03 00:00:00','18209876579',NULL,9),
(12,'赵敏','女','1992-02-03 00:00:00','18209179577',NULL,1);

/*Table structure for table `boys` */

DROP TABLE IF EXISTS `boys`;

CREATE TABLE `boys` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `boyName` varchar(20) DEFAULT NULL,
  `userCP` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

/*Data for the table `boys` */

insert  into `boys`(`id`,`boyName`,`userCP`) values 
(1,'张无忌',100),
(2,'鹿晗',800),
(3,'黄晓明',50),
(4,'段誉',300);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

一、数据类型

一、数值型
1.整型
分类:
tinyintsmallintmediumintint/integerbigint

整数类型                  字节                                          范围
Tinyint                    1                                有符号:-128~127,无符号:0~255
Smallint                   2                             有符号:-32768~32767,无符号:0~65535
Mediumint                  3                         有符号:-8388608~8388607,无符号:0~1677215
Intinteger              4                     有符号:- 2147483648~2147483647,无符号:0~4294967295
Bigint                     8         有符号:-9223372036854775808~9223372036854775807无符号:0~9223372036854775807*2+1

特点:
① 如果不设置无符号还是有符号,**默认是有符号**,如果想设置无符号,需要字段后面添加unsigned关键字。
② 如果插入的数值超出了整型的范围,会报out of range异常,并且插入临界值(8.0以上版本会直接报错,并且不插入任何值)。
③ 如果不设置长度,会有默认的长度。
④int(N)中的N不是限制字段取值范围的,N这个值即长度,代表了显示的最小宽度,是为了在字段中的值不够时补零的,如果不够会用0在左边填充,但是必须搭配zerofill使用!
如果int不指定N,则默认为11int的取值范围是固定的(04294967295)或(-21474836482147483647);使用zerofill约束后,范围默认为无符号。
如果插入8888,查看的数据应该为00008888,如果插入1111111111111,查看的数据应该为4294967295

2.小数
分类:
1.浮点型:float(M,D),double(M,D)
2.定点型:dec(M,D),decimal(M,D)

浮点数类型       字节                               范围
float             4                   ±1.75494351E-38~±3.402823466E+38
double            8          ±2.2250738585072014E-308~±1.7976931348623157E+308

定点数类型                  字节                              范围
DEC(M,D),DECIMAL(M,D)       M+2    最大取值范围与double相同,给定decimal的有效取值范围由M和D决定

特点:
①M:总长度(整数部位+小数部位)
 D:小数部位

如果小数部分超出范围,则会将超出的数值四舍五入。如double52),插入123.345会变成123.35;插入123.23499会变成123.23。
如果整数部分超过范围,则插入临界值(8.0版本直接报错,不会插入数值)。

②M和D都可以省略
如果是decimal,则M默认为10,D默认为0。
如果插入的数附带小数,则进行四舍五入。
如果是floatdouble,则会根据插入的数值的精度来决定精度。
float的默认长度为6double的默认长度为17。

③定点型的精确度较高,如果要求插入数值的精度较高如货币运算等则考虑使用。

#M和D的可用测试代码
DROP TABLE if exists tab_float;
CREATE TABLE tab_float(
  f1 FLOAT,
  f2 DOUBLE,
  f3 DECIMAL
);
SELECT * FROM tab_float;
DESC tab_float;
INSERT INTO tab_float VALUES(123.4523,123.4523,123.4523);
INSERT INTO tab_float VALUES(123.456,123.456,123.456);
INSERT INTO tab_float VALUES(123.4,123.4,123.4);
INSERT INTO tab_float VALUES(1523.4,1523.4,1523.4);

#原则:所选择的类型越简单越好,能保存数值的类型越小越好

二、字符型
分类:
较短的文本:char,varchar
字符串类型   最多字符数      描述及存储需求
char(M)         M        M为0~255之间的整数
varchar         M       M为0~65535之间的整数

较长的文本:text,blob(较大的二进制)

其他:
binaryvarbinary类型
说明:类似于charvarchar,不同的是它们包含二进制字符串而不包含非二进制字符串。

Enum类型
说明:又称为枚举类型,要求插入的值必须属于列表中指定的值之一。
	  如果列表成员为1~255,则需要1个字节存储。如果列表成员为255~65535,则需要2个字节存储。
	  最多需要65535个成员。

Set类型
说明:和Enum类型类似,里面可以保存0~64个成员。
	  和Enum 类型最大的区别是:SET 类型一次可以选取多个成员,而Enum 只能选一个。
      根据成员个数不同,存储所占的字节也不同。

特点:
|         |     写法   |             M的意思            |      特点      |空间的耗费|效率|
|  char   |  char(M)   | 最大的字符数,可以省略,默认为1 | 固定长度的字符 | 比较耗费 ||
| varchar | varchar(M) |     最大的字符数,不可以省略    | 可变长度的字符 | 比较节省 ||


/*
char与varchar都可以描述字符串,char是定长字符串,指定长度多长,就占用多少个字符,和字段值的长度无关。
varchar是变长字符串,指定的长度为最大占用长度。相对来说,char的性能会更高些。
如果是用utf8编码就是一个汉字就是一个字符。而一个字符占用多大的空间就得看编码了。
char的存储效率高,varchar节省空间。
*/

CREATE TABLE tab_char(c1 ENUM('a','b','c'));
INSERT INTO tab_char VALUES('a');
INSERT INTO tab_char VALUES('b');
INSERT INTO tab_char VALUES('c');
INSERT INTO tab_char VALUES('m');  #报错。
INSERT INTO tab_char VALUES('A');  #大写变为对应的小写。

SELECT * FROM tab_char;

CREATE TABLE tab_set(s1 SET('a','b','c','d'));
INSERT INTO tab_set VALUES('a');
INSERT INTO tab_set VALUES('A,B');  #大写变为对应的小写。
INSERT INTO tab_set VALUES('a,c,d');
INSERT INTO tab_set VALUES('a,c,a,d');  #a不按照set( )里的顺序,去除。
INSERT INTO tab_set VALUES('a,d,c,a,d');  #d,a不按照set( )里的顺序,去除。
INSERT INTO tab_set VALUES('a,d');  #能按顺序。

SELECT * FROM tab_set;


#enum只能选取一个列表成员,而set能一次选取多个列表成员,用逗号连接。且set无顺序要求,自动按照set()内顺序执行。
#set的逻辑比较难理解,慎用。

三、日期型
分类:
date            只保存日期
time            只保存时间
year             只保存年
datetime       保存日期+时间
timestamp      保存日期+时间

|类型     |大小|                   范围                  |        格式            |           描述           |
|DATE     |3|           1000-01-019999-12-31        |      YYYY-MM-DD        |           日期值         |
|TIME     |3|            838:59:59838:59:59         |       HH:MM:SS         |      时间值或持续时间     |
|YEAR     |1|                 19012155              |         YYYY           |          年份值          |
|DATETIME |8|  1000-01-01 00:00:009999-12-31 23:59:59|  YYYY-MM-DD HH:MM:SS   |      混合日期和时间值     |
|TIMESTAMP|4|  1970-01-01 00:00:012038-01-19 03:14:07|  YYYY-MM-DD HH:MM:SS   |   混合日期和时间值,时间戳 |

特点:
|            |字节 |     范围    |                                时区等的影响                                        |
|  datetime  |  8  |大,1000-9999|                            只能反映插入时的当地时区                                |
|  timestamp |  4  |小,1970-2038|和实际时区有关,能反映实际的日期,<br>timestamp的属性也受Mysql版本和SQLMode的影响很大|

#测试代码:
CREATE TABLE tab_date(
  t1 DATETIME,
  t2 TIMESTAMP
);
INSERT INTO tab_date VALUES(NOW(),NOW());
SELECT * FROM tab_date;
SHOW VARIABLES LIKE 'time_zone';#输出当前时区。
SET time_zone='+9:00'; 

二、DDL语句(数据定义语言)

一、查询
1.库操作
SELECT DATABASES;   #查询所有数据库
SELECT DATABASE();   #查询当前数据库

2.表操作
SHOW TABLES;    #查询当前数据库所有表
DESC 表名;   #查询表结构
SHOW CREATE TABLE 表名;  #查询指定表的建表语句

二、创建
1.库创建
CREATE DATABASEIF NOT EXISTS】 数据库名 【DEFAULT CHARSET 字符集】 【COLLATE 排序规则】;
CREATE DATABASEIF NOT EXISTS】 数据库名 【CHARACTER SET 字符集】;

2.表创建
CREATE TABLE 表名(
  列名1 列的类型【(长度) 】【COMMENT 注释】【约束】,
  列名2 列的类型【(长度) 】【COMMENT 注释】【约束】,
  列名3 列的类型【(长度) 】【COMMENT 注释】【约束】,
  ...
  列名n 列的类型【(长度) 】【COMMENT 注释】【约束】
)COMMENT 表注释】;

#案例:创建表Book
CREATE TABLE book(
  id INT COMMENT '编号',
  bName VARCHAR(20) COMMENT '图书名',
  price DOUBLE COMMENT '价格',
  authorId  INT COMMENT '作者编号',
  publishDate DATETIME COMMENT '出版日期'
) COMMENT '书';

三、修改
1.库修改
#①修改库名(已经废弃了)
RENAME DATABASE 老库名 TO 新库名;
#现在直接到data文件夹里修改库名就行。
#②修改库的字符集
ALTER DATABASE 库名 CHARACTER SET 字符集;

2.表修改
#①修改列名和列类型
ALTER TABLE 表名 CHANGE 【COLUMN】 旧列名 新列名 列类型(长度)COMMENT 注释】【约束】;

#②修改列的数据类型
ALTER TABLE 表名 MODIFY 列名 新数据类型(长度);

#③添加新列
ALTER TABLE 表名 ADD 列名 列类型(长度)COMMENT 注释】【约束】;    #在最后一行后插入列
ALTER TABLE 表名 ADD 列名 列类型(长度)COMMENT 注释】【约束】【AFTER 列名】;    #在某一行之后插入列
ALTER TABLE 表名 ADD 列名 列类型(长度)COMMENT 注释】【约束】【FIRST;   #在第一行插入列

#④删除列
ALTER TABLE 表名 DROP 列名;

#⑤修改表名
ALTER TABLE 表名 RENAMETO/AS】 新表名;

四、删除
1.库删除
DROP DATABASE [IF EXISTS] 数据库名;

2.表删除(在删除表时,表中的全部数据也会被删掉)
#①删除表
DROP TABLE [IF EXISTS] 表名;
#②删除指定表,并重新创建该表
TRUNCATE TABLE 表名;

五、复制(可以跨库复制表)
#1.仅仅复制表的结构(即没有数据)
CREATE TABLE copy LIKE author;

#2.复制表的结构+数据(复制+查询)
CREATE TABLE copy2 
SELECT * FROM author;

#案例:只复制部分数据(加数据筛选条件)
CREATE TABLE copy3
SELECT id,au_name
FROM author 
WHERE nation='中国';

#案例:仅仅复制某些字段(给一个不可能成立的条件,这样就不会有符合的数据被选择)
CREATE TABLE copy4 
SELECT id,au_name
FROM author
WHERE 0;  #换成 WHERE 1=2也可以

六、使用
USE 数据库名;
案例:
#1.  创建表dept1
NAME  NULL?    TYPE
id      √     INT(7)
NAME    √   VARCHAR(25)

USE test;
CREATE TABLE dept1(
  id INT(7),
  NAME VARCHAR(25)
);

#2.  将表departments中的数据插入新表dept2中
CREATE TABLE dept2
SELECT department_id,department_name
FROM myemployees.departments;

#3.  创建表emp5
NAME      NULL?   TYPE
id          √     INT(7)
First_name  √  VARCHAR (25)
Last_name   √  VARCHAR(25)
Dept_id     √     INT(7)

CREATE TABLE emp5(
id INT(7),
first_name VARCHAR(25),
last_name VARCHAR(25),
dept_id INT(7)
);

#4.  将列Last_name的长度增加到50
ALTER TABLE emp5 MODIFY COLUMN last_name VARCHAR(50);

#5.  根据表employees创建employees2
CREATE TABLE employees2 LIKE myemployees.employees;

#6.  删除表emp5
DROP TABLE IF EXISTS emp5;

#7.  将表employees2重命名为emp5
ALTER TABLE employees2 RENAME TO emp5;

#8.在表dept和emp5中添加新列test_column,并检查所作的操作
ALTER TABLE emp5 ADD COLUMN test_column INT;

#9.直接删除表emp5中的列 dept_id
DESC emp5;
ALTER TABLE emp5 DROP COLUMN test_column;

三、DML语言(数据操作语言)

#修改报错的首先写set sql_safe_updates=0  准许修改

(一)添加数据
一、给指定字段添加数据
#方式一
INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (1,2, ...); 

#案例: 给employee表所有的字段添加数据 ;
insert into employee(id,workno,name,gender,age,idcard,entrydate)
values(1,'1','Itcast','男',10,'123456789012345678','2000-01-01');

#方式二
INSERT INTO 表名 SET 字段名1=,字段名2=,...

#案例:
INSERT INTO beauty
SET id=19,NAME='刘涛',phone='999';

#两种方式大pk ★
1、方式一支持插入多行,方式二不支持
INSERT INTO beauty
VALUES(23,'唐艺昕1','女','1990-4-23','1898888888',NULL,2),
(24,'唐艺昕2','女','1990-4-23','1898888888',NULL,2),
(25,'唐艺昕3','女','1990-4-23','1898888888',NULL,2);

2、方式一支持子查询,方式二不支持
INSERT INTO beauty(id,NAME,phone)
SELECT 26,'宋茜','11809866';

INSERT INTO beauty(id,NAME,phone)
SELECT id,boyname,'1234567'
FROM boys WHERE id<3;

#values和select都相当于创建一个虚拟表用insert into插入到表中。

二、给全部字段添加数据
INSERT INTO 表名 VALUES (1,2, ...); 
#案例:插入数据到employee表,具体的SQL如下:
insert into employee values(2,'2','张无忌','男',18,'123456789012345670','2005-01-01');

三、.批量添加数据
#方式一:
INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (1,2, ...), (1,2, ...), (1,2, ...) ;
#方式二:
INSERT INTO 表名 VALUES (1,2, ...), (1,2, ...), (1,2, ...) ;

#案例:批量插入数据到employee表,具体的SQL如下:
insert into employee values(3,'3','韦一笑','男',38,'123456789012345670','2005-01-01'),(4,'4','赵敏','女',18,'123456789012345670','2005-01-01');

#注意事项:
• 插入数据时,指定的字段顺序需要与值的顺序是一一对应的,且字段数和值的个数必须一致。
• 字段的顺序可以调换,但相应的数据也应当调换。
• 字符串和日期型数据应该包含在引号中。
• 插入的数据大小,应该在字段的规定范围内,且插入的值的类型要与字段的类型一致或兼容。
• 不可以为null的列必须插入值。
• 联合查询只能创建虚拟表,而insert into才是修改表,勿混淆。

#1.插入的值的类型要与列的类型一致或兼容
INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id)
VALUES(13,'唐艺昕','女','1990-4-23','1898888888',NULL,2);

#2.不可以为null的列必须插入值。可以为null的列可以选择插入null,也可以选择不写出省略对应的列名和数据。
#①:
INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id)
VALUES(13,'唐艺昕','女','1990-4-23','1898888888',NULL,2);

#②:
INSERT INTO beauty(id,NAME,sex,phone)
VALUES(15,'娜扎','女','1388888888');

#3.列的顺序可以调换
INSERT INTO beauty(NAME,sex,id,phone)
VALUES('蒋欣','女',16,'110');

#4.列数和值的个数必须一致
INSERT INTO beauty(NAME,sex,id,phone)
VALUES('关晓彤','女',17,'110');

#5.可以省略列名,默认所有列,而且列的顺序和表中列的顺序一致
INSERT INTO beauty
VALUES(18,'张飞','男',NULL,'119',NULL,NULL);

(二)修改数据
1.修改单表的记录★
语法:
update 表名
set=新值,=新值,...
where 筛选条件;

2.修改多表的记录【补充】
语法:
sql92语法:
update1 别名,2 别名
set=,...
where 连接条件
and 筛选条件;

sql99语法:
update1 别名
inner|left|right join2 别名
on 连接条件
set=,...
where 筛选条件;

#1.修改单表的记录
#案例1:修改beauty表中姓唐的女神的电话为13899888899
UPDATE beauty SET phone = '13899888899'
WHERE NAME LIKE '唐%';

#案例2:修改boys表中id好为2的名称为张飞,魅力值 10
UPDATE boys SET boyname='张飞',usercp=10
WHERE id=2;

#2.修改多表的记录
#案例 1:修改张无忌的女朋友的手机号为114
UPDATE boys bo
INNER JOIN beauty b ON bo.id=b.boyfriend_id
SET b.phone='119',bo.userCP=1000
WHERE bo.boyName='张无忌';

#案例2:修改没有男朋友的女神的男朋友编号都为2号
UPDATE boys bo
RIGHT JOIN beauty b ON bo.id=b.boyfriend_id
SET b.boyfriend_id=2
WHERE bo.id IS NULL;

SELECT * FROM boys;

(三)删除语句
方式一:delete
语法:
1、单表的删除【★】
delete from 表名 【where 筛选条件】 【limit 条目数】 

2、多表的删除【补充】
sql92语法:
delete1的别名,2的别名
from1 别名,2 别名
where 连接条件
and 筛选条件;

sql99语法:
delete1的别名,2的别名
from1 别名
inner|left|right join2 别名 on 连接条件
where 筛选条件;

方式二:truncate(又叫清除语句)
语法:truncate table 表名;

#方式一:delete
#1.单表的删除
#案例:删除手机号以9结尾的女神信息
DELETE FROM beauty WHERE phone LIKE '%9';
SELECT * FROM beauty;

#2.多表的删除
#案例:删除张无忌的女朋友的信息
DELETE b
FROM beauty b
INNER JOIN boys bo ON b.boyfriend_id = bo.id
WHERE bo.boyName='张无忌';

#案例:删除黄晓明的信息以及他女朋友的信息
DELETE b,bo
FROM beauty b
INNER JOIN boys bo ON b.boyfriend_id=bo.id
WHERE bo.boyName='黄晓明';

#方式二:truncate语句
#案例:将魅力值>100的男神信息删除
TRUNCATE TABLE boys ;

#delete pk truncate【面试题★】
1.delete 可以加where 条件,truncate不能加
2.truncate删除,效率高一丢丢
3.假如要删除的表中有自增长列,如果用delete删除后,再插入数据,自增长列的值从断点开始,
而truncate删除后,再插入数据,自增长列的值从1开始。
4.truncate删除没有返回值,delete删除有返回值
5.truncate删除不能回滚,delete删除可以回滚

#detele是一行行删除,自增长会继续;truncate是将整个表删除,然后再建一个同结构的表,自增长要重新开始

#测试语句:
SELECT * FROM boys;

DELETE FROM boys;
TRUNCATE TABLE boys;
INSERT INTO boys (boyname,usercp)
VALUES('张飞',100),('刘备',100),('关云长',100);
案例:
#1.  运行以下脚本创建表my_employees
USE my_employees;
CREATE TABLE my_employees(
  Id INT(10),
  First_name VARCHAR(10),
  Last_name VARCHAR(10),
  Userid VARCHAR(10),
  Salary DOUBLE(10,2)
);
CREATE TABLE users(
  id INT,
  userid VARCHAR(10),
  department_id INT
);

#2.  显示表my_employees的结构
DESC my_employees;

#3.  向my_employees表中插入下列数据
ID  FIRST_NAME  LAST_NAME  USERID    SALARY
1     patel       Ralph    Rpatel      895
2     Dancs       Betty    Bdancs      860
3      Biri        Ben      Bbiri     1100
4     Newman       Chad    Cnewman     750
5    Ropeburn     Audrey   Aropebur   1550

#方式一:
INSERT INTO my_employees
VALUES(1,'patel','Ralph','Rpatel',895),
(2,'Dancs','Betty','Bdancs',860),
(3,'Biri','Ben','Bbiri',1100),
(4,'Newman','Chad','Cnewman',750),
(5,'Ropeburn','Audrey','Aropebur',1550);

#方式二:
INSERT INTO my_employees
SELECT 1,'patel','Ralph','Rpatel',895 UNION
SELECT 2,'Dancs','Betty','Bdancs',860 UNION
SELECT 3,'Biri','Ben','Bbiri',1100 UNION
SELECT 4,'Newman','Chad','Cnewman',750 UNION
SELECT 5,'Ropeburn','Audrey','Aropebur',1550;

#4.   向users表中插入数据
1  Rpatel  10
2  Bdancs  10
3  Bbiri  20
4  Cnewman  30
5  Aropebur  40

INSERT INTO users
VALUES(1,'Rpatel',10),
(2,'Bdancs',10),
(3,'Bbiri',20);

#5.将3号员工的last_name修改为“drelxer”
UPDATE my_employees SET last_name='drelxer' WHERE id = 3;

#6.将所有工资少于900的员工的工资修改为1000
UPDATE my_employees SET salary=1000 WHERE salary<900;

#7.将userid 为Bbiri的user表和my_employees表的记录全部删除
DELETE u,e
FROM users u
JOIN my_employees e ON u.userid=e.Userid
WHERE u.userid='Bbiri';

#8.删除所有数据
DELETE FROM my_employees;
DELETE FROM users;

#9.检查所作的修正
SELECT * FROM my_employees;
SELECT * FROM users;
#10.清空表my_employees
TRUNCATE TABLE my_employees;

四、DCL语言(数据控制语言)

DCL英文全称是Data Control Language(数据控制语言),用来管理数据库用户、控制数据库的访问权限。

一、管理用户
1.查询用户
select * from mysql.user;use mysql;
select * from user;

#Host代表当前用户访问的主机, 如果为localhost, 仅代表只能够在当前本机访问,是不可以远程访问的。 User代表的是访问该数据库的用户名。在MySQL中需要通过Host和User来唯一标识一个用户。

2.创建用户
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码'; 

3.修改用户密码
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码'; 

4.删除用户
DROP USER '用户名'@'主机名'; 

注意事项:
• 在MySQL中需要通过用户名@主机名的方式,来唯一标识一个用户。
• 主机名可以使用 % 通配。
• 这类SQL开发人员操作的比较少,主要是DBA( Database Administrator 数据库管理员)使用。

二、权限管理
MySQL中定义了很多种权限,但是常用的就以下几种:
|       权限        |      说明       |
|ALL, ALL PRIVILEGES|    所有权限     |
|       SELECT      |    查询数据     |
|       INSERT      |    插入数据     |
|       UPDATE      |    修改数据     |
|       DELETE      |    删除数据     |
|       ALTER       |     修改表      |
|        DROP       |删除数据库//视图|
|       CREATE      |  创建数据库/|

上述只是简单罗列了常见的几种权限描述,其他权限描述及含义,可以直接参考:
(https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html)

1.查询权限
SHOW GRANTS FOR '用户名'@'主机名' ;

2.授予权限
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名'; 

3.撤销权限
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名'; 

注意事项:
• 多个权限之间,使用逗号分隔。
• 授权时, 数据库名和表名可以使用 * 进行通配,代表所有。
案例:
一、管理用户
案例:
1. 创建用户itcast, 只能够在当前主机localhost访问, 密码123456;
create user 'itcast'@'localhost' identified by '123456';

2. 创建用户heima, 可以在任意主机访问该数据库, 密码123456;
create user 'heima'@'%' identified by '123456'; 

3. 修改用户heima的访问密码为1234;
alter user 'heima'@'%' identified with mysql_native_password by '1234'; 

4. 删除 itcast@localhost 用户
drop user 'itcast'@'localhost'; 

#记得在root用户下操作。

二、权限管理
1. 查询 'heima'@'%' 用户的权限
show grants for 'heima'@'%'; 

2.授予 'heima'@'%' 用户itcast数据库所有表的所有操作权限
grant all on itcast.* to 'heima'@'%';

3.撤销 'heima'@'%' 用户的itcast数据库的所有权限
revoke all on itcast.* from 'heima'@'%'; 

#记得在root用户下操作。

五、DQL语言(数据查询语言)

5.1 基础查询

1、查询单个字段
select 字段名 from 表名;

2、查询多个字段
select 字段名,字段名 from 表名;

3、查询所有字段
select * from 表名;

4、查询常量
注意:字符型和日期型的常量值必须用单引号引起来,数值型不需要
select 常量值;

5、查询函数
select 函数名(实参列表);

6、查询表达式
select 100/1234;

7、起别名
/*
①便于理解
②如果要查询的字段有重名的情况,使用别名可以区分开来
*/as
select 字段名 as 别名 from 表名;

exp:
SELECT 100%98 AS 结果;
SELECT last_name AS,first_name ASFROM employees;

② 空格
select 字段名 别名 from 表名;

#案例:查询salary,显示结果为 out put
SELECT salary AS "out put" FROM employees;

8、去重
select distinct 字段名 from 表名;
只能去重单个字段,无法去重两个字段。

#案例:查询员工表中涉及到的所有的部门编号
SELECT DISTINCT department_id FROM employees;

false exp:

select distinct 字段名1,字段名2 from 表名;#去重了第一个字段,但是第二个字段没有去重,无法达到想要的效果。

select distinct 字段名1distinct 字段名2 from 表名;#直接报错。

9+
作用:做数值加法运算
select 数值+数值;  # 直接运算
select 字符+数值;  # 先试图将字符转换成数值,如果转换成功,则继续运算;否则转换成0,再做运算
select null+;  # 结果都为null

10、【补充】concat函数
功能:拼接字符
select concat(字符1,字符2,字符3,...);
字符可以加单引号

11、【补充】ifnull函数
功能:判断某字段或表达式是否为null,如果为null 返回指定的值,否则返回原本的值
select ifnull(commission_pct,0) from employees;

12、【补充】isnull函数
功能:判断某字段或表达式是否为null,如果是,则返回1,否则返回0

案例:
#1.  下面的语句是否可以执行成功  
SELECT last_name, job_id, salary AS sal
FROM employees; 

#2.下面的语句是否可以执行成功  
SELECT * FROM employees; 

#3.找出下面语句中的错误 
SELECT employee_id, last_name,
salary * 12 AS "ANNUAL SALARY"
FROM employees;

#4.显示表departments的结构,并查询其中的全部数据
DESC departments;
SELECT * FROM departments;

#5.显示出表employees中的全部job_id(不能重复)
SELECT DISTINCT job_id FROM employees;

#6.显示出表employees的全部列,各个列之间用逗号连接,列头显示成OUT_PUT
SELECT CONCAT(first_name,',',last_name,',',job_id,',',IFNULL(commission_pct,0)) AS out_put
FROM employees;

5.2 基础查询

一、条件查询的基本结构
select 查询列表 from 表名 where 筛选条件
#由于执行顺序的问题,select中对列起的别名不能在where中,即使用where后面不支持字段的别名

二、分类:
1、按条件表达式筛选
简单条件运算符:> < = != <> >= <=

2、按逻辑表达式筛选
逻辑运算符:&&,||,!,and,or,not
作用:用于连接条件表达式

&&and:两个条件都为true,结果为true,反之为false
||or: 只要有一个条件为true,结果为true,反之为false
!not: 如果连接的条件本身为false,结果为true,反之为false

3、模糊查询
(1)(not) between 数值 and 数值

①使用between and 可以提高语句的简洁度
②包含临界值
③两个数值不要调换顺序
     
select 查询列表 from 表名 where 列表值 between 100 and 120;# 等价于 >= 100 && <= 120 

(2)(not) in ('列表值', '列表值') 
含义:判断某字段的值是否属于in列表中的某一项
①使用in提高语句简洁度
②in列表的值类型必须一致或兼容(数值或者字符)
③in列表中不支持通配符

select 查询列表 from 表名 where 列表值 in('字符名','字符名',···);#查询所有包含in( )里字符名的结果

(3)is null/is not null 
#用于判断null值, =不能判断null
select 查询列表 from 表名 where 列表值 is null;  #查询结果为null的值
false exp:
select 查询列表 from 表名 where 列表值 = null; # = 不能判断null

(4)like  
# 一般搭配通配符使用,可以判断字符型或数值型 
select 查询列表 from 表名 where 列表值 like '%e%';  #筛选包含e的值 

三、通配符:
%:任意多个字符,包括0个
_:任意单个字符

通配符转义 \ 或者 '_任意字母或符号与查询的字符%' ESCAPE '任意字母或符号'
select 查询列表 from 表名 where 列表值 like '_\_%'; #查询包含_的值
select 查询列表 from 表名 where 列表值 like '_$_%' ESCAPE '$';  #查询包含_的值

四、安全等于<=>
|       |普通类型的数值 |   null| 可读性 |
|is null|      ×       |||
|  <=>  |||   ×    |


IS NULL:仅仅可以判断NULL值,可读性较高,建议使用
<=> :既可以判断NULL值,又可以判断普通的数值,可读性较低
案例:
#一、按条件表达式筛选
#案例1:查询工资>12000的员工信息
SELECT *
FROM employees
WHERE salary>12000;

#案例2:查询部门编号不等于90号的员工名和部门编号
SELECT last_name,department_id
FROM employees
WHERE department_id<>90;

#二、按逻辑表达式筛选
#案例1:查询工资z在10000到20000之间的员工名、工资以及奖金
SELECT last_name,salary,commission_pct
FROM employees
WHERE salary>=10000 AND salary<=20000;

#案例2:查询部门编号不是在90到110之间,或者工资高于15000的员工信息
SELECT *
FROM employees
WHERE NOT(department_id>=90 AND  department_id<=110) OR salary>15000;

#三、模糊查询
#1.like
#案例1:查询员工名中包含字符a的员工信息
select *
from employees
where last_name like '%a%';

#案例2:查询员工名中第三个字符为e,第五个字符为a的员工名和工资
select last_name,salary
FROM employees
WHERE last_name LIKE '__e_a%';

#案例3:查询员工名中第二个字符为_的员工名
SELECT last_name
FROM employees
WHERE last_name LIKE '_\_%';

#2.between and
#案例1:查询员工编号在100到120之间的员工信息
SELECT *
FROM employees
WHERE employee_id >= 120 AND employee_id<=100;
#----------------------
SELECT * 
FROM employees
WHERE employee_id BETWEEN 120 AND 100;

#3.in
#案例:查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号
SELECT last_name,job_id
FROM employees
WHERE job_id = 'IT_PROT' OR job_id = 'AD_VP' OR job_id  ='AD_PRES';
#------------------
SELECT last_name,job_id
FROM employees
WHERE job_id IN( 'IT_PROT' ,'AD_VP','AD_PRES');

#4、is null
#案例1:查询没有奖金的员工名和奖金率
SELECT last_name,commission_pct
FROM employees
WHERE commission_pct IS NULL;

#案例2:查询有奖金的员工名和奖金率
SELECT last_name,commission_pct
FROM employees
WHERE commission_pct IS NOT NULL;
#----------以下为是错误示范
SELECT last_name,commission_pct
FROM employees
WHERE salary IS 12000;

安全等于  <=>
#案例1:查询没有奖金的员工名和奖金率
SELECT last_name,commission_pct
FROM employees
WHERE commission_pct <=>NULL;

#案例2:查询工资为12000的员工信息
SELECT last_name,salary
FROM employees
WHERE salary <=> 12000;

5.3 排序查询

一、排序查询的基本结构
select 查询列表
from 表
【where 筛选条件】
order by 排序列表 【asc/desc】;

二、特点

1. asc(ascend):升序,如果不写则默认升序
   desc(descend):降序
2. order by子句支持单个字段、别名、表达式、函数、多个字段
3. order by的位置一般放在查询语句的最后(除limit语句之外)

#1、按单个字段排序
SELECT  FROM employees ORDER BY salary DESC;

#2、添加筛选条件再排序
#案例:查询部门编号>=90的员工信息,并按员工编号降序*
SELECT 
FROM employees
WHERE department_id>=90
ORDER BY employee_id DESC;

#3、按表达式排序
#案例:查询员工信息 按年薪降序
SELECT salary*12*(1+IFNULL(commission_pct,0))
FROM employees
ORDER BY salary12(1+IFNULL(commission_pct,0)) DESC;

#4、按别名排序
#案例:查询员工信息 按年薪升序
SELECT salary12*(1+IFNULL(commission_pct,0)) 年薪
FROM employees
ORDER BY 年薪 ASC;

#5、按函数排序
#案例:查询员工名,并且按名字的长度降序
SELECT LENGTH('last_name'),last_name
FROM employees
ORDER BY LENGTH('last_name') DESC;

#6、按多个字段排序
#案例:查询员工信息,要求先按工资降序,再按employee_id升序
SELECT *
FROM employees
ORDER BY salary DESC,employee_id ASC;
案例:
#1.查询员工的姓名和部门号和年薪,按年薪降序 按姓名升序
SELECT last_name,department_id,salary*12*(1+IFNULL(commission_pct,0)) 年薪
FROM employees
ORDER BY 年薪 DESC,last_name ASC;

#2.选择工资不在8000到17000的员工的姓名和工资,按工资降序
SELECT last_name,salary
FROM employees
WHERE salary NOT BETWEEN 8000 AND 17000
ORDER BY salary DESC;

#3.查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序
SELECT *,LENGTH(email)
FROM employees
WHERE email LIKE '%e%'
ORDER BY LENGTH(email) DESC,department_id ASC;

5.4 常见函数

概念:类似于java的方法,将一组逻辑语句封装在方法体中,对外暴露方法名
好处:1、隐藏了实现细节  2、提高代码的重用性
调用:select 函数名(实参列表)from 表】;   #函数的实参不能用别名

分类:
(一)单行函数
一.字符函数
1.length 
#获取参数值的字节个数(utf-8一个汉字代表3个字节,jbk为2个字节)
SELECT LENGTH('john');
SELECT LENGTH('张三丰hahaha');#一个汉字占3个字节(是一个字符)
SHOW VARIABLES LIKE '%char%'

2.concat 
#拼接字符串
SELECT CONCAT(last_name,'_',first_name) 姓名 FROM employees;

3.upper、lower
SELECT UPPER('john');
SELECT LOWER('joHn');

#示例:将姓变大写,名变小写,然后拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name))  姓名 FROM employees;

4.substr、substring
#注意:索引从1开始
#截取从指定索引处后面所有字符
SELECT SUBSTR('李莫愁爱上了陆展元',7)  out_put;

#截取从指定索引处指定字符长度的字符
SELECT SUBSTR('李莫愁爱上了陆展元',1,3) out_put;

#案例:姓名中首字符大写,其他字符小写然后用_拼接,显示出来
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2)))  out_put
FROM employees;

5.instr 
#返回子串第一次出现的索引,如果找不到返回0
SELECT INSTR('杨不殷六侠悔爱上了殷六侠','殷八侠') AS out_put;

6.trim
#去除字符前后的空格
SELECT LENGTH(TRIM('    张翠山    ')) AS out_put;

#去除指定的字符,无法去除原有字符之间的(即无法修改),只能去除前后。
SELECT TRIM('a' FROM 'aaaaaaaaa张aaaaaaaaaaaa翠山aaaaaaaaaaaaaaaaaaa')  AS out_put;
#上方语句会输出  张aaaaaaaaaaaa翠山

7.lpad 
#用指定的字符实现左填充指定长度
SELECT LPAD('殷素素',10,'*') AS out_put;
#上方语句会输出   *******殷素素

#输出长度必定为括号中指定的填充长度,原字符串若大于指定填充长度,则会从右边截断到指定长度
SELECT LPAD('殷素素',2,'*') AS out_put;
#上方语句会输出   殷素

8.rpad 
#用指定的字符实现右填充指定长度
SELECT RPAD('殷素素',10,'ab') AS out_put;
#上方语句会输出   殷素素abababa

#输出长度必定为括号中指定的填充长度,原字符串若大于指定填充长度,则会从右边截断到指定长度
SELECT RPAD('殷素素',2,'ab') AS out_put;
#上方语句会输出:殷素

9.replace 
#替换字符
SELECT REPLACE('张无忌爱上了周芷若','周芷若','赵敏') AS out_put;
#上方语句会输出:张无忌爱上了赵敏

#replace会替换所有符合条件的字符
SELECT REPLACE('周芷若周芷若周芷若周芷若张无忌爱上了周芷若','周芷若','赵敏') AS out_put;
#上方语句会输出:赵敏赵敏赵敏赵敏张无忌爱上了赵敏

#replace从左边开始替换
SELECT REPLACE('aaaaaaa','aa','b') AS out_put;
#上方语句会输出:bbba

10.stuff函数
stuff函数用于删除指定长度的字符,并可以在制定的起点处插入另一组字符。sql stuff函数中如果开始位置或长度值是负数,或者如果开始位置大于第一个字符串的长度,将返回空字符串。如果要删除的长度大于第一个字符串的长度,将删除到第一个字符串中的第一个字符。

①作用
删除指定长度的字符,并在指定的起点处插入另一组字符。

②语法
STUFF(character_expression,start,length,character_expression)

参数
character_expression:
一个字符数据表达式。character_expression 可以是常量、变量,也可以是字符列或二进制数据列。

start:
一个整数值,指定删除和插入的开始位置。如果 start 或 length 为负,则返回空字符串。如果 start 比第一个 character_expression 长,则返回空字符串。start 可以是 bigint 类型。

length:
一个整数,指定要删除的字符数。如果 length 比第一个 character_expression 长,则最多删除到最后一个 character_expression 中的最后一个字符。length 可以是 bigint 类型。

返回类型
如果 character_expression 是受支持的字符数据类型,则返回字符数据。如果 character_expression 是一个受支持的 binary 数据类型,则返回二进制数据。

③备注
1、如果开始位置或长度值是负数,或者如果开始位置大于第一个字符串的长度,将返回空字符串。如果要删除的长度大于第一个字符串的长度,将删除到第一个字符串中的第一个字符。
2、如果结果值大于返回类型支持的最大值,则产生错误。

④案例
实例1
select STUFF('abcdefg',1,0,'1234')       --结果为'1234abcdefg'
select STUFF('abcdefg',1,1,'1234')       --结果为'1234bcdefg'
select STUFF('abcdefg',2,1,'1234')       --结果为'a1234cdefg'
select STUFF('abcdefg',2,2,'1234')       --结果为'a1234defg'
#相当于从第几个字符开始,替换掉几个字符成后面的

实例2
#将列转成字符串并用逗号分隔
SELECT STUFF((SELECT ','+FieldName FROM TableName FOR XML PATH('')),1,1,'') AS T;

二、数字函数
1.round 
#四舍五入
SELECT ROUND(-1.55);
#输出  -2

#小数点后保留两位
SELECT ROUND(1.567,2);
#输出 1.57

2.ceil 向上取整,返回>=该参数的最小整数
SELECT CEIL(-1.02);
#输出  -1

3.floor 
#向下取整,返回<=该参数的最大整数
SELECT FLOOR(-9.99);
#输出   -10

4.truncate 
#从指定位置的小数开始截断,即保留指定位的小数
SELECT TRUNCATE(1.69999,1);
#输出  1.6

5.mod取余
#被除数为正,则结果为正,反之为负。
mod(a,b)等效于 a-a/b*b
#mod(-10,-3)等效于-10- (-10)/(-3)*(-3)=-1

SELECT MOD(10,3);
#输出   1

SELECT 10%3;
#输出   1

SELECT MOD(-10,3);
#输出   -1

SELECT MOD(-10,-3);
#输出   -1

6.rand
#获取随机数,返回0-1之间的小数
SELECT RAND();

#当给rand一个参数的时候,会将该参数作为一个随机种子,生成一个介于0-1之间的一个数
SELECT RAND(1),RAND(1),RAND(2);
#输出0.713591993212924    0.713591993212924    0.713610626184182
#第一个与第二个随机数一致

#使用order by rand()会对结果进行随机排序
- SELECT * FROM student ORDER BY RAND();
#输出:
  class    name   
    5    Zara  
    3    Jack  
    3    Jack  
    2    Ram  
    4    Jill  
    5    Zara  
    1    John
    
再执行一次
SELECT * FROM student ORDER BY RAND();
#输出
  class       name  
     5        Zara  
     2        Ram  
     3        Jack  
     1        John  
     4        Jill  
     3        Jack  
     5        Zara
#顺序跟之前不一样了  

7.cast
#用于将某种数据类型的表达式显式转换为另一种数据类型。
CAST (expression AS data_type);

expression:任何有效的SQServer表达式。  
AS:用于分隔两个参数,在AS之前的是要处理的数据,在AS之后是要转换的数据类型。  
data_type:目标系统所提供的数据类型,包括bigint和sql_variant,不能使用用户定义的数据类型。

#可以转换的类型是有限制的。这个类型可以是以下值其中的一个:
二进制,同带binary前缀的效果 : BINARY
字符型,可带参数 : CHAR()
日期 : DATE
时间: TIME
日期时间型 : DATETIME
浮点数 : DECIMAL
整数 : SIGNED
无符号整数 : UNSIGNED

SELECT CAST('9.0' AS decimal);
#结果:9

SELECT NOW();
#结果:2017-11-27 10:43:22
SELECT  CAST(NOW() AS DATE);
#结果:2017-11-27

三、日期函数
1.now 返回当前系统日期+时间
SELECT NOW();

2.curdate 返回当前系统日期,不包含时间
SELECT CURDATE();

3.curtime 返回当前时间,不包含日期
SELECT CURTIME();

4.获取指定的部分,年、月、日、小时、分钟、秒
SELECT YEAR(NOW());
SELECT YEAR('1998-1-1');
SELECT  YEAR(hiredate)FROM employees;
SELECT MONTH(NOW());
SELECT MONTHNAME(NOW()); #获取的月份显示为英文。
SELECT DAY(NOW()) 日期;
SELECT DAYNAME(NOW()) 星期几; #获取的星期几显示为英文。
SELECT HOUR(NOW()) 小时;
SELECT MINUTE(NOW()) 分钟;
SELECT SECOND(NOW()) 秒钟;

5.datediff
#获取两个日期相差的天数
SELECT DATEDIFF('日期1','日期2');
SELECT DATEDIFF('2022-02-28','2020-01-01');

6.DATE_ADD
#返回一个日期/时间值加上一个时间间隔后的时间值
SELECT DATE_ADD(date, INTERVAL 数字 计量数【如dayyear等】);  

7.str_to_date 
#通过匹配指定的日期字符串格式,将其转换成正常格式的日期
|序号|  格式符 |        功能        |
|  1 |   %Y   |      四位的年份     |
|  2 |   %y   |      2位的年份      |
|  3 |   %m   | 月份(01,0211,12|
|  4 |   %c   | 月份(1,2,11,12|
|  5 |   %d   |    日(01,02,…)    |
|  6 |   %H   |   小时(24小时制)  |
|  7 |   %h   |   小时(12小时制)  |
|  8 |   %i   |   分钟(00,0159|
|  9 |   %s   |    秒(00,01,59|

SELECT STR_TO_DATE('3-2-1999','%m-%d-%Y') AS out_put;
#结果:1999-03-02

#案例:查询入职日期为1992-4-3的员工信息
SELECT * FROM employees WHERE hiredate = '1992-4-3';
SELECT * FROM employees WHERE hiredate = STR_TO_DATE('4-3 1992','%c-%d %Y');

8.date_format 
#将日期转换成字符
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS out_put;
#结果:22年02月27日

#案例:查询有奖金的员工名和入职日期(xx月/xx日 xx年)
SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %y年') 入职日期
FROM employees
WHERE commission_pct IS NOT NULL;

四、其他函数
SELECT VERSION();  #查看当前mysql版本
SELECT DATABASE();  #查看当前数据库
SELECT USER();  #查看当前用户
SELECT PASSWORD('字符');   #返回该字符的密码形式 (8.0版本已弃用)
SELECT MD5('字符');  #返回该字符的md5加密形式

五、流程控制函数
1.if函数
#if else 的效果,类似于三元运算符
SELECT IF(条件,t,f); #如果value为true,则返回t,否则返回f

SELECT IF(10<5,'大','小');
#结果:小
SELECT last_name,commission_pct,IF(commission_pct IS NULL,'没奖金,呵呵','有奖金,嘻嘻') 备注
FROM employees;

2.ifnull函数
IFNULL(value1 , value2)
#如果value1不为空,返回value1,否则返回value2;

3.case函数的使用一: switch case 的效果
/*
java中
switch(变量或表达式){
  case 常量1:语句1;break;
  ...
  default:语句n;break;
}
*/

mysql中:
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1
when 常量2 then 要显示的值2或语句2
...
else 要显示的值n或语句n
end

/*案例:查询员工的工资,要求
部门号=30,显示的工资为1.1倍
部门号=40,显示的工资为1.2倍
部门号=50,显示的工资为1.3倍
其他部门,显示的工资为原工资
*/

SELECT salary 原始工资,department_id,
CASE department_id
WHEN 30 THEN salary*1.1
WHEN 40 THEN salary*1.2
WHEN 50 THEN salary*1.3
ELSE salary
END AS 新工资
FROM employees;

4.case 函数的使用二:类似于 多重if
/*
java中:
if(条件1){
  语句1;
}else if(条件2){
  语句2;
}
...
else{
  语句n;
}

mysql中:
case 
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2
···
else 要显示的值n或语句n
end
*/

#案例:查询员工的工资的情况
如果工资>20000,显示A级别
如果工资>15000,显示B级别
如果工资>10000,显示C级别
否则,显示D级别

SELECT salary,
CASE 
WHEN salary>20000 THEN 'A'
WHEN salary>15000 THEN 'B'
WHEN salary>10000 THEN 'C'
ELSE 'D'
END AS 工资级别
FROM employees;

(二)分组函数
功能:用作统计使用,又称为聚合函数或统计函数或组函数

分类:
sum 求和、avg 平均值、max 最大值 、min 最小值 、count 计算个数

特点:
1、sum、avg一般用于处理数值型
   max、min、count可以处理任何类型(数值和字符)
2、以上分组函数都忽略null3、可以和distinct搭配实现去重的运算
4、一般使用count(*)用作统计行数
5、和分组函数一同查询的字段要求是group by后的字段

#1、简单的使用
SELECT SUM(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT MIN(salary) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT COUNT(salary) FROM employees;
SELECT SUM(salary),AVG(salary) 平均,MAX(salary) 最高,MIN(salary) 最低,COUNT(salary) 个数 FROM employees;
SELECT SUM(salary),ROUND(AVG(salary),2) 平均,MAX(salary) 最高,MIN(salary) 最低,COUNT(salary) 个数 FROM employees;

#2、参数支持哪些类型
SELECT SUM(last_name) ,AVG(last_name) FROM employees;
SELECT SUM(hiredate) ,AVG(hiredate) FROM employees;
SELECT MAX(last_name),MIN(last_name) FROM employees;
SELECT MAX(hiredate),MIN(hiredate) FROM employees;
SELECT COUNT(commission_pct) FROM employees;
SELECT COUNT(last_name) FROM employees;

#3、是否忽略null
SELECT SUM(commission_pct) ,AVG(commission_pct),SUM(commission_pct)/35,SUM(commission_pct)/107 FROM employees;
SELECT MAX(commission_pct) ,MIN(commission_pct) FROM employees;
SELECT COUNT(commission_pct) FROM employees;
SELECT commission_pct FROM employees;

#4、和distinct搭配
SELECT SUM(DISTINCT salary),SUM(salary) FROM employees;
SELECT COUNT(DISTINCT salary),COUNT(salary) FROM employees;

#5、count函数的详细介绍
SELECT COUNT(salary) FROM employees;
SELECT COUNT(*) FROM employees;
SELECT COUNT(1) FROM employees;
SELECT COUNT('崔大侠') FROM employees;

效率:
MYISAM存储引擎下  ,COUNT(*)的效率高
INNODB存储引擎下,COUNT(*)COUNT(1)的效率差不多,比COUNT(字段)要高一些

#6、和分组函数一同查询的字段有限制
SELECT AVG(salary),employee_id  FROM employees;
案例:
(一)单行函数
#1.  显示系统时间(注:日期+时间)
SELECT NOW();

#2.  查询员工号,姓名,工资,以及工资提高百分之20%后的结果(new salary)
SELECT employee_id,last_name,salary,salary1.2 "new salary"
FROM employees;

#3.  将员工的姓名按首字母排序,并写出姓名的长度(length)
SELECT LENGTH(last_name) 长度,SUBSTR(last_name,1,1) 首字符,last_name
FROM employees
ORDER BY 首字符;

#4.  做一个查询,产生下面的结果
<last_name> earns <salary> monthly but wants <salary3>
Dream Salary
King earns 24000 monthly but wants 72000

SELECT CONCAT(last_name,' earns ',salary,' monthly but wants ',salary*3) AS "Dream Salary"
FROM employees
WHERE salary=24000;

(二)分组函数
#1.查询公司员工工资的最大值,最小值,平均值,总和
SELECT MAX(salary) 最大值,MIN(salary) 最小值,AVG(salary) 平均值,SUM(salary)FROM employees;

#2.查询员工表中的最大入职时间和最小入职时间的相差天数 (DIFFRENCE)
SELECT MAX(hiredate) 最大,MIN(hiredate) 最小,(MAX(hiredate)-MIN(hiredate))/1000/3600/24 DIFFRENCE
FROM employees;
SELECT DATEDIFF(MAX(hiredate),MIN(hiredate)) DIFFRENCE
FROM employees;
SELECT DATEDIFF('1995-2-7','1995-2-6');

#3.查询部门编号为90的员工个数
SELECT COUNT(*) FROM employees WHERE department_id = 90;

5.5 分组查询

语法:
select 分组函数,group by后出现的字段   #分组函数和字段可以交换,只是显示位置会换一下。
from 表
【where 筛选条件】
group by 分组的字段
【having 分组后的筛选】
【order by 排序的字段】;

特点:
1、和分组函数一同查询的字段必须是group by后出现的字段
2、筛选分为两类:分组前筛选和分组后筛选                                                         
|          |     针对的表     |   位置   |连接的关键字|
|分组前筛选|      原始表       |group by|   where   |
|分组后筛选| group by后的结果集|group by|  having   |

where是对分组前的每一行数据进行筛选,having是对分组后的每一行进行筛选,筛选条件是每一组的群体特性,比如一组的总和,平均值等等。where 过滤行,having过滤分组。
where的筛选条件在分组前进行执行,having的筛选条件在分组后执行。同时where不能使用分组函数,但是having可以使用分组函数。

问题1:分组函数做筛选能不能放在where后面
答:不能

问题2where——group by——having
一般来讲,能用分组前筛选的,尽量使用分组前筛选,提高效率
分组函数做条件肯定是放在having子句中。

3group by子句支持单个字段分组,也支持多个字段分组(多个字段之间用逗号隔开,没有顺序要求),也支持表达式和函数(用得较少)。
GROUP BY 字段1,字段2#字段1和字段2可交换,无影响
GROUP BY LENGTH(字段);

4、可以搭配着排序使用(排序放在整个分组查询的最后)。

#1.简单的分组
#案例1:查询每个工种的员工平均工资
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id;

#案例2:查询每个位置的部门个数
SELECT COUNT(*),location_id
FROM departments
GROUP BY location_id;

#2、可以实现分组前的筛选
#案例1:查询邮箱中包含a字符的 每个部门的最高工资
SELECT MAX(salary),department_id
FROM employees
WHERE email LIKE '%a%'
GROUP BY department_id;

#案例2:查询有奖金的每个领导手下员工的平均工资
SELECT AVG(salary),manager_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id;

#3、分组后筛选
#案例:查询哪个部门的员工个数>5
#①查询每个部门的员工个数
SELECT COUNT(*),department_id
FROM employees
GROUP BY department_id;

#② 筛选刚才①结果
SELECT COUNT(*),department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*)>5;

#案例2:每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
SELECT job_id,MAX(salary)
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary)>12000;

#案例3:领导编号>102的每个领导手下的最低工资大于5000的领导编号和最低工资
SELECT manager_id,MIN(salary)
FROM employees
WHERE manager_id>102
GROUP BY manager_id
HAVING MIN(salary)>5000;

#4.添加排序
#案例:每个工种有奖金的员工的最高工资>6000的工种编号和最高工资,按最高工资升序
SELECT job_id,MAX(salary) m
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING m>6000
ORDER BY m ;

#5.按多个字段分组
#案例:查询每个工种每个部门的最低工资,并按最低工资降序
SELECT MIN(salary),job_id,department_id
FROM employees
GROUP BY department_id,job_id
ORDER BY MIN(salary) DESC;

#6.GROUP BY后的分组越多,表格数据越多。
案例:
#1.查询各job_id的员工工资的最大值,最小值,平均值,总和,并按job_id升序
SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary),job_id
FROM employees
GROUP BY job_id
ORDER BY job_id;

#2.查询员工最高工资和最低工资的差距(DIFFERENCE)
SELECT MAX(salary)-MIN(salary) DIFFRENCE
FROM employees;

#3.查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT MIN(salary),manager_id
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING MIN(salary)>=6000;

#4.查询所有部门的编号,员工数量和工资平均值,并按平均工资降序
SELECT department_id,COUNT(*),AVG(salary) a
FROM employees
GROUP BY department_id
ORDER BY a DESC;

#5.选择具有各个job_id的员工人数
SELECT COUNT(*) 个数,job_id
FROM employees
GROUP BY job_id;

5.6 连接查询(多表查询)

含义:
又称多表查询,当查询的字段来自于多个表时,就会用到连接查询

笛卡尔乘积现象:
表1有m行,表2有n行,结果=m*n行
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件

分类:
按年代分类:
sql92标准:仅仅支持内连接
sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接

#功能:sql99支持的较多
#可读性:sql99实现连接条件和筛选条件的分离,可读性较高

按功能分类:
  1.内连接:等值连接,非等值连接,自连接
  2.外连接:左外连接,右外连接,全外连接
  3.交叉连接

SELECT * FROM beauty;
SELECT * FROM boys;
SELECT NAME,boyName FROM boys,beauty
WHERE beauty.boyfriend_id= [boys.id](http://boys.id);

5.6.1 SQL92标准

一、等值连接
1.特点:
① 多表等值连接的结果为多表的交集部分
②n表连接,至少需要n-1个连接条件
③ 多表的顺序没有要求
④一般需要为表起别名
⑤可以搭配前面介绍的所有子句使用,比如排序、分组、筛选

#案例1:查询女神名和对应的男神名
SELECT NAME,boyName 
FROM boys,beauty
WHERE beauty.boyfriend_id= boys.id;

#案例2:查询员工名和对应的部门名
SELECT last_name,department_name
FROM employees,departments
WHERE employees.department_id=departments.department_id;

2.为表起别名
/*
①提高语句的简洁度
②区分多个重名的字段
注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定
*/

#查询员工名、工种号、工种名
SELECT e.last_name,e.job_id,j.job_title
FROM employees  e,jobs j
WHERE e.job_id=j.job_id;

3.两个表的顺序可以调换
#案例:查询员工名、工种号、工种名
SELECT e.last_name,e.job_id,j.job_title
FROM jobs j,employees e     #此处表的位置可以互换。
WHERE e.job_id=j.job_id;

4.可以加筛选
#案例:查询有奖金的员工名、部门名
SELECT last_name,department_name,commission_pct
FROM employees e,departments d
WHERE e.department_id=d.department_id
AND e.commission_pct IS NOT NULL;

#案例2:查询城市名中第二个字符为o的部门名和城市名
SELECT department_name,city
FROM departments d,locations l
WHERE d.location_id = l.location_id
AND city LIKE '_o%';

#5.可以加分组
#案例1:查询每个城市的部门个数
SELECT COUNT(*) 个数,city
FROM departments d,locations l
WHERE d.location_id=l.location_id
GROUP BY city;

#案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
SELECT department_name,d.manager_id,MIN(salary)
FROM departments d,employees e
WHERE d.department_id=e.department_id
AND commission_pct IS NOT NULL
GROUP BY department_name,d.manager_id;

6.可以加排序
#案例:查询每个工种的工种名和员工的个数,并且按员工个数降序
SELECT job_title,COUNT(*)
FROM employees e,jobs j
WHERE e.job_id=j.job_id
GROUP BY job_title
ORDER BY COUNT(*) DESC;

7.可以实现三表连接?
#案例:查询员工名、部门名和所在的城市
SELECT last_name,department_name,city
FROM employees e,departments d,locations l
WHERE e.department_id=d.department_id
AND d.location_id=l.location_id
AND city LIKE 's%'
ORDER BY department_name DESC;

二、非等值连接
#案例1:查询员工的工资和工资级别
SELECT salary,grade_level
FROM employees e,job_grades g
WHERE salary BETWEEN g.lowest_sal AND g.highest_sal
AND g.grade_level='A';

三、自连接
#案例:查询 员工名和上级的名称
SELECT e.employee_id,e.last_name,m.employee_id,m.last_name
FROM employees e,employees m
WHERE e.manager_id=m.employee_id;

5.6.2 SQL99标准(推荐)

(一)内连接
语法:
select 查询列表
from1 别名
【innerjoin2 别名
on 连接条件;

#三个或三个以上的表同时查询:
SELECT 字段1,字段2,···
FROM1
JOIN2 ON 连接条件1
JOIN3 ON 连接条件2
···;

或:
SELECT 字段1,字段2,···
FROM1
JOIN2 
JOIN3 
ON 连接条件1
AND 连接条件2
···;

#推荐第一种,较为精确。第二种格式不标准,虽然能运行。

分类:
等值连接
非等值连接
自连接

特点:
①可以添加排序、分组、筛选。
②inner可以省略,只有join则默认为inner join,即内连接。
③筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读。
④inner join连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集。
⑤三个或三个以上的表同时查询时,表的顺序会有影响,执行过程是第一个表连接第二个表,然后形成的新表连接第三个表,以此类推,但若前面的表没有连接条件(比如两个表没有相关的字段可以连接)时,就会连接失败,出错。即必须有可连接项,因此要考虑顺序。
⑥n表连接至少要n-1个连接条件。
⑦内连接的结果是多表的交集。

#一、等值连接
#案例1.查询员工名、部门名
SELECT last_name,department_name
FROM departments d
JOIN  employees e
ON e.department_id = d.department_id;

#案例2.查询名字中包含e的员工名和工种名(添加筛选)
SELECT last_name,job_title
FROM employees e
INNER JOIN jobs j
ON e.job_id=  j.job_id
WHERE e.last_name LIKE '%e%';

#案例3. 查询部门个数>3的城市名和部门个数,(添加分组+筛选)
SELECT city,COUNT(*) 部门个数
FROM departments d
INNER JOIN locations l
ON d.location_id=l.location_id
GROUP BY city
HAVING COUNT(*)>3;

#案例4.查询哪个部门的员工个数>3的部门名和员工个数,并按个数降序(添加排序)
#①查询每个部门的员工个数
SELECT COUNT(*),department_name
FROM employees e
INNER JOIN departments d
ON e.department_id=d.department_id
GROUP BY department_name

#② 在①结果上筛选员工个数>3的记录,并排序
SELECT COUNT(*) 个数,department_name
FROM employees e
INNER JOIN departments d
ON e.department_id=d.department_id
GROUP BY department_name
HAVING COUNT(*)>3
ORDER BY COUNT(*) DESC;

#案例5.查询员工名、部门名、工种名,并按部门名降序(添加三表连接)
SELECT last_name,department_name,job_title
FROM employees e
INNER JOIN departments d ON e.department_id=d.department_id
INNER JOIN jobs j ON e.job_id = j.job_id
ORDER BY department_name DESC;

#二)非等值连接
#查询员工的工资级别
SELECT salary,grade_level
FROM employees e
JOIN job_grades g
ON e.salary BETWEEN g.lowest_sal AND g.highest_sal;

#查询工资级别的个数>20的个数,并且按工资级别降序
SELECT COUNT(*),grade_level
FROM employees e
JOIN job_grades g
ON e.salary BETWEEN g.lowest_sal AND g.highest_sal
GROUP BY grade_level
HAVING COUNT(*)>20
ORDER BY grade_level DESC;

#三)自连接
#查询员工的名字、上级的名字
SELECT e.last_name,m.last_name
FROM employees e
JOIN employees m
ON e.manager_id= m.employee_id;

#查询姓名中包含字符k的员工的名字、上级的名字
SELECT e.last_name,m.last_name
FROM employees e
JOIN employees m
ON e.manager_id= m.employee_id
WHERE e.last_name LIKE '%k%';

(二)外连接
应用场景:用于查询一个表中有,另一个表没有的记录

语法:
select 查询列表
from1 别名
left|right|fullouterjoin2 别名 
on 连接条件    
where 筛选条件    
group by 分组列表    
having 分组后的筛选    
order by 排序列表    
limit 子句;

特点:
1、外连接的查询结果为主表中的所有记录。
   如果从表中有和它匹配的,则显示匹配的值。
   如果从表中没有和它匹配的,则显示null。
   外连接查询结果=内连接结果+主表中有而从表没有的记录。
   外连接=内连接+主表中未满足连接条件的null项。
2、左外连接,left join左边的是主表。
   右外连接,right join右边的是主表。
3、左外和右外交换两个表的顺序,可以实现同样的效果 。
4、全外连接=内连接的结果+1中有但表2没有的+2中有但表1没有的。
5、一般用于查询除了交集部分的剩余的不匹配的行。
6、实际工作中尽量都用左外连接。

#左外连接
SELECT b.name,bo.*
FROM boys bo
LEFT OUTER JOIN beauty b
ON b.boyfriend_id = bo.id
WHERE b.id IS NULL;

#案例1:查询哪个部门没有员工
#左外
SELECT d.*,e.employee_id
FROM departments d
LEFT OUTER JOIN employees e
ON d.department_id = e.department_id
WHERE e.employee_id IS NULL;

#右外
SELECT d.*,e.employee_id
FROM employees e
RIGHT OUTER JOIN departments d
ON d.department_id = e.department_id
WHERE e.employee_id IS NULL;

#全外(mysql不支持)
USE girls;
SELECT b.*,bo.*
FROM beauty b
FULL OUTER JOIN boys bo
ON b.boyfriend_id = bo.id;

(三)交叉连接
#交叉连接相当于92语法的笛卡尔乘积
SELECT b.,bo.
FROM beauty b
CROSS JOIN boys bo;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

案例:
USE myemployees;
#1.显示所有员工的姓名,部门号和部门名称。
SELECT last_name,d.department_id,department_name
FROM employees e,departments d
WHERE e.department_id = d.department_id;

#2.查询90号部门员工的job_id和90号部门的location_id
SELECT job_id,location_id
FROM employees e,departments d
WHERE e.department_id=d.department_id
AND e.department_id=90;

#3.  选择所有有奖金的员工的last_name , department_name , location_id , city
SELECT last_name , department_name , l.location_id , city
FROM employees e,departments d,locations l
WHERE e.department_id = d.department_id
AND d.location_id=l.location_id
AND e.commission_pct IS NOT NULL;

#4.选择city在Toronto工作的员工的last_name , job_id , department_id , department_name
SELECT last_name , job_id , d.department_id , department_name
FROM employees e,departments d ,locations l
WHERE e.department_id = d.department_id
AND d.location_id=l.location_id
AND city = 'Toronto';

#5.查询每个工种、每个部门的部门名、工种名和最低工资
SELECT department_name,job_title,MIN(salary) 最低工资
FROM employees e,departments d,jobs j
WHERE e.department_id=d.department_id
AND e.job_id=j.job_id
GROUP BY department_name,job_title;

#6.查询每个国家下的部门个数大于2的国家编号
SELECT country_id,COUNT(*) 部门个数
FROM departments d,locations l
WHERE d.location_id=l.location_id
GROUP BY country_id
HAVING 部门个数>2;

#7、选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
employees  Emp#  manager  Mgr#
kochhar    101  king  100

SELECT e.last_name employees,e.employee_id "Emp#",m.last_name manager,m.employee_id "Mgr#"
FROM employees e,employees m
WHERE e.manager_id = m.employee_id
AND e.last_name='kochhar';
案例:
#一、查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有,用null填充
SELECT b.id,b.name,bo.*
FROM beauty b
LEFT OUTER JOIN boys bo
ON b.boyfriend_id = bo.id
WHERE b.id>3;

#二、查询哪个城市没有部门
SELECT city
FROM departments d
RIGHT OUTER JOIN locations l
ON d.location_id=l.location_id
WHERE  d.department_id IS NULL;

#三、查询部门名为SAL或IT的员工信息
SELECT e.*,d.department_name,d.department_id
FROM departments  d
LEFT JOIN employees e
ON d.department_id = e.department_id
WHERE d.department_name IN('SAL','IT');

5.7 子查询

含义:
出现在其他语句(可以是insertupdatedeleteselect等)中的select语句,称为子查询或内查询。
外部的查询语句,称为主查询或外查询。
主查询需要从子查询的结果集中获取数据的查询叫做子查询。

分类:
按子查询出现的位置:
 1.select后面:
   仅仅支持标量子查询

 2.from后面:
   支持表子查询

 3.wherehaving后面:★
   标量子查询(单行) √
   列子查询  (多行) √
   行子查询(较少用)

4.exists后面(相关子查询)
   表子查询

按结果集的行列数不同:
  标量子查询(结果集只有一行一列)
  列子查询(结果集只有一列多行)
  行子查询(结果集有一行多列或者多行多列)
  表子查询(结果集一般为多行多列)

5.7.1 where或having后的子查询

1、标量子查询(单行子查询)
2、列子查询(多行子查询)
3、行子查询(多列多行)

特点:
①子查询放在小括号内
②子查询一般放在条件的右侧
③标量子查询,一般搭配着单行操作符使用: = <= = <>
 列子查询,一般搭配着多行操作符使用: inany/someall
 
|  操作符 |           含义              |
|IN/NOT IN|     等于列表中的任意一个     |
| ANY|SOME|  和子查询返回的某一个值比较  |
|    ALL  |   和子查询返回的所有值比较   |

④子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果

#1.标量子查询★
#案例1:谁的工资比 Abel 高?
SELECT *
FROM employees
WHERE salary>(
  SELECT salary
  FROM employees
  WHERE last_name = 'Abel'
);

#案例2:返回job_id与141号员工相同,salary比143号员工多的员工 姓名,job_id 和工资
SELECT last_name,job_id,salary
FROM employees
WHERE job_id = (
  SELECT job_id
  FROM employees
  WHERE employee_id = 141
) AND salary>(
  SELECT salary
  FROM employees
  WHERE employee_id = 143
);

#案例3:返回公司工资最少的员工的last_name,job_id和salary
SELECT last_name,job_id,salary
FROM employees
WHERE salary=(
  SELECT MIN(salary)
  FROM employees
);

#案例4:查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
  SELECT  MIN(salary)
  FROM employees
  WHERE department_id = 50
);

#用连接查询:
select e.department_id,min(e.salary)  
from employees e  
join employees m  
where m.department_id=50  
group by e.department_id  
having min(e.salary)>min(m.salary);

#非法使用标量子查询
SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
  SELECT  salary   #结果多行,不符合运算要求。
  FROM employees
  WHERE department_id = 250  #查询结果为空。
);

#2.列子查询(多行子查询)★
#案例1:返回location_id是1400或1700的部门中的所有员工姓名
SELECT last_name
FROM employees
WHERE department_id  <>ALL(
  SELECT DISTINCT department_id
  FROM departments
  WHERE location_id IN(1400,1700)
);

#案例2:返回其它工种中比job_id为‘IT_PROG’工种任一工资低的员工的员工号、姓名、job_id 以及salary
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<ANY(
  SELECT DISTINCT salary
  FROM employees
  WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';
#或
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<(
  SELECT MAX(salary)
  FROM employees
  WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';

#案例3:返回其它部门中比job_id为‘IT_PROG’部门所有工资都低的员工   的员工号、姓名、job_id 以及salary
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<ALL(
  SELECT DISTINCT salary
  FROM employees
  WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';
#或
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<(
  SELECT MIN( salary)
  FROM employees
  WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';

#用连接查询
select j.employee_id,j.last_name,j.job_id,j.salary  
from employees e  
join employees j  
ON e.job_id='IT_PROG'  
where j.job_id !='IT_PROG'  
group by e.department_id,j.employee_id,j.last_name,j.job_id,j.salary  
having max(e.salary)>j.salary;

#3、行子查询(结果集一行多列或多行多列)
#案例:查询员工编号最小并且工资最高的员工信息
SELECT * 
FROM employees
WHERE (employee_id,salary)=(
  SELECT MIN(employee_id),MAX(salary)
  FROM employees
);

#①查询最小的员工编号
SELECT MIN(employee_id)
FROM employees
#②查询最高工资
SELECT MAX(salary)
FROM employees

#③查询员工信息
SELECT *
FROM employees
WHERE employee_id=(
  SELECT MIN(employee_id)
  FROM employees
)AND salary=(
  SELECT MAX(salary)
  FROM employees
);

5.7.2 select后的子查询

仅仅支持标量子查询

#案例:查询每个部门的员工个数
SELECT d.*,(
  SELECT COUNT(*)
  FROM employees e
  WHERE e.department_id = d.department_id
  ) 个数
  FROM departments d;

#用连接查询
select d.*,count(employee_id)  
from employees e  
right join departments d on d.department_id = e.department_id  
group by d.department_id;

#案例2:查询员工号=102的部门名
SELECT (
  SELECT department_name
  FROM departments d
  INNER JOIN employees e
  ON d.department_id=e.department_id
  WHERE e.employee_id=102
) 部门名;
#用连接查询

select department_name  
from departments d  
join employees e on d.department_id = e.department_id  
where employee_id=102;
#where后子查询也可以做

5.7.3 from后的子查询

将子查询结果充当一张表,要求必须起别名

#案例:查询每个部门的平均工资的工资等级
#①查询每个部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
SELECT * FROM job_grades;

#②连接①的结果集和job_grades表,筛选条件平均工资 between lowest_sal and highest_sal
SELECT  ag_dep.*,g.grade_level
FROM (
  SELECT AVG(salary) ag,department_id
  FROM employees
  GROUP BY department_id
) ag_dep
INNER JOIN job_grades g
ON ag_dep.ag BETWEEN lowest_sal AND highest_sal;

#内连接
select avg(salary) av,department_id,grade_level,lowest_sal, highest_sal  
from employees e  
join job_grades  
group by department_id, grade_level,lowest_sal, highest_sal  
having avg(salary) between lowest_sal and highest_sal;

#where后查询
select avg(salary) av,department_id,grade_level  
from employees e  
join job_grades  
where (  
  select grade_level,lowest_sal,highest_sal  
  from employees d  
  group by d.department_id  
  having (select avg(salary)  
  where d.department_id<=>e.department_id  
  ) between lowest_sal and highest_sal  
  ) =(grade_level,lowest_sal,highest_sal)  
group by department_id, grade_level;

5.7.4exists后的子查询(相关子查询)

语法:
exists(完整的查询语句【一行一列、多行多列、一行多列、多行一列的查询结果都行】)

结果:
10(布尔型的输出)(即truefalseSELECT EXISTS(SELECT employee_id FROM employees WHERE salary=300000);

#案例1:查询有员工的部门名

#in
SELECT department_name
FROM departments d
WHERE d.department_id IN(
  SELECT department_id
  FROM employees
);

#exists
SELECT department_name
FROM departments d
WHERE EXISTS(
  SELECT *
  FROM employees e
  WHERE d.department_id=e.department_id
);

#案例2:查询没有女朋友的男神信息
#in
SELECT bo.*
FROM boys bo
WHERE [bo.id](http://bo.id) NOT IN(
  SELECT boyfriend_id
  FROM beauty
);

#exists
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS(
  SELECT boyfriend_id
  FROM beauty b
  WHERE bo.id=b.boyfriend_id
);
案例:
#1.  查询和Zlotkey相同部门的员工姓名和工资
SELECT last_name,salary
FROM employees
WHERE department_id = (
  SELECT department_id
  FROM employees
  WHERE last_name = 'Zlotkey'
)#内查询做法
select e.last_name,e.salary  
from employees e,employees d  
where e.department_id=d.department_id  
and d.last_name='zlotkey'#2.查询工资比公司平均工资高的员工的员工号,姓名和工资。
SELECT last_name,employee_id,salary
FROM employees
WHERE salary>(
  SELECT AVG(salary)
  FROM employees
);
  
#内连接做法
select e.employee_id,e.last_name,e.salary  
from employees e,employees d  
group by e.employee_id,e.last_name,e.salary  
having e.salary>avg(d.salary);

#3.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资
SELECT employee_id,last_name,salary,e.department_id
FROM employees e
INNER JOIN (
  SELECT AVG(salary) ag,department_id
  FROM employees
  GROUP BY department_id
  ) ag_dep
ON e.department_id = ag_dep.department_id
WHERE salary>ag_dep.ag;

#其他做法
select employee_id,last_name,salary  
from employees e  
where salary>(
  select avg(d.salary)  
  from employees d  
  group by d.department_id  
  having d.department_id=e.department_id);  
------------------------------------------
select e.employee_id,e.last_name,e.salary  
from employees e  join employees d  
on d.department_id=e.department_id  
group by d.department_id,e.employee_id,e.last_name,e.salary  
having avg(d.salary)<e.salary;  
-----------------------------------------------------------
select e.employee_id,e.last_name,e.salary  
from employees e  join (  
  select avg(salary) av,department_id  
  from employees d  
  group by department_id  
  ) avg  
on salary>av  
where e.department_id=avg.department_id;

#4.  查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
SELECT last_name,employee_id
FROM employees
WHERE department_id IN(
  SELECT  DISTINCT department_id
  FROM employees
  WHERE last_name LIKE '%u%'
);

#其他做法:
select d.last_name,d.employee_id,d.department_id  
from employees e,employees d  
where e.last_name like('%u%')  
and d.department_id=e.department_id  
and d.last_name not like ('%u%')  
group by d.department_id,d.last_name,d.employee_id  
;  
---------------------------------------------------
select last_name,employee_id,e.department_id  
from employees e  
join (  
  select distinct department_id  
  from employees d  
    where d.last_name like('%u%')  
    ) d  
on e.department_id=d.department_id  
where last_name not like ('%u%');

#5. 查询在部门的location_id为1700的部门工作的员工的员工号
SELECT employee_id
FROM employees
WHERE department_id =ANY(
  SELECT DISTINCT department_id
  FROM departments 
  WHERE location_id  = 1700
);

#其他做法:
select employee_id  
from employees e  
join departments d on d.department_id = e.department_id  
where location_id=1700;  
-------------------------------------------------------
select employee_id  
from employees e  
join departments d on e.department_id = d.department_id  
where e.department_id=(  
  select d.department_id  
  where d.location_id=1700  
  );

#6.查询管理者是King的员工姓名和工资
SELECT last_name,salary
FROM employees
WHERE manager_id IN(
  SELECT employee_id
  FROM employees
  WHERE last_name  = 'K_ing'
);

#其他做法:
select e.last_name,e.salary  
from employees e  
join employees m  
where m.last_name='K_ing'  
and m.employee_id=e.manager_id;

#7.查询工资最高的员工的姓名,要求first_name和last_name显示为一列,列名为 姓.名
SELECT CONCAT(first_name,last_name) "姓.名"
FROM employees
WHERE salary=(
  SELECT MAX(salary)
  FROM employees
);

#其他做法:
select concat(e.first_name,e.last_name) 姓名,e.salary  
from employees e,employees m  
group by e.first_name,e.last_name,e.salary  
having e.salary=max(m.salary);
案例:
1. 查询工资最低的员工信息: last_name, salary
SELECT last_name,salary
FROM employees
WHERE salary=(
  SELECT MIN(salary)
  FROM employees
);

2. 查询平均工资最低的部门信息
#方式一:
#①各部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id

#②查询①结果上的最低平均工资
SELECT MIN(ag)
FROM (
  SELECT AVG(salary) ag,department_id
  FROM employees
  GROUP BY department_id
) ag_dep;

#③查询哪个部门的平均工资=②
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)=(
  SELECT MIN(ag)
  FROM (
    SELECT AVG(salary) ag,department_id
    FROM employees
    GROUP BY department_id
  ) ag_dep
);

#④查询部门信息
SELECT d.*
FROM departments d
WHERE d.department_id=(
  SELECT department_id
  FROM employees
  GROUP BY department_id
  HAVING AVG(salary)=(
    SELECT MIN(ag)
    FROM (
      SELECT AVG(salary) ag,department_id
      FROM employees
      GROUP BY department_id
    ) ag_dep
  )
);

#方式二:
#①各部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id;

#②求出最低平均工资的部门编号
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) 
LIMIT 1;

#③查询部门信息
SELECT *
FROM departments
WHERE department_id=(
  SELECT department_id
  FROM employees
  GROUP BY department_id
  ORDER BY AVG(salary) 
  LIMIT 1
);

#其他做法:
select d.*  
from departments d  
join employees e on d.department_id = e.department_id  
group by d.department_id,e.department_id  
having avg(salary)=(  
  select min(av)  
  from (  
    select avg(b.salary) av  
    from employees b  
    group by b.department_id  
  ) a  
);
------------------------------------------------------
select d.*  
from departments d  
join employees e on d.department_id = e.department_id  
group by e.department_id  
order by avg(salary)  
limit 1;

#用limit的风险在于如果有两个部门的平均工资都是最低的,那么就会出现漏检,慎用!!!。

3. 查询平均工资最低的部门信息和该部门的平均工资
#①各部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id;

#②求出最低平均工资的部门编号
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) 
LIMIT 1;

#③查询部门信息
SELECT d.*,ag
FROM departments d
JOIN (
  SELECT AVG(salary) ag,department_id
  FROM employees
  GROUP BY department_id
  ORDER BY AVG(salary) 
  LIMIT 1
) ag_dep
ON d.department_id=ag_dep.department_id;

#其他做法:
select d.*,avg(salary)  
from departments d  
join employees e on d.department_id = e.department_id  
group by e.department_id  
having avg(salary)=(  
  select avg(salary)  
  from employees  
  group by department_id  
  order by avg(salary)  
  limit 1  
  );

#其他做法:
select d.*,av  
from departments d  
join (  
  select avg(salary) av,department_id  
  from employees e  
  group by department_id) a  
where a.department_id=d.department_id  
and av<=all(  
  select avg(salary) av  
  from employees e  
  group by department_id  
  );

4. 查询平均工资最高的 job 信息
#①查询最高的job的平均工资
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id
ORDER BY AVG(salary) DESC
LIMIT 1;

#②查询job信息
SELECT * 
FROM jobs
WHERE job_id=(
  SELECT job_id
  FROM employees
  GROUP BY job_id
  ORDER BY AVG(salary) DESC
  LIMIT 1
);

#其他写法:
select j.*  
from jobs j  
where job_id=(  
  select job_id  
  from employees  
  group by job_id  
  having avg(salary)=(  
    select max(av)  
    from (  
      select avg(salary) av  
      from employees  
      group by job_id  
    ) a  
  )  
);

5. 查询平均工资高于公司平均工资的部门有哪些?
#①查询平均工资
SELECT AVG(salary)
FROM employees;

#②查询每个部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id;

#③筛选②结果集,满足平均工资>①
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)>(
  SELECT AVG(salary)
  FROM employees
);

6. 查询出公司中所有 manager 的详细信息.
#①查询所有manager的员工编号
SELECT DISTINCT manager_id
FROM employees

#②查询详细信息,满足employee_id=①
SELECT *
FROM employees
WHERE employee_id =ANY(
  SELECT DISTINCT manager_id
  FROM employees
);

#其他做法:
select m.*  
from employees e  
join employees m on e.manager_id=m.employee_id  
group by m.employee_id;

7. 各个部门中 最高工资中最低的那个部门的 最低工资是多少
#①查询各部门的最高工资中最低的部门编号
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY MAX(salary)
LIMIT 1;

#②查询①结果的那个部门的最低工资
select min(salary),department_id
from employees e
where department_id=(
  select department_id
  from employees
  group by department_id
  order by max(salary)
  limit 1
  )
group by department_id;

#其他写法:
select min(salary)  
from employees e  
join (  
  select max(salary) max,department_id  
  from employees d  
  group by d.department_id  
  ) a on e.department_id=a.department_id  
where max<=all(  
  select max(salary) max  
  from employees f  
  group by f.department_id  
  ) 
group by e.department_id;

8. 查询平均工资最高的部门的 manager 的详细信息: last_name, department_id, email, salary
#①查询平均工资最高的部门编号
SELECT department_id 
FROM employees 
GROUP BY department_id 
ORDER BY AVG(salary) DESC 
LIMIT 1;

#②将employees和departments连接查询,筛选条件是①
SELECT last_name, d.department_id, email, salary 
FROM employees e 
INNER JOIN departments d 
ON d.manager_id = e.employee_id 
WHERE d.department_id = 
   (SELECT department_id 
    FROM employees 
    GROUP BY department_id 
    ORDER BY AVG(salary) DESC 
    LIMIT 1) ;

#其他写法:
select last_name,department_id,email,salary  
from employees m  
where m.employee_id=(  
  select d.manager_id  
  from departments d  
    where d.department_id=(  
    	select department_id  
  		from employees e  
    	group by department_id  
  		order by avg(e.salary) desc  
  		limit 1  
));
-----------------------------------------------------
select c.last_name,c.department_id,email,salary  
from departments d  
join employees c on d.department_id = c.department_id  
where c.employee_id in(  
  select d.manager_id  
  from departments  
  where d.department_id=(  
    SELECT department_id  
    FROM employees  
    GROUP BY department_id  
    HAVING AVG(salary)=(  
      SELECT Max(ag)  
      FROM (  
        SELECT AVG(salary) ag,department_id  
        FROM employees  
        GROUP BY department_id  
    ) ag_dep  
    )  
  )  
);

5.8 分页查询

应用场景:
当要显示的数据,一页显示不全,需要分页提交sql请求

语法:
select 查询列表
from 表
【join typejoin2
on 连接条件
where 筛选条件
group by 分组字段
having 分组后的筛选
order by 排序的字段】
limitoffset,】size;

  
offset:要显示条目的起始索引(起始索引从0开始)
size:要显示的条目个数

特点:
①limit语句放在查询语句的最后
②公式:
select 查询列表
fromlimit (page-1)*size,size;#要显示的页数 page,每页的条目数size

#案例1:查询前五条员工信息
SELECT * FROM  employees LIMIT 0,5;
SELECT * FROM  employees LIMIT 5;

#案例2:查询第11条——第25条
SELECT * FROM  employees LIMIT 10,15;

#案例3:有奖金的员工信息,并且工资较高的前10名显示出来
SELECT *
FROMemployees 
WHERE commission_pct IS NOT NULL 
ORDER BY salary DESC 
LIMIT 10 ;

5.9 联合查询

union:联合 合并:将多条查询语句的结果合并成一个结果

语法:
查询语句1
union
查询语句2
union
...

应用场景:
要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时。

特点:★
1、要求多条查询语句的查询列数是一致的!
2、要求多条查询语句的查询的每一列的类型和顺序最好一致。
3union关键字默认去重,如果使用union all 可以包含重复项。
4union相当于增加表的行数,join相当于增加表的列数。
   联合查询是上下拼接,连接查询是左右拼接。
5、联合查询以第一张表的字段为准。

#引入的案例:查询部门编号>90或邮箱包含a的员工信息
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;;
SELECT * FROM employees  WHERE email LIKE '%a%'
UNION
SELECT * FROM employees  WHERE department_id>90;

#案例:查询中国用户中男性的信息以及外国用户中年男性的用户信息
SELECT id,cname FROM t_ca WHERE csex='男'
UNION ALL
SELECT t_id,tname FROM t_ua WHERE tGender='male';
查询语句执行顺序:
先查表(from),再连接(join),再筛选(where),再分组(group by),再筛选(having),再选出列表(select),再排序(order by),再限制(limit)。

六、TCL语句(事务控制语言)

Transaction Control Language 事务控制语言

事务:
一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。

事务的特性:ACID
原子性:一个事务不可再分割,要么都执行要么都不执行
> 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

一致性:一个事务执行会使数据从一个一致状态切换到另外一个一致状态
> 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

隔离性:一个事务的执行不受其他事务的干扰
> 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性:一个事务一旦提交,则会永久的改变数据库的数据
> 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

语法:
开启事务的语句;
updateset 张三丰的余额=500 where name='张三丰'
updateset 郭襄的余额=1500 where name='郭襄' 
结束事务的语句;

#显示自动提交的开启与否
SHOW VARIABLES LIKE 'autocommit';

#显示存储引擎
SHOW ENGINES;

一、使用步骤
事务的创建:
1.隐式事务:事务没有明显的开启和结束的标记
比如insertupdatedelete语句

2.显式事务:事务具有明显的开启和结束的标记
前提:必须先设置自动提交功能为禁用
set autocommit=0;    #只禁用了当前会话的自动提交。
(对于一个单一系列的语句禁用autocommit模式,则可以使用START TRANSACTION语句)

步骤1:开启事务
方式一:
set autocommit=0;start transaction;】

方式二:
BEGIN;

步骤2:编写事务中的sql语句(增删查改)(select insert update delete)
语句1;
语句2;
...

步骤3:结束事务(暂时不能根据情况选择结束的方式,比如回顾或是提交,后面讲JDBC会细说)
commit;提交事务
rollback;回滚事务

#1.演示事务的使用步骤
#开启事务
SET autocommit=0;
START TRANSACTION;
#编写一组事务的语句
UPDATE account SET balance = 1000 WHERE username='张无忌';
UPDATE account SET balance = 1000 WHERE username='赵敏';
#结束事务
ROLLBACK;
#或commit;

SELECT * FROM account;

二、设置保存点
语法:
savepoint 节点名;#设置保存点,只能与rollback搭配使用。

#演示savepoint 的使用
SET autocommit=0;
START TRANSACTION;
DELETE FROM account WHERE id=25;
SAVEPOINT a;#设置保存点
DELETE FROM account WHERE id=28;
ROLLBACK TO a;#回滚到保存点
SELECT * FROM account;

三、delete和trucate在事务使用时的区别
演示delete,支持回滚
SET autocommit = 0;
START TRANSACTION;
DELETE FROM account;
ROLLBACK;

演示truncate,不支持回滚
SET autocommit = 0;
START TRANSACTION;
TRUNCATE TABLE account;
ROLLBACK;

四、事务的隔离
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
1.脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。
2.不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。
3.幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中 插入了一些新的行。 之后, 如果 T1 再次读取同一个表, 就会多出几行。

不可重复读的原因是对于某一条数据,前一个事务还没有结束时,另外一个事务也访问该记录。显然,锁住这一条记录就可以解决问题。
不可重复读,是在同一个事务中多次读一个值却出现不同的结果,导致出错。
幻读的发生是前一个事务从表中查出某个字段的所有记录,此时另一个事务向表中插入新的记录,插入后之前的事务如果执行同样的查询,可能就会多出来一些记录。

不可重复读的重点是修改 ,幻读的重点在于新增或者删除。

数据库事务的隔离性: 
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响, 避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。

数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。

|            隔离级别          |描述|
|READ UNCOMMITTED(读未提交数据)|允许事务读取未被其他事物提交的变更。脏读、不可重复读和幻读的问题都会出现|
| READ COMMITTED(读巳提交数据) |只允许事务读取已经被其它事务提交的变更。可以避免脏读,但不可重复读和幻读问题仍然可能出现|
|  REPEATABLE READ(可重复读)  |确保事务可以多次从一个字段中读取相同的值在这个事务持续期问,禁止其他事物对这个字段进行更新.可以避免脏读和不可重复读,但幻读的问题仍然存在|
|     SERIALIZABLE(串行化)    |确保事务可以从一个表中读取相同的行在这个事务持续期问,禁止其他事务对该表执行插入,更新和删除操作.所有并发问题都可以避免,但性能十分低下|

事务的隔离级别对应的可能出现的问题:
|                |脏读|不可重复读|幻读|
|read uncommitted||||
| read committed |  × |||
| repeatable read|  × |    ×    ||
|  serializable  |  × |    ×    |  × |

各类隔离级别可能会出现的问题:
read uncommitted:两个事务同时执行,事务A修改数据,未提交,事务B读取数值时会是已修改的数据。(即脏读)
read committed:两个事务同时执行,事务B读取了一个数据,事务A在这之后修改了数据并提交,事务B再次读取数据,会出现前后数据不同的现象。(即不可重复读)
repeatable read:两个事务同时执行,事务A查看了数据,共n行,事务B在这之后增加(或减少)了m行并提交,事务A在修改所有行数据的时候,会出现修改了n+m(或n-m)的结果,而不是修改了之前查询到的n行。(即幻读)

Oracle支持的2种事务隔离级别READ COMMITTED,SERIALIZABLE。 
Oracle默认的事务隔离级别为:READ COMMITTED

Mysql支持4种事务隔离级别。
Mysql默认的事务隔离级别为: REPEATABLE READ

每启动一个 mysql 程序, 就会获得一个单独的数据库连接。
每个数据库连接都有一个全局变量 @@tx_isolation, 表示当前的事务隔离级别。

查看隔离级别:
select @@tx_isolation;
select @@transaction_isolation;#mysql8.0用这个语句

设置设置当前 mySQL 连接的隔离级别:
set session transaction isolation level 隔离级别;
设置数据库系统的全局的隔离级别(需要重启):
set global transaction isolation level 隔离级别;

七、视图

含义:虚拟表,和普通表一样使用。

MySQL从5.0.1版本开始提供视图功能。一种虚拟存在的表,行和列的数据来自定义视图的查询中使用的表。
并且是在使用视图时动态生成的,只保存了sql逻辑,不保存查询结果,是通过表动态生成的数据。

视图并不直接保存数据,只是保存了逻辑,只是保存了SQL语句,是在使用视图的时候动态生成的。
对于某些频繁使用的结果集,可以考虑定义为一个视图。某种意义上讲,视图就是被保存了的SQL语句。

|    |创建语法的关键字 |是否实际占用物理空间|           使用            |
|视图|  create view   | 只是保存了sql逻辑  |增删改查,只是一般不能增删改|
||  create table  |     保存了数据     |         增删改查          |

应用场景:
– 多个地方用到同样的查询结果
– 该查询结果使用的sql语句较复杂

好处:
• 重用sql语句
• 简化复杂的sql操作,不必知道它的查询细节
• 保护数据,提高安全性

一、创建视图
语法:
create view 视图名
as
查询语句;

USE myemployees;
#1.查询姓名中包含a字符的员工名、部门名和工种信息
#①创建
CREATE VIEW myv1
AS
SELECT last_name,department_name,job_title
FROM employees e
JOIN departments d ON e.department_id  = d.department_id
JOIN jobs j ON j.job_id  = e.job_id;
#②使用
SELECT * FROM myv1 WHERE last_name LIKE '%a%';

#2.查询各部门的平均工资级别
#①创建视图查看每个部门的平均工资
CREATE VIEW myv2
AS
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id;

#②使用
SELECT myv2.ag,g.grade_level
FROM myv2
JOIN job_grades g
ON myv2.ag BETWEEN g.lowest_sal AND g.highest_sal;

#3.查询平均工资最低的部门信息
SELECT * FROM myv2 ORDER BY ag LIMIT 1;

#4.查询平均工资最低的部门名和工资
CREATE VIEW myv3
AS
SELECT * FROM myv2 ORDER BY ag LIMIT 1;
SELECT d.*,m.ag
FROM myv3 m
JOIN departments d
ON m.department_id=d.department_id;

二、修改视图
方式一:
create or replace view  视图名    #**存在则修改,不存在则创建**
as
查询语句;

#测试代码:
SELECT * FROM myv3 
CREATE OR REPLACE VIEW myv3
AS
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id;

方式二:
语法:
alter view 视图名
as 
查询语句;

#测试代码:
ALTER VIEW myv3
AS
SELECT * FROM employees;

三、删除视图
语法:
drop view 视图名1,视图名2,...;

四、查看视图
DESC 视图名;
SHOW CREATE VIEW 视图名;

五、更新视图
更新视图的数据,也会对原始表的数据进行更新。

#测试代码
CREATE OR REPLACE VIEW myv1
AS
SELECT last_name,email,salary*12*(1+IFNULL(commission_pct,0)) "annual salary"
FROM employees;
CREATE OR REPLACE VIEW myv1
AS
SELECT last_name,email
FROM employees;
SELECT * FROM myv1;
SELECT * FROM employees;

#1.插入
INSERT INTO 视图名 VALUES(值,值,···);

#2.修改
UPDATE 视图名 SET 列名=字符/WHERE 筛选条件;

#3.删除
DELETE FROM 视图名 WHERE 筛选条件;

#具备以下特点的视图不允许更新:

①包含以下关键字的sql语句:分组函数、distinctgroup  byhavingunion或者union all
#测试代码
CREATE OR REPLACE VIEW myv1
AS
SELECT MAX(salary) m,department_id
FROM employees
GROUP BY department_id;
SELECT * FROM myv1;
#更新
UPDATE myv1 SET m=9000 WHERE department_id=10;

②常量视图
#测试代码
CREATE OR REPLACE VIEW myv2
AS
SELECT 'john' NAME;
SELECT * FROM myv2;
#更新
UPDATE myv2 SET NAME='lucy';Select中包含子查询
#测试代码
CREATE OR REPLACE VIEW myv3
AS
SELECT department_id,(SELECT MAX(salary) FROM employees) 最高工资
FROM departments;
#更新
SELECT * FROM myv3;
UPDATE myv3 SET 最高工资=100000;join9299都不行)
#测试代码
CREATE OR REPLACE VIEW myv4
AS
SELECT last_name,department_name
FROM employees e
JOIN departments d
ON e.department_id  = d.department_id;
#更新
SELECT * FROM myv4;
UPDATE myv4 SET last_name  = '张飞' WHERE last_name='Whalen';
INSERT INTO myv4 VALUES('陈真','xxxx');

#⑤from一个不能更新的视图
#测试代码
CREATE OR REPLACE VIEW myv5
AS
SELECT * FROM myv3;
#更新
SELECT * FROM myv5;
UPDATE myv5 SET 最高工资=10000 WHERE department_id=60;

#⑥where子句的子查询引用了from子句中的表

#测试代码
CREATE OR REPLACE VIEW myv6
AS
SELECT last_name,email,salary
FROM employees
WHERE employee_id IN(
  SELECT  manager_id
  FROM employees
  WHERE manager_id IS NOT NULL
);
#更新
SELECT * FROM myv6;
UPDATE myv6 SET salary=10000 WHERE last_name = 'k_ing';
案例:
#一、创建视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱
CREATE OR REPLACE VIEW emp_v1
AS
SELECT last_name,salary,email
FROM employees
WHERE phone_number LIKE '011%';

#二、创建视图emp_v2,要求查询部门的最高工资高于12000的部门信息
CREATE OR REPLACE VIEW emp_v2
AS
SELECT MAX(salary) mx_dep,department_id
FROM employees
GROUP BY department_id
HAVING MAX(salary)>12000;

SELECT d.*,m.mx_dep
FROM departments d
JOIN emp_v2 m
ON m.department_id = d.department_id;

八、变量

分类:
系统变量(由系统提供,非用户定义的):
  全局变量:针对于整个服务器,打开任何客户端都有效。
  会话变量:针对于一次会话(客户端的一次连接)有效。
自定义变量(由用户自行定义的):
  用户变量:直接在类中声明的变量叫成员变量(又称全局变量)。
  局部变量:用java的话来说——方法中的参数、方法中定义的变量和代码块中定义的变量统称为局部变量。

()系统变量
说明:变量由系统定义,不是用户定义,属于服务器层面。
注意:全局变量需要添加global关键字,会话变量需要添加session关键字,如果不写,默认会话级别。
使用步骤:具体查询用select,模糊查询用show1、查看所有系统变量
show global variables;   #查看全局变量
showsession】variables;   #查看会话变量

2、查看满足条件的部分系统变量
show global|session】 variables like '任意字符';

#测试代码
show global|session】 variables like '%char%';

3、查看指定的系统变量的值(具体的名字用select查看)
select @@global|session.指定的系统变量名;

4、为某个系统变量赋值(有@@就要加个**.**)
方式一:
set global|session】系统变量名=;
方式二:
set @@global|session.系统变量名=;

一、全局变量
作用域:为整个服务器,针对于所有会话(连接)有效,但不能跨重启。
属于服务器层面上的,必须拥有super权限才能为系统变量赋值。

1.查看所有全局变量
SHOW GLOBAL VARIABLES;

2.查看满足条件的部分系统变量
show global variables like '任意字符';

#测试代码
SHOW GLOBAL VARIABLES LIKE '%char%';

3.查看指定的系统变量的值
select @@global.指定的系统变量名;

#测试代码
SELECT @@global.autocommit;

4.为某个系统变量赋值
set @@global.系统变量名=;
set global 系统变量名=;

#测试代码
SET @@global.autocommit=0;
SET GLOBAL autocommit=0;

二、会话变量
作用域:针对于当前会话(连接)有效
服务器为每一个连接的客户端都提供了系统变量

1.查看所有会话变量
SHOW SESSION VARIABLES;

2.查看满足条件的部分会话变量
showsession】 variables like '任意字符';

#测试代码
SHOW SESSION VARIABLES LIKE '%char%';

3.查看指定的会话变量的值
select @@【session**.**指定的系统变量名;

- 测试代码
SELECT @@autocommit;
SELECT @@session.tx_isolation;

4.为某个会话变量赋值
set @@【session.系统变量名=;
set @@【session】 系统变量名=;

#测试代码
SET @@session.tx_isolation='read-uncommitted';
SET SESSION tx_isolation='read-committed';

()自定义变量
说明:变量由用户自定义,而不是系统提供的

使用步骤:
1、声明
2、赋值
3、使用(查看、比较、运算等)

一、用户变量
作用域:针对于当前会话(连接)有效,作用域同于会话变量

用户变量声明必须初始化。

#赋值操作符:=或:=

①声明并初始化(以下三种都有用)
SET @变量名=;
SET @变量名**:**=;
SELECT @变量名**:**=;

②赋值(更新变量的值)
#方式一:
  SET @变量名=;
  SET @变量名:=;
  SELECT @变量名:=;

#方式二:
  SELECT 字段 INTO @变量名
  FROM;
 
③使用(查看变量的值)
SELECT @变量名;

二、局部变量
作用域:仅仅在定义它的begin end块中有效。
应用在 begin end中的第一句话。

①声明
DECLARE 变量名 类型;
DECLARE 变量名 类型 【DEFAULT 值】;

②赋值(更新变量的值)
#方式一:
SET 局部变量名=;
SET 局部变量名:=;
SELECT @局部变量名:=;

#方式二:
SELECT 字段 INTO 局部变量名
FROM;
  
③使用(查看变量的值)
SELECT 局部变量名;

#案例:声明两个变量,求和并打印
#用户变量
SET @m=1;
SET @n=1;
SET @sum=@m+@n;
SELECT @sum;

#局部变量
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 1;
DECLARE SUM INT;
SET SUM=m+n;
SELECT SUM;

三、两者的比较
|        |      作用域      |      定义位置     |         语法          |
|用户变量|      当前会话     |   会话的任何地方  | 加@符号,不用指定类型  |
|局部变量|定义它的BEGIN END|BEGIN END的第一句话|一般不用加@,需要指定类型|

九、约束

含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性。

分类:
六大约束:非空约束、默认约束、主键约束、唯一约束、检查约束、外键约束。

|           |  名称  |功能|举例|
|  NOT NULL |非空约束|非空,用于保证该字段的值不能为空|姓名、学号|
|  DEFAULT  |默认约束|默认,用于保证该字段有默认值|性别|
|PRIMARY KEY|主键约束|主键,用于保证该字段的值具有唯一性,并且非空|学号、员工编号|
|   UNIQUE  |唯一约束|唯一,用于保证该字段的值具有唯一性,可以为空|座位号|
|   CHECK   |检查约束|检查约束【mysql8.0版本支持】|
|FOREIGN KEY|外键约束|外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值,限制两表主外键数据的一致性。
在从表添加外键约束,用于引用主表中某列的值|学生表的专业编号,员工表的部门编号,员工表的工种编号|

#在unique约束中,两个NULL不会认为是重复。
#在外键约束中,删除主表中的对应字段后,从表中的外键字段会级联自动删除?

添加约束的时机:(数据添加之前)
  1.创建表时
  2.修改表时

约束的添加分类:
  列级约束:
    六大约束语法上都支持,但外键约束没有效果
  表级约束:
    除了非空、默认,其他的都支持

主键和唯一的大对比:
|    |保证唯一性|是否允许为空|    一个表中可以有多少个   |是否允许组合 |
|主键||     ×     | 只能有一个,可以是组合主键 |√,但不推荐 |
|唯一|||        可以有多个         |√,但不推荐 |

联合主键:
【CONSTRAINT 约束名】 PRIMARY KEY(列名1,列名2,···)
#同列可用相同,但不能所有列同时相同。

外键:
  1、要求在从表设置外键关系
  2、从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
  3、主表的关联列必须是一个key(一般是主键或唯一)
  4、插入数据时,先插入主表,再插入从表

删除数据时,先删除从表,再删除主表

关键字一: ON DELETE CASCADE( 级联删除)
效果: 当父表中的列被删除时,子表中相对应的列也被删除。
ALTER TABLE 表名 ADD CONSTRAINT 外键约束名 FOREIGN KEY(列名1) REFERENCES 外部表(列名2) ON DELETE CASCADE;

关键字二:ON DELETE SET NULL( 级联置空)
效果: 主表数据删除,外表相关字段置空
ALTER TABLE 表名 ADD CONSTRAINT 外键约束名 FOREIGN KEY(列名1) REFERENCES 外部表(列名2) ON DELETE SET NULL;

列级约束与表级约束的区别:
|       |    位置    |     支持的约束类型      |  是否可以起约束名  |
|列级约束|  列的后面  |语法都支持,但外键没有效果|       不可以      |
|表级约束|所有列的下面|默认和非空不支持,其他支持|可以(主键没有效果)|

①位置不同 :列级约束是写在列的后面,标记约束时写在所有字段的最后面
②列级约束不可以起约束名,表记约束可以起约束名(主键除外,主键使用的PRIMARY KEY)
③支持的约束类型不同:列级约束可以支持除了外键之外的约束类型,表级约束不能支持非空和默认
④列级约束是定义在列属性中的,而表级约束是定义在列之后的,两者本质上没什么区别,而如果需要同时对多列进行约束,那么就只能采用表级约束,因为表级约束面向的是表(当然就包括所有列),而列级约束只能针对该列进行约束。

一、创建表时添加约束
1.添加列级约束
只支持:默认、非空、主键、唯一、检查(仅在8.0以上版本可用)

语法:直接在字段名和类型后面追加,约束类型即可。可以同时加多个约束,直接用空格隔开就好。
CREATE TABLE 表名(
  列名1 列类型 PRIMARY KEY,#主键
  列名2 列类型 NOT NULL,#非空
  列名3 列类型 CHECK(列名3=/字符 OR 列名3 =/字符),#检查
  列名4 列类型 UNIQUE,#唯一
  列名5 列类型 DEFAULT/字符,#默认约束
  列名6 列类型 REFERENCES 外部表(列名7),#外键,无效
  列名8 列类型 约束1 约束2 ···    #多个约束用空格隔开
);

#查看表名中的所有索引,包括主键、外键、唯一
SHOW INDEX FROM 表名;

#找默认名字的办法:
SHOW CREATE TABLE 表名;create table里的建表信息复制出来,着重号里的就是默认别名

#测试代码
USE students;
DROP TABLE stuinfo;
CREATE TABLE stuinfo(
  id INT PRIMARY KEY,#主键
  stuName VARCHAR(20) NOT NULL,#非空
  gender CHAR(1) CHECK(gender='男' OR gender ='女'),#检查
  seat INT UNIQUE,#唯一
  age INT DEFAULT  18,#默认约束
  majorId INT REFERENCES major(id)#外键,无效
);
CREATE TABLE major(
  id INT PRIMARY KEY,
  majorName VARCHAR(20)
);
SHOW INDEX FROM stuinfo;

2.添加表级约束
只支持:主键、唯一、检查、外键

语法:在各个字段的最下面添加【constraint 约束名】 约束类型(列名) 

CREATE TABLE 表名(
  列名1 列类型,
  列名2 列类型,
  列名3 列类型,
  列名4 列类型,
  【CONSTRAINT 约束名】 PRIMARY KEY(列名1),           #主键CONSTRAINT 约束名】 UNIQUE(列名2),               #唯一键CONSTRAINT 约束名】 CHECK(列名3=/字符 OR 列名3=/字符),      #检查CONSTRAINT 约束名】 FOREIGN KEY(列名) REFERENCES 其他表名(列名5)     #外键
);

#通用的写法:★
主键,唯一,非空,检查,默认写在列级约束,外键写在表级约束

CREATE TABLE IF NOT EXISTS stuinfo(
  id INT PRIMARY KEY,
  stuname VARCHAR(20) NOT NULL,
  sex CHAR(1),
  age INT DEFAULT 18,
  seat INT UNIQUE,
  majorid INT,
  CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)
);

#测试代码
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
  id INT,
  stuname VARCHAR(20),
  gender CHAR(1),
  seat INT,
  age INT,
  majorid INT,
  CONSTRAINT pk PRIMARY KEY(id),           #主键
  CONSTRAINT uq UNIQUE(seat),               #唯一键
  CONSTRAINT ck CHECK(gender ='男' OR gender  = '女'),      #检查
  CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)     #外键
);
SHOW INDEX FROM stuinfo;

二、修改表时添加约束
1、添加列级约束
alter table 表名 modifycolumn】 列名 列类型 新约束;

2、添加表级约束
alter table 表名 addconstraint 约束名】 约束类型(列名) 【外键的引用】;

①添加非空约束
ALTER TABLE 表名 MODIFYcolumn】 列名 列类型 NOT NULL;

②添加默认约束
ALTER TABLE 表名 MODIFYcolumn】 列名 列类型 DEFAULT/字符;

③添加主键
列级约束:
ALTER TABLE 表名 MODIFYcolumn】 列名 列类型 PRIMARY KEY;

表级约束:
ALTER TABLE 表名 ADD PRIMARY KEY(列名);

④添加唯一
列级约束:
ALTER TABLE 表名 MODIFYcolumn】 列名 列类型 UNIQUE;

表级约束:
ALTER TABLE 表名 ADD UNIQUE(列名);

⑤添加外键
ALTER TABLE 表名 ADDCONSTRAINT 约束名】 FOREIGN KEY(列名1) REFERENCES 其他表名(列名2); 

#测试代码
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
  id INT,
  stuname VARCHAR(20),
  gender CHAR(1),
  seat INT,
  age INT,
  majorid INT
)
DESC stuinfo;

三、修改表时删除约束
1.删除非空约束
ALTER TABLE 表名 MODIFYCOLUMN】 列名 列类型 【NULL;

2.删除默认约束
ALTER TABLE 表名 MODIFYCOLUMN】 列名 列类型;

3.删除主键
ALTER TABLE 表名 DROP PRIMARY KEY;

4.删除唯一
ALTER TABLE 表名 DROP INDEX 列名;

5.删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 外键约束名;
SHOW INDEX FROM 表名;
#show create table 表名之后发现Key还在(其实就是索引还在),需要ALTER TABLE 表名 DROP KEY 外键约束名,才能删除干净。
标识列(自增长列)
含义:可以不用手动的插入值,系统提供默认的序列值

特点:
1、标识列必须和主键搭配吗?不一定,但要求是一个key
2、一个表可以有几个标识列?至多一个!
3、标识列的类型只能是数值型,输入null时会自动填充。
4、标识列可以通过
   SET auto_increment_increment=数值;
   设置步长,重新设置了步长后再插入数值,会按照新的步长自增。
   也可以通过
   set auto_increment_offset=数值; 
   设置起始值,重新设置了初始值后再插入数值,会从上次的数值继续自增,重新设置的初始值只会在下次生效。
   也可以通过手动插入值,改变起始值。

一、创建表时设置标识列
语法:
CREATE TABLE 表名(
  列名 列类型 AUTO_INCREMENT,
);

#测试代码
DROP TABLE IF EXISTS tab_identity;
CREATE TABLE tab_identity(  
  id INT  PRIMARY KEY AUTO_INCREMENT,  
  NAME varchar(10)  
);  #用来测试数字自增长

CREATE TABLE tab_identity(
  id INT  ,
  NAME FLOAT UNIQUE AUTO_INCREMENT,
  seat INT 
);
TRUNCATE TABLE tab_identity;
INSERT INTO tab_identity(id,NAME) VALUES(NULL,'john');
INSERT INTO tab_identity(NAME) VALUES('lucy');
SELECT * FROM tab_identity;
SHOW VARIABLES LIKE '%auto_increment%';
SET auto_increment_increment=3;

二、修改表时设置标识列
ALTER TABLE 表名 MODIFYCOLUMN】 列名 列类型 原有的键 AUTO_INCREMENT;  #列原有的键也应该添加上去

三、修改表时删除标识列
ALTER TABLE 表名 MODIFYCOLUMN】 列名 列类型;
案例:
#1.向表emp2的id列中添加PRIMARY KEY约束(my_emp_id_pk)
ALTER TABLE emp2 MODIFY COLUMN id INT PRIMARY KEY;
ALTER TABLE emp2 ADD CONSTRAINT my_emp_id_pk PRIMARY KEY(id);
#2.  向表dept2的id列中添加PRIMARY KEY约束(my_dept_id_pk)
#3.  向表emp2中添加列dept_id,并在其中定义FOREIGN KEY约束,与之相关联的列是dept2表中的id列。
ALTER TABLE emp2 ADD COLUMN dept_id INT;
ALTER TABLE emp2 ADD CONSTRAINT fk_emp2_dept2 FOREIGN KEY(dept_id) REFERENCES dept2(id);

十、存储过程和函数

存储过程
含义:一组预先编译好的SQL语句的集合,理解成批处理语句
作用:
1、提高代码的重用性
2、简化操作
3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率

一、创建语法
CREATE PROCEDURE 存储过程名(参数列表)
BEGIN
  存储过程体(一组合法的SQL语句)
END;

注意:
1、参数列表包含三部分
参数模式  参数名  参数类型

举例:
in stuname varchar(20)

参数模式:
in:该参数可以作为输入,也就是该参数需要调用方传入值(默认为inout:该参数可以作为输出,也就是该参数可以作为返回值
inout:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值

可以理解JAVA中传入函数,和返回值类型, in就行相当于void ,out就有相应的返回值,比如varchar那么返回值就是字符型。
out一般要定义一个用户变量来存储输出的结果。

2、如果存储过程体仅仅只有一句话,begin end可以省略
存储过程体中的每条sql语句的结尾要求必须加分号。
存储过程的结尾可以使用delimiter重新设置。(等同于自己设置结束标记)

语法:
delimiter 结束标记

#DELIMITER关键字申明当前段分隔符,这样MySQL才会将";"当做存储过程中的代码,不会执行这些代码,用完了之后要把分隔符还原。
#在8.0以上版本已经可以不用设置结束标记了,会自动识别BEGIN和END。
#之所以要改变结束标记,是因为存储过程可以有多个begin end,程序不知道要在哪里结束,用分号又和存储体内的混淆,所以改变结束标记,表示这一整段是一个存储过程。

二、调用语法
CALL 存储过程名(实参列表);

1.空参列表
#案例:插入到admin表中五条记录
SELECT * FROM admin;
DELIMITER $    #8.0版本可以不用了。
CREATE PROCEDURE myp1()
BEGIN
  INSERT INTO admin(username,password) 
  VALUES('john1','0000'),('lily','0000'),('rose','0000'),('jack','0000'),('tom','0000');
END $   #直接用;就好
#调用
CALL myp1()$    #直接用;就好

2.创建带in模式参数的存储过程
#案例1:创建存储过程实现 根据女神名,查询对应的男神信息
CREATE PROCEDURE myp2(IN beautyName VARCHAR(20))
BEGIN
  SELECT bo.*
  FROM boys bo
  RIGHT JOIN beauty b ON bo.id= b.boyfriend_id
  WHERE b.name=beautyName;
END $    
#调用
CALL myp2('柳岩')$

#案例2 :创建存储过程实现,用户是否登录成功
CREATE PROCEDURE myp4(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
BEGIN
  DECLARE result INT DEFAULT 0;#声明并初始化
  SELECT COUNT(*) INTO result#赋值
  FROM admin
  WHERE admin.username = username
  AND admin.password = PASSWORD;
  SELECT IF(result>0,'成功','失败');#使用
END $
#调用
CALL myp4('张飞','8888')$

3.创建out 模式参数的存储过程
#案例1:根据输入的女神名,返回对应的男神名
CREATE PROCEDURE myp6(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20))
BEGIN
  SELECT bo.boyname INTO boyname
  FROM boys bo
  RIGHT JOIN
  beauty b ON b.boyfriend_id =bo.id
  WHERE b.name=beautyName ;
END $
call myp6('热巴',@name);
select @name;

#案例2:根据输入的女神名,返回对应的男神名和魅力值
CREATE PROCEDURE myp7(IN beautyName VARCHAR(20),OUT boyName VARCHAR(20),OUT usercp INT) 
BEGIN
  SELECT boys.boyname ,boys.usercp INTO boyname,usercp
  FROM boys 
  RIGHT JOIN
  beauty b ON b.boyfriend_id =boys.id
  WHERE b.name=beautyName ;
END $

#调用
#这里省略了定义变量,set @name;
CALL myp7('小昭',@name,@cp)$
SELECT @name,@cp$

4.创建带inout模式参数的存储过程
#案例1:传入a和b两个值,最终a和b都翻倍并返回
CREATE PROCEDURE myp8(INOUT a INT ,INOUT b INT)
BEGIN
  SET a=a*2;
  SET b=b*2;
END $
#调用
SET @m=10$
SET @n=20$
CALL myp8(@m,@n)$
SELECT @m,@n$

三、删除存储过程
语法:drop procedure 存储过程名;

DROP PROCEDURE p1;
DROP PROCEDURE p2,p3;  #错误的,只能一次删除一个。

四、查看存储过程的信息
DESC myp2 ;  # 错误的,存储过程不是结构
SHOW CREATE PROCEDURE  myp2;

五、修改存储信息
一般不改,直接删
函数
含义:一组预先编译好的SQL语句的集合,理解成批处理语句
1、提高代码的重用性。
2、简化操作。
3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率。

区别:
存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新。
函数:有且仅有1 个返回,适合做处理数据后返回一个结果。

一、创建语法
CREATE FUNCTION 函数名(参数列表) RETURNS 返回类型
BEGIN
  函数体
END;

注意:
1.参数列表 包含两部分:参数名 参数类型。
2.函数体:肯定会有return语句,如果没有会报错。
如果return语句没有放在函数体的最后也不报错,但不建议。
3.函数体中仅有一句话,则可以省略begin end4.使用 delimiter语句设置结束标记。

/*
mysql的设置默认是不允许创建函数。需要进行修改:
1、更改全局配置
SET GLOBAL log_bin_trust_function_creators = 1;  
有主从复制的时候 , 从机必须要设置,不然会导致主从同步失败
2、上面的动态设置的方式会在服务重启后失效,我们还可以更改配置文件my.cnf
log-bin-trust-function-creators=1   #重启服务生效
*/

/*
关于error 1418,是因为我们开启了bin-log, 我们就必须明确指明函数的类型,如果我们开启了二进制日志(bin-log), 那么我们就必须为我们的function指定一个参数。
其中对于function,下面几种参数类型里面,只有 DETERMINISTIC, NO SQL 和 READS SQL DATA 被支持。
这样一来相当于明确的告知MySQL服务器这个函数不会修改数据:
1 DETERMINISTIC 不确定的  
2 NO SQL 没有SQl语句,当然也不会修改数据  
3 READS SQL DATA 只是读取数据,当然也不会修改数据  
4 MODIFIES SQL DATA 要修改数据  
5 CONTAINS SQL 包含了SQL语句
以上参数类型在定义的时候需要加在‘returns 返回类型’ 的后面

select @@global.log_bin_trust_function_creators;   
这个变量控制是否可以信任存储函数创建者,不会创建写入二进制日志引起不安全事件的存储函数。如果设置为0(默认值),用户不得创建或修改存储函数,除非它们具有除CREATE ROUTINE或ALTER ROUTINE特权之外的SUPER权限。 设置为0还强制使用DETERMINISTIC特性或READS SQL DATA或NO SQL特性声明函数的限制。 如果变量设置为1,MySQL不会对创建存储函数实施这些限制。 此变量也适用于触发器的创建。
为什么MySQL有这样的限制呢? 因为二进制日志的一个重要功能是用于主从复制,而存储函数有可能导致主从的数据不一致。所以当开启二进制日志后,参数log_bin_trust_function_creators就会生效,限制存储函数的创建、修改、调用。
*/

二、调用语法
SELECT 函数名(参数列表)

#------------------------------案例演示----------------------------

#1.无参有返回

#案例:返回公司的员工个数
CREATE FUNCTION myf1() RETURNS INT
BEGIN
  DECLARE c INT DEFAULT 0;#定义局部变量
  SELECT COUNT(*) INTO c#赋值
  FROM employees;
  RETURN c;
END $
SELECT myf1()$

#2.有参有返回

#案例1:根据员工名,返回它的工资
CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLE
BEGIN
  SET @sal=0;#定义用户变量 
  SELECT salary INTO @sal   #赋值
  FROM employees
  WHERE last_name = empName;
  RETURN @sal;
END $
SELECT myf2('k_ing') $

#案例2:根据部门名,返回该部门的平均工资
CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLE
BEGIN
  DECLARE sal DOUBLE ;
  SELECT AVG(salary) INTO sal
  FROM employees e
  JOIN departments d ON e.department_id = d.department_id
  WHERE d.department_name=deptName;
  RETURN sal;
END $
SELECT myf3('IT')$

三、查看函数
SHOW CREATE FUNCTION 函数名;

四、删除函数
DROP FUNCTION 函数名;

#案例:创建函数,实现传入两个float,返回二者之和
CREATE FUNCTION test_fun1(num1 FLOAT,num2 FLOAT) RETURNS FLOAT
BEGIN
  DECLARE SUM FLOAT DEFAULT 0;
  SET SUM=num1+num2;
  RETURN SUM;
END $
SELECT test_fun1(1,2)$
案例:
#一、创建存储过程实现传入用户名和密码,插入到admin表中
CREATE PROCEDURE test_pro1(IN username VARCHAR(20),IN loginPwd VARCHAR(20))
BEGIN
  INSERT INTO admin(admin.username,PASSWORD)
  VALUES(username,loginpwd);
END $

#二、创建存储过程实现传入女神编号,返回女神名称和女神电话
CREATE PROCEDURE test_pro2(IN id INT,OUT NAME VARCHAR(20),OUT phone VARCHAR(20))
BEGIN
  SELECT b.name ,b.phone INTO NAME,phone
  FROM beauty b
  WHERE b.id = id;
END $

#三、创建存储存储过程或函数实现传入两个女神生日,返回大小
CREATE PROCEDURE test_pro3(IN birth1 DATETIME,IN birth2 DATETIME,OUT result INT)
BEGIN
  SELECT DATEDIFF(birth1,birth2) INTO result;
END $

#四、创建存储过程或函数实现传入一个日期,格式化成xx年xx月xx日并返回
CREATE PROCEDURE test_pro4(IN mydate DATETIME,OUT strDate VARCHAR(50))
BEGIN
  SELECT DATE_FORMAT(mydate,'%y年%m月%d日') INTO strDate;
END $
CALL test_pro4(NOW(),@str)$
SELECT @str $

#五、创建存储过程或函数实现传入女神名称,返回:女神 and 男神  格式的字符串
如 传入 :小昭
返回: 小昭 AND 张无忌

DROP PROCEDURE test_pro5 $
CREATE PROCEDURE test_pro5(IN beautyName VARCHAR(20),OUT str VARCHAR(50))
BEGIN
  SELECT CONCAT(beautyName,' and ',IFNULL(boyName,'null')) INTO str
  FROM boys bo
  RIGHT JOIN beauty b ON b.boyfriend_id =bi.id
  WHERE b.name=beautyName;
END $
CALL test_pro5('柳岩',@str)$
SELECT @str $

#六、创建存储过程或函数,根据传入的条目数和起始索引,查询beauty表的记录
DROP PROCEDURE test_pro6$
CREATE PROCEDURE test_pro6(IN startIndex INT,IN size INT)
BEGIN
  SELECT * FROM beauty LIMIT startIndex,size;
END $
CALL test_pro6(3,5)$

十一、流程控制结构

顺序结构:程序从上往下依次执行
分支结构:程序从两条或多条路径中选择一条去执行
循环结构:程序在满足一定条件的基础上,重复执行一段代码
分支结构
一、if函数
语法:
if(条件,1,值2)  #条件成立,则返回值1,不成立则返回值2

功能:实现双分支
应用在begin end中或外面

二、case结构
特点:
①可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,BEGIN END中或者外面都行。
 也可以作为独立的语句去使用,只能放在BEGIN END 中。
②如果when中的值满足或者条件成立,则执行对应的then后面的语句,并且结束case。
 如果都不满足,则执行else中的值或语句。
③else可以省略,如果else省略了,并且所有的when条件都不满足,则返回null;

语法:
1.作为独立的语句
情况1:类似于switch,一般用于等值判断
case 变量或表达式或字段
when 要判断的值1 then 返回的语句1;
when 要判断的值2 then 返回的语句2;
...
else 返回的语句n;
end case;

情况2:类似于多重if,一般用于区间判断
case 
when 要判断的条件1 then 返回的语句1;
when 要判断的条件2 then 返回的语句2;
...
else 返回的语句n;
end case;

2.作为表达式
情况一:类似于switch,一般用于等值判断
case 表达式
when1 then1
when2 then2else 值n
end ;

情况二:类似于多重if,一般用于区间判断
case
when 条件1 then1
when 条件2 then2else 值n
end ;

三、if结构
语法:
if 条件1 then 语句1;
elseif 条件2 then 语句2;
...
else 语句n;
end if;

功能:类似于多重if
只能应用在begin end
#案例1:创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
CREATE FUNCTION test_if(score FLOAT) RETURNS CHAR
BEGIN
  DECLARE ch CHAR DEFAULT 'A';
  IF score>90 THEN SET ch='A';
  ELSEIF score>80 THEN SET ch='B';
  ELSEIF score>60 THEN SET ch='C';
  ELSE SET ch='D';
  END IF;
  RETURN ch;
END $
SELECT test_if(87)$

#案例2:创建存储过程,如果工资<2000,则删除,如果5000>工资>2000,则涨工资1000,否则涨工资500
CREATE PROCEDURE test_if_pro(IN sal DOUBLE)
BEGIN
  IF sal<2000 THEN DELETE FROM employees WHERE employees.salary=sal;
  ELSEIF sal>=2000 AND sal<5000 THEN UPDATE employees SET salary=salary+1000 WHERE employees.`salary`=sal;
  ELSE UPDATE employees SET salary=salary+500 WHERE employees.`salary`=sal;
  END IF;
END $
CALL test_if_pro(2100)$

#案例1:创建函数,实现传入成绩,如果成绩>90,返回A,如果成绩>80,返回B,如果成绩>60,返回C,否则返回D
CREATE FUNCTION test_case(score FLOAT) RETURNS CHAR
BEGIN 
  DECLARE ch CHAR DEFAULT 'A';
  CASE 
  WHEN score>90 THEN SET ch='A';
  WHEN score>80 THEN SET ch='B';
  WHEN score>60 THEN SET ch='C';
  ELSE SET ch='D';
  END CASE;
  RETURN ch;
END $
SELECT test_case(56)$
循环结构
分类:whilelooprepeat

循环控制:
iterate类似于continue,继续,结束本次循环,继续下一次
leave类似于break,跳出,结束当前所在的循环


①这三种循环都可以省略名称,但如果循环中添加了循环控制语句(leaveiterate)则必须添加名称

②loop 一般用于实现简单的死循环
 while 先判断后执行
 repeat 先执行后判断,无条件至少执行一次

1.while
语法:
【标签:】while 循环条件 do
  循环体;
end while【 标签】;

2.loop
语法:
【标签:】loop
  循环体;
end loop 【标签】;
可以用来模拟简单的死循环

3.repeat
语法:
【标签:】repeat
  循环体;
until 结束循环的条件
end repeat 【标签】;

#1.没有添加循环控制语句
#案例:批量插入,根据次数插入到admin表中多条记录
DROP PROCEDURE pro_while1$
CREATE PROCEDURE pro_while1(IN insertCount INT)
BEGIN
  DECLARE i INT DEFAULT 1;
  WHILE i<=insertCount DO
    INSERT INTO admin(username,password) VALUES(CONCAT('Rose',i),'666');
    SET i=i+1;
  END WHILE;
END $
CALL pro_while1(100)$

#2.添加leave语句
#案例:批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止
TRUNCATE TABLE admin$
DROP PROCEDURE test_while1$
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
  DECLARE i INT DEFAULT 1;
  a:WHILE i<=insertCount DO
    INSERT INTO admin(username,password) VALUES(CONCAT('xiaohua',i),'0000');
    IF i>=20 THEN LEAVE a;
    END IF;
    SET i=i+1;
  END WHILE a;
END $
CALL test_while1(100)$

#3.添加iterate语句
#案例:批量插入,根据次数插入到admin表中多条记录,只插入偶数次
TRUNCATE TABLE admin$
DROP PROCEDURE test_while1$
CREATE PROCEDURE test_while1(IN insertCount INT)
BEGIN
  DECLARE i INT DEFAULT 0;
  a:WHILE i<=insertCount DO
    SET i=i+1;
    IF MOD(i,2)!=0 THEN ITERATE a;
    END IF;
    INSERT INTO admin(username,password) VALUES(CONCAT('xiaohua',i),'0000');
  END WHILE a;
END $
CALL test_while1(100)$
案例:
/*一、已知表stringcontent
其中字段:
id 自增长
content varchar(20)
向该表插入指定个数的,随机的字符串
*/
DROP TABLE IF EXISTS stringcontent;
CREATE TABLE stringcontent(
  id INT PRIMARY KEY AUTO_INCREMENT,
  content VARCHAR(20)
);
DELIMITER $
CREATE PROCEDURE test_randstr_insert(IN insertCount INT)
BEGIN
  DECLARE i INT DEFAULT 1;
  DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
  DECLARE startIndex INT;#代表初始索引
  DECLARE len INT;#代表截取的字符长度
  WHILE i<=insertcount DO
    SET startIndex=FLOOR(RAND()*26+1);#代表初始索引,随机范围1-26
    SET len=FLOOR(RAND()*(20-startIndex+1)+1);#代表截取长度,随机范围1-(20-startIndex+1)
    INSERT INTO stringcontent(content) VALUES(SUBSTR(str,startIndex,len));
    SET i=i+1;
  END WHILE;
END $
CALL test_randstr_insert(10)$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值