精华内容
下载资源
问答
  • JavaScript笔记:函数作用作用

    1、函数中的作用域

    考虑如下的代码:

    function foo(a) { 
        var b = 2;
        // 一些代码
        function bar() { 
            // ...
        }
        // 更多的代码 
        var c = 3;
    }

    在这个代码片段中,foo(..) 的作用域中包含了标识符 a、b、c 和 bar。

    bar(..) 拥有自己的作用域。全局作用域也有自己的作用域气泡,它只包含了一个标识符:foo。

    由于标识符 a、b、c 和 bar 都附属于 foo(..) 的作用域,因此无法从 foo(..) 的外部 对它们进行访问。也就是说,这些标识符全都无法从全局作用域中进行访问,因此下面的代码会导致 ReferenceError 错误:

    bar(); // 失败
    console.log( a, b, c ); // 三个全都失败

    函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)。这种设计方案是非常有用的,能充分利用 JavaScript 变量可以根据需要改变值类型的“动态”特性。

    但与此同时,如果不细心处理那些可以在整个作用域范围内被访问的变量,可能会带来意想不到的问题。

    2、隐藏内部实现

    对函数的传统认知就是先声明一个函数,然后再向里面添加代码。但反过来想也可以带来一些启示:从所写的代码中挑选出一个任意的片段,然后用函数声明对它进行包装,实际上就是把这些代码“隐藏”起来了。

    实际的结果就是在这个代码片段的周围创建了一个作用域气泡,也就是说这段代码中的任何声明(变量或函数)都将绑定在这个新创建的包装函数的作用域中,而不是先前所在的作用域中。换句话说,可以把变量和函数包裹在一个函数的作用域中,然后用这个作用域来“隐藏”它们。

    为什么“隐藏”变量和函数是一个有用的技术?

    有很多原因促成了这种基于作用域的隐藏方法。它们大都是从最小特权原则中引申出来的,也叫最小授权或最小暴露原则。

    这个原则是指在软件设计中,应该最小限度地暴露必要内容,而将其他内容都“隐藏”起来,比如某个模块或对象的 API 设计。

    这个原则可以延伸到如何选择作用域来包含变量和函数。如果所有变量和函数都在全局作用域中,当然可以在所有的内部嵌套作用域中访问到它们。但这样会破坏前面提到的最小特权原则,因为可能会暴漏过多的变量或函数,而这些变量或函数本应该是私有的,正确的代码应该是可以阻止对这些变量或函数进行访问的。

    function doSomething(a) {
        b = a + doSomethingElse( a * 2 );
        console.log( b * 3 );
    }
    function doSomethingElse(a) { 
        return a - 1;
    }
    var b;
    doSomething( 2 ); // 15

    在这个代码片段中,变量 b 和函数 doSomethingElse(..) 应该是 doSomething(..) 内部具体实现的“私有”内容。给予外部作用域对 b 和 doSomethingElse(..) 的“访问权限”不仅没有必要,而且可能是“危险”的,因为它们可能被有意或无意地以非预期的方式使用, 从而导致超出了 doSomething(..) 的适用条件。更“合理”的设计会将这些私有的具体内容隐藏在 doSomething(..) 内部:

    function doSomething(a) { 
        function doSomethingElse(a) {
            return a - 1; 
        }
        var b;
        b = a + doSomethingElse( a * 2 );
        console.log( b * 3 );
    }
    doSomething( 2 ); // 15

    现在,b 和 doSomethingElse(..) 都无法从外部被访问,而只能被 doSomething(..) 所控制。功能性和最终效果都没有受影响,但是设计上将具体内容私有化了,设计良好的软件都会 依此进行实现。

    规避冲突:

    “隐藏”作用域中的变量和函数所带来的另一个好处,是可以避免同名标识符之间的冲突, 两个标识符可能具有相同的名字但用途却不一样,无意间可能造成命名冲突。冲突会导致变量的值被意外覆盖。

    1、全局命名空间

    变量冲突的一个典型例子存在于全局作用域中。当程序中加载了多个第三方库时,如果它们没有妥善地将内部私有的函数或变量隐藏起来,就会很容易引发冲突。

    这些库通常会在全局作用域中声明一个名字足够独特的变量,通常是一个对象。这个对象被用作库的命名空间,所有需要暴露给外界的功能都会成为这个对象(命名空间)的属性,而不是将自己的标识符暴漏在顶级的词法作用域中。

    2、模块管理

    另外一种避免冲突的办法和现代的模块机制很接近,就是从众多模块管理器中挑选一个来使用。使用这些工具,任何库都无需将标识符加入到全局作用域中,而是通过依赖管理器 的机制将库的标识符显式地导入到另外一个特定的作用域中。

    显而易见,这些工具并没有能够违反词法作用域规则的“神奇”功能。它们只是利用作用域的规则强制所有标识符都不能注入到共享作用域中,而是保持在私有、无冲突的作用域中,这样可以有效规避掉所有的意外冲突。

    3、函数作用域

    在任意代码片段外部添加包装函数,可以将内部的变量和函数定义“隐藏”起来,外部作用域无法访问包装函数内部的任何内容。

    var a = 2;
    function foo() { 
        var a = 3; 
        console.log( a ); // 3
    } 
    foo();
    console.log( a ); // 2

    虽然这种技术可以解决一些问题,但是它并不理想,因为会导致一些额外的问题。

    首先,必须声明一个具名函数 foo(),意味着 foo 这个名称本身“污染”了所在作用域(在这个例子中是全局作用域)。
    其次,必须显式地通过函数名(foo())调用这个函数才能运行其中的代码。

    如果函数不需要函数名(或者至少函数名可以不污染所在作用域),并且能够自动运行,这将会更加理想。

    JavaScript 提供了能够同时解决这两个问题的方案。

    (function foo(){ 
        var a = 3;
        console.log( a ); // 3 
    })();

    首先,包装函数的声明以 (function… 而不仅是以 function… 开始。尽管看上去这并不是一个很显眼的细节,但实际上却是非常重要的区别。函数会被当作函数表达式而不是一个标准的函数声明来处理。

    区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果 function 是声明中 的第一个词,那么就是一个函数声明,否则就是一个函数表达式。

    函数声明和函数表达式之间最重要的区别是它们的名称标识符将会绑定在何处。 比较一下前面两个代码片段。第一个片段中 foo 被绑定在所在作用域中,可以直接通过 foo() 来调用它。第二个片段中 foo 被绑定在函数表达式自身的函数中而不是所在作用域中。

    匿名和具名

    对于函数表达式你最熟悉的场景可能就是回调参数了,比如:

     setTimeout( function() {
        console.log("I waited 1 second!");
    }, 1000 );

    这叫作匿名函数表达式,因为 function().. 没有名称标识符。函数表达式可以是匿名的,而函数声明则不可以省略函数名——在 JavaScript 的语法中这是非法的。

    匿名函数表达式书写起来简单快捷,很多库和工具也倾向鼓励使用这种风格的代码。但是它也有几个缺点需要考虑。

    1. 匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难。
    2. 如果没有函数名,当函数需要引用自身时只能使用已经过期的arguments.callee引用, 比如在递归中。另一个函数需要引用自身的例子,是在事件触发后事件监听器需要解绑自身。
    3. 匿名函数省略了对于代码可读性/可理解性很重要的函数名。一个描述性的名称可以让代码不言自明。

    行内函数表达式非常强大且有用——匿名和具名之间的区别并不会对这点有任何影响。给函数表达式指定一个函数名可以有效解决以上问题。始终给函数表达式命名是一个最佳实践:

    setTimeout( function timeoutHandler() { // <-- 快看,有名字了! 
        console.log( "I waited 1 second!" );
    }, 1000 );

    立即执行函数表达式

    var a = 2;
    (function foo() { 
        var a = 3;
        console.log( a ); // 3
    })();
    console.log( a ); // 2

    由于函数被包含在一对 ( ) 括号内部,因此成为了一个表达式,通过在末尾加上另外一个 ( ) 可以立即执行这个函数,比如 (function foo(){ .. })()。第一个 ( ) 将函数变成表达式,第二个 ( ) 执行了这个函数。

    这种模式很常见,几年前社区给它规定了一个术语:IIFE,代表立即执行函数表达式 (Immediately Invoked Function Expression)。

    相较于传统的 IIFE 形式,很多人都更喜欢另一个改进的形式:(function(){ .. }())。仔细观察其中的区别。第一种形式中函数表达式被包含在( )中,然后在后面用另一个()括号来调用。第二种形式中用来调用的()括号被移进了用来包装的( )括号中。

    这两种形式在功能上是一致的。选择哪个全凭个人喜好。

    IIFE 的另一个非常普遍的进阶用法是把它们当作函数调用并传递参数进去。

    var a = 2;
    (function IIFE( global ) {
        var a = 3;
        console.log( a ); // 3 
        console.log( global.a ); // 2
    })( window );
    console.log( a ); // 2

    我们将 window 对象的引用传递进去,但将参数命名为 global,因此在代码风格上对全局对象的引用变得比引用一个没有“全局”字样的变量更加清晰。当然可以从外部作用域传递任何你需要的东西,并将变量命名为任何你觉得合适的名字。这对于改进代码风格是非常有帮助的。

    这个模式的另外一个应用场景是解决 undefined 标识符的默认值被错误覆盖导致的异常(虽然不常见)。将一个参数命名为 undefined,但是在对应的位置不传入任何值,这样就可以保证在代码块中 undefined 标识符的值真的是 undefined:

    undefined = true; // 给其他代码挖了一个大坑!绝对不要这样做! 
    (function IIFE( undefined ) {
        var a;
        if (a === undefined) {
            console.log( "Undefined is safe here!" );
        }
    })();

    IIFE 还有一种变化的用途是倒置代码的运行顺序,将需要运行的函数放在第二位,在 IIFE 执行之后当作参数传递进去。

    这种模式在UMD(Universal Module Definition)项目中被广泛使用。尽管这种模式略显冗长,但有些人认为它更易理解。

    var a = 2;
    (function IIFE( def ) { 
        def( window );
    })(function def( global ) {
        var a = 3;
        console.log( a ); // 3 
        console.log( global.a ); // 2
    });

    4、块作用域

    尽管函数作用域是最常见的作用域单元,当然也是现行大多数 JavaScript 中最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀、简洁的代码。

    除 JavaScript 外的很多编程语言都支持块作用域,因此其他语言的开发者对于相关的思维方式会很熟悉,但是对于主要使用 JavaScript 的开发者来说,这个概念会很陌生。

    下面两段代码中的变量,都会污染全局作用域。

    for (var i=0; i<10; i++) { 
        console.log( i );
    }
    
    var foo = true;
    if (foo) {
        var bar = foo * 2;
        bar = something( bar ); 
        console.log( bar );
    }

    表面上看 JavaScript 并没有块作用域的相关功能。

    除非你更加深入地研究。

    with

    它不仅是一个难于理解的结构,同时也是块作用域的一个例子(块作用域的一种形式),用 with 从对象中创建出的作用域仅在 with 声明中而非外部作用域中有效。

    try/catch

    JavaScript 的 ES3 规范中规定 try/catch 的 catch 分句会创建一个块作用域,其中声明的变量仅在 catch 内部有效。

    try {
        undefined(); // 执行一个非法操作来强制制造一个异常
    }
    catch (err) {
        console.log( err ); // 能够正常执行! 
    }
    
    console.log( err ); // ReferenceError: err not found

    正如你所看到的,err 仅存在 catch 分句内部,当试图从别处引用它时会抛出错误。

    let

    ES6 引入了新的 let 关键字,提供了除 var 以外的另一种变量声明方式。

    let 关键字可以将变量绑定到所在的任意作用域中(通常是 { .. } 内部)。换句话说,let为其声明的变量隐式地了所在的块作用域。

    var foo = true;
    if (foo) {
        let bar = foo * 2;
        console.log( bar );
    }
    console.log( bar ); // ReferenceError

    但是使用 let 进行的声明不会在块作用域中进行提升。声明的代码被运行之前,声明并不“存在”。
    (提升是指声明会被视为存在于其所出现的作用域的整个范围内。)

    块作用域非常有用的原因和闭包及回收内存垃圾的回收机制相关。

    function process(data) {
        // 在这里做点有趣的事情
    }
    var someReallyBigData = { .. };
    process( someReallyBigData );
    
    var btn = document.getElementById( "my_button" );
    btn.addEventListener( "click", function click(evt) {
        console.log("button clicked");
    }, /*capturingPhase=*/false );

    click 函数的点击回调并不需要 someReallyBigData 变量。理论上这意味着当 process(..) 执 行后,在内存中占用大量空间的数据结构就可以被垃圾回收了。但是,由于 click 函数形成 了一个覆盖整个作用域的闭包,JavaScript 引擎极有可能依然保存着这个结构(取决于具体实现)。

    块作用域可以打消这种顾虑,可以让引擎清楚地知道没有必要继续保存 someReallyBigData 了:

    function process(data) {
        // 在这里做点有趣的事情
    }
    // 在这个块中定义的内容可以销毁了! 
    {
        let someReallyBigData = { .. }; 
        process( someReallyBigData );
    }
    var btn = document.getElementById( "my_button" );
    btn.addEventListener( "click", function click(evt){
        console.log("button clicked");
    }, /*capturingPhase=*/false );

    let循环

    一个 let 可以发挥优势的典型例子就是之前讨论的 for 循环。

    for (let i=0; i<10; i++) { 
        console.log( i );
    }
    console.log( i ); // ReferenceError

    const

    除了 let 以外,ES6 还引入了 const,同样可以用来创建块作用域变量,但其值是固定的 (常量)。之后任何试图修改值的操作都会引起错误。

    var foo = true;
    if (foo) {
        var a = 2;
        const b = 3; //包含在if中的块作用域常量
        a = 3; //正常!
        b = 4; //错误! 
    }
    console.log( a ); // 3
    console.log( b ); // ReferenceError!
    展开全文
  • Mysql个人学习笔记

    万次阅读 多人点赞 2019-09-04 14:35:57
    好处:1、隐藏了实现细节 2、提高代码的重用性 调用:select 函数名(实参列表) 【from 表】; 特点: ①叫什么(函数名) ②干什么(函数功能) 分类: #一、字符函数 #1.length 获取参数值的字节个数 SELECT ...

    mysql

    进阶一-基础

    #进阶1:基础查询
    /*
    语法:
    select 查询列表 from 表名;
    类似于:System.out.println(打印东西);
    特点:
    1、查询列表可以是:表中的字段、常量值、表达式、函数
    2、查询的结果是一个虚拟的表格
    */
    
    USE myemployees;   #使用这个数据库
    
    #1.查询表中的单个字段
    
    SELECT last_name FROM employees;
    
    #2.查询表中的多个字段
    SELECT last_name,salary,email FROM employees;
    
    #3.查询表中的所有字段
    
    #方式一:
    SELECT 
        `employee_id`,
        `first_name`,
        `last_name`,
        `phone_number`,
        `last_name`,
        `job_id`,
        `phone_number`,
        `job_id`,
        `salary`,
        `commission_pct`,
        `manager_id`,
        `department_id`,
        `hiredate` 
    FROM
        employees ;
    #方式二:  
     SELECT * FROM employees;
     
     #4.查询常量值
     SELECT 100;
     SELECT 'john';
     
     #5.查询表达式
     SELECT 100%98;
     
     #6.查询函数 
     SELECT VERSION();
      
     #7.起别名
     /*
     ①便于理解
     ②如果要查询的字段有重名的情况,使用别名可以区分开来 
     */
     #方式一:使用as
    SELECT 100%98 AS 结果;
    SELECT last_name AS 姓,first_name AS 名 FROM employees;
    
    #方式二:使用空格
    SELECT last_name 姓,first_name 名 FROM employees;
    
    #案例:查询salary,显示结果为 out put
    SELECT salary AS "out put" FROM employees;
    
    #8.去重
    
    #案例:查询员工表中涉及到的所有的部门编号
    SELECT DISTINCT department_id FROM employees;
    
    #9.+号的作用
    /*
    java中的+号:
    ①运算符,两个操作数都为数值型
    ②连接符,只要有一个操作数为字符串
    mysql中的+号:
    仅仅只有一个功能:运算符
    
    select 100+90; 两个操作数都为数值型,则做加法运算
    select '123'+90;只要其中一方为字符型,试图将字符型数值转换成数值型
    			如果转换成功,则继续做加法运算
    select 'john'+90;	如果转换失败,则将字符型数值转换成0
    select null+10; 只要其中一方为null,则结果肯定为null
    */
    #案例:查询员工名和姓连接成一个字段,并显示为 姓名
    SELECT CONCAT('a','b','c') AS 结果;
    
    SELECT 
    	CONCAT(last_name,first_name) AS 姓名
    FROM
    	employees;
    

    进阶二-条件查询

    #进阶2:条件查询
    /*
    语法:
    	select 
    		查询列表
    	from
    		表名
    	where
    		筛选条件;
    分类:
    	一、按条件表达式筛选	
    	简单条件运算符:> < = != <> >= <=	
    	二、按逻辑表达式筛选
    	逻辑运算符:
    	作用:用于连接条件表达式
    		&& || !
    		and or not		
    	&&和and:两个条件都为true,结果为true,反之为false
    	||或or: 只要有一个条件为true,结果为true,反之为false
    	!或not: 如果连接的条件本身为false,结果为true,反之为false	
    	三、模糊查询
    		like
    		between and
    		in
    		is 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;
    #三、模糊查询
    /*
    like	
    between and
    in
    is null|is not null
    
    */
    #1.like
    /*
    特点:
    ①一般和通配符搭配使用
    	通配符:
    	% 任意多个字符,包含0个字符
    	_ 任意单个字符
    *、
    
    #案例1:查询员工名中包含字符a的员工信息
    
    select 
    	*
    from
    	employees
    where
    	last_name like '%a%';#abc
    #案例2:查询员工名中第三个字符为e,第五个字符为a的员工名和工资
    select
    	last_name,
    	salary
    FROM
    	employees
    WHERE
    	last_name LIKE '__n_l%';
    	
    #案例3:查询员工名中第二个字符为_的员工名
    SELECT
    	last_name
    FROM
    	employees
    WHERE
    	last_name LIKE '_$_%' ESCAPE '$';
    #2.between and
    /*
    ①使用between and 可以提高语句的简洁度
    ②包含临界值
    ③两个临界值不要调换顺序
    */
    
    #案例1:查询员工编号在100到120之间的员工信息
    SELECT
    	*
    FROM
    	employees
    WHERE
    	employee_id >= 100 AND employee_id<=120;
    #----------------------
    SELECT
    	*
    FROM
    	employees
    WHERE
    	employee_id BETWEEN 100 AND 120;
    
    #3.in
    /*
    含义:判断某字段的值是否属于in列表中的某一项
    特点:
    	①使用in提高语句简洁度
    	②in列表的值类型必须一致或兼容
    	③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
    /*
    =或<>不能用于判断null值
    is null或is not null 可以判断null值
    */
    
    #案例1:查询没有奖金的员工名和奖金率
    SELECT
    	last_name,
    	commission_pct
    FROM
    	employees
    WHERE
    	commission_pct IS NULL;
    
    #案例1:查询有奖金的员工名和奖金率
    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;	
    #is null pk <=>
    
    IS NULL:仅仅可以判断NULL值,可读性较高,建议使用
    <=>    :既可以判断NULL值,又可以判断普通的数值,可读性较低
    

    进阶三-排序

    #进阶3:排序查询
    /*
    语法:
    select 查询列表
    from 表名
    【where  筛选条件】
    order by 排序的字段或表达式;
    特点:
    1、asc代表的是升序,可以省略
    desc代表的是降序
    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 salary*12*(1+IFNULL(commission_pct,0)) DESC;
    #4、按别名排序
    #案例:查询员工信息 按年薪升序
    SELECT *,salary*12*(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;
    

    进阶四-函数

    #流程函数
    if(条件,值1,值2)       =======类似三元运算 条件true则值1,否则值2
    case 字段               ============ 类似switch
    when 条件  then  结果
    else
    end
    #分组函数
     max、min、count可以处理任何类型
     忽略null
     datediff(now(),date):相差天数
     MAX(salary)-MIN(salary) DIFFRENCE:相差工资
    
    #进阶4:常见函数
    概念:类似于java的方法,将一组逻辑语句封装在方法体中,对外暴露方法名
    好处:1、隐藏了实现细节  2、提高代码的重用性
    调用:select 函数名(实参列表) 【from 表】;
    特点:
    	①叫什么(函数名)
    	②干什么(函数功能)
    分类:	
    #一、字符函数
    #1.length 获取参数值的字节个数
    SELECT LENGTH('john');
    SELECT LENGTH('张三丰hahaha');
    #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('aa' FROM 'aaaaaaaaa张aaaaaaaaaaaa翠山aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')  AS out_put;
    #7.lpad 用指定的字符实现左填充指定长度
    SELECT LPAD('殷素素',2,'*') AS out_put;
    #8.rpad 用指定的字符实现右填充指定长度
    SELECT RPAD('殷素素',12,'ab') AS out_put;
    #9.replace 替换
    SELECT REPLACE('周芷若周芷若周芷若周芷若张无忌爱上了周芷若','周芷若','赵敏') AS out_put;
    #二、数学函数
    #round 四舍五入
    SELECT ROUND(-1.55);
    SELECT ROUND(1.567,2);
    #ceil 向上取整,返回>=该参数的最小整数
    SELECT CEIL(-1.02);
    #floor 向下取整,返回<=该参数的最大整数
    SELECT FLOOR(-9.99);
    #truncate 截断
    SELECT TRUNCATE(1.69999,1);
    #mod取余
    /*
    mod(a,b) :  a-(a/b*b) 先除后乘
    mod(-10,-3):-10- (-10)/(-3)*(-3)=-1
    */
    SELECT MOD(10,-3);
    SELECT 10%3;
    #三、日期函数
    #now 返回当前系统日期+时间
    SELECT NOW();
    #curdate 返回当前系统日期,不包含时间
    SELECT CURDATE();
    #curtime 返回当前时间,不包含日期
    SELECT CURTIME();
    #可以获取指定的部分,年、月、日、小时、分钟、秒
    SELECT YEAR(NOW()) 年;
    SELECT YEAR('1998-1-1') 年;
    SELECT YEAR(hiredate) 年 FROM employees;
    SELECT MONTH(NOW()) 月;
    SELECT MONTHNAME(NOW()) 月;
    #str_to_date 将字符通过指定的格式转换成日期
    SELECT STR_TO_DATE('1998-3-2','%Y-%c-%d') AS out_put;
    #查询入职日期为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');
    #date_format 将日期转换成字符
    SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS out_put;
    #查询有奖金的员工名和入职日期(xx月/xx日 xx年)
    SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %y年') 入职日期
    FROM employees
    WHERE commission_pct IS NOT NULL;
    #四、其他函数
    SELECT VERSION();
    SELECT DATABASE();
    SELECT USER();
    
    #五、流程控制函数
    #1.if函数: if else 的效果
    SELECT IF(10<5,'大','小');   #===========三元运算
    SELECT last_name,commission_pct,IF(commission_pct IS NULL,'没奖金,呵呵','有奖金,嘻嘻') 备注
    FROM employees;
    #2.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;
    #3.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、以上分组函数都忽略null值
    3、可以和distinct搭配实现去重的运算
    4、count函数的单独介绍
    一般使用count(*)用作统计行数
    5、和分组函数一同查询的字段要求是group by后的字段
    */
    #1、简单 的使用
    SELECT SUM(salary) 和,AVG(salary) 平均,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;
    效率:
    MYISAM存储引擎下  ,COUNT(*)的效率高
    INNODB存储引擎下,COUNT(*)和COUNT(1)的效率差不多,比COUNT(字段)要高一些
    #6、和分组函数一同查询的字段有限制(一般和group by一起)
    SELECT AVG(salary),employee_id  FROM employees;
    

    进阶五-分组

    select 查询列表
    from 表
    【where 筛选条件】
    group by 分组的字段
    【having 筛选条件】
    【order by 排序的字段】;
    
    
    #进阶5:分组查询
    /*
    语法:
    select 查询列表
    from 表
    【where 筛选条件】
    group by 分组的字段
    【order by 排序的字段】;
    特点:
    1、和分组函数一同查询的字段必须是group by后出现的字段
    问题1:分组函数做筛选能不能放在where后面
    答:不能
    问题2:where——group by——having
    一般来讲,能用分组前筛选的,尽量使用分组前筛选,提高效率
    3、分组可以按单个字段也可以按多个字段
    4、可以搭配着排序使用
    */
    #引入:查询每个部门的员工个数
    
    SELECT COUNT(*) FROM employees WHERE department_id=90;
    #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的领导编号和最低工资
    MIN(salary)>5000;
    manager_id>102
        |
    SELECT manager_id,MIN(salary)
    FROM employees
    GROUP BY manager_id
    	|
    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:连接查询
    /*
    含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询
    
    笛卡尔乘积现象:表1 有m行,表2有n行,结果=m*n行
    
    发生原因:没有有效的连接条件
    如何避免:添加有效的连接条件	
    	按功能分类:
    		内连接:
    			等值连接
    			非等值连接
    			自连接
    		外连接:
    			左外连接
    			右外连接
    			全外连接
    */
    #案例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`;
    #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;
    
    #2、非等值连接
    
    #案例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';
    
    #3、自连接
    
    #案例:查询 员工名和上级的名称
    
    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`;
    #二、sql99语法
    
    语法:
    	select 查询列表
    	from 表1 别名 【连接类型】
    	join 表2 别名 
    	on 连接条件
    	【where 筛选条件】
    	【group by 分组】
    	【having 筛选条件】
    	【order by 排序列表】
    	
    
    分类:
    内连接(★):inner
    外连接
    	左外(★):left 【outer】
    	右外(★):right 【outer】
    	全外:full【outer】
    交叉连接:cross 
    #一)内连接
    
    语法:
    
    select 查询列表
    from 表1 别名
    inner join 表2 别名
    on 连接条件;
    特点:
    ①添加排序、分组、筛选
    ②inner可以省略
    
    #案例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 b.*,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;
     #全外
     USE girls;
     SELECT b.*,bo.*
     FROM beauty b
     FULL OUTER JOIN boys bo
     ON b.`boyfriend_id` = bo.id;
    
    

    进阶七-子查询

    select语句中(select)
    
    #进阶7:子查询
    
    含义:
    出现在其他语句中的select语句,称为子查询或内查询
    外部的查询语句,称为主查询或外查询
    
    分类:
    按子查询出现的位置:
    	select后面:
    		仅仅支持标量子查询	
    	from后面:
    		支持表子查询
    	where或having后面:★
    		标量子查询(单行) √
    		列子查询  (多行) √		
    		行子查询
    		
    	exists后面(相关子查询)
    		表子查询
    
    #案例2:返回job_id与141号员工相同,salary比143号员工多的员工 姓名,job_id 和工资
    
    #①查询141号员工的job_id
    SELECT job_id
    FROM employees
    WHERE employee_id = 141
    
    #②查询143号员工的salary
    SELECT salary
    FROM employees
    WHERE employee_id = 143
    
    #③查询员工的姓名,job_id 和工资,要求job_id=①并且salary>②
    
    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
    );
    
    #案例4:查询最低工资大于50号部门最低工资的部门id和其最低工资
    
    #①查询50号部门的最低工资
    SELECT  MIN(salary)
    FROM employees
    WHERE department_id = 50
    
    #②查询每个部门的最低工资
    
    SELECT MIN(salary),department_id
    FROM employees
    GROUP BY department_id
    
    #③ 在②基础上筛选,满足min(salary)>①
    SELECT MIN(salary),department_id
    FROM employees
    GROUP BY department_id
    HAVING MIN(salary)>(
    	SELECT  MIN(salary)
    	FROM employees
    	WHERE department_id = 50
    );
    
    #2.列子查询(多行子查询)★
    #案例1:返回location_id是1400或1700的部门中的所有员工姓名
    
    #①查询location_id是1400或1700的部门编号
    SELECT DISTINCT department_id
    FROM departments
    WHERE location_id IN(1400,1700)
    
    #②查询员工姓名,要求部门号是①列表中的某一个
    SELECT last_name
    FROM employees
    WHERE department_id IN(
    	SELECT DISTINCT department_id
    	FROM departments
    	WHERE location_id IN(1400,1700)
    );
    
    #案例2:返回其它工种中比job_id为‘IT_PROG’工种任一工资低的员工的员工号、姓名、job_id 以及salary
    
    #①查询job_id为‘IT_PROG’部门任一工资
    SELECT DISTINCT salary
    FROM employees
    WHERE job_id = 'IT_PROG'
    
    #②查询员工号、姓名、job_id 以及salary,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';
    
    #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
    );
    
    
    #二、select后面
    #案例:查询每个部门的员工个数
    
    SELECT d.*,(
    	SELECT COUNT(*)
    	FROM employees e
    	WHERE e.department_id = d.`department_id`
     ) 个数
     FROM departments d; 
     
     #案例2:查询员工号=102的部门名
     
    SELECT (
    	SELECT department_name,e.department_id
    	FROM departments d
    	INNER JOIN employees e
    	ON d.department_id=e.department_id
    	WHERE e.employee_id=102	
    ) 部门名;
    
    #三、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;
    
    #四、exists后面(相关子查询)
    /*
    语法:
    exists(完整的查询语句)
    结果:
    1或0 ,true or false
    */
    
    SELECT 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 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`
    );
    
    

    进阶八-分页查询

    limit 0,1  从第一个开始选1个
    
    #进阶8:分页查询 ★
    /*
    
    应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求
    语法:
    	select 查询列表
    	from 表
    	【join type join 表2
    	on 连接条件
    	where 筛选条件
    	group by 分组字段
    	having 分组后的筛选
    	order by 排序的字段】
    	limit 【offset,】size;
    	
    	offset要显示条目的起始索引(起始索引从0开始)
    	size 要显示的条目个数
    特点:
    	①limit语句放在查询语句的最后
    	②公式
    	要显示的页数 page,每页的条目数size
    	
    	select 查询列表
    	from 表
    	limit (page-1)*size,size;
    	
    	size=10
    	page  
    	1	0
    	2  	10
    	3	20
    	
    */
    #案例1:查询前五条员工信息
    SELECT * FROM  employees LIMIT 0,5;
    SELECT * FROM  employees LIMIT 5;
            #从0开始的可以省略
    #案例2:查询第11条——第25条
    SELECT * FROM  employees LIMIT 10,15;
    
    #案例3:有奖金的员工信息,并且工资较高的前10名显示出来
    SELECT 
        * 
    FROM
        employees 
    WHERE commission_pct IS NOT NULL 
    ORDER BY salary DESC 
    LIMIT 10 ;
    
    

    联合查询

    union  
    union all  #放在查询语句之后
    
    #进阶9:联合查询
    /*
    union 联合 合并:将多条查询语句的结果合并成一个结果
    语法:
    查询语句1
    union
    查询语句2
    union
    ...
    
    应用场景:
    要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时
    
    特点:★
    1、要求多条查询语句的查询列数是一致的!
    2、要求多条查询语句的查询的每一列的类型和顺序最好一致
    3、union关键字默认去重,如果使用union all 可以包含重复项
    
    */
    #引入的案例:查询部门编号>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';
    
    

    库和表DDL

    创建: create
    修改: alter
    删除: drop
    查看表结构 desc
    
    #DDL/*
    数据定义语言
    库和表的管理
    一、库的管理
    创建、修改、删除
    二、表的管理
    创建: create
    修改: alter
    删除: drop
    */
    #一、库的管理
    #1、库的创建
    /*
    语法:
    create database  [if not exists]库名;
    */
    #案例:创建库Books
    
    CREATE DATABASE IF NOT EXISTS books ;
    #2、库的修改
    
    RENAME DATABASE books TO 新库名;
    
    #更改库的字符集
    ALTER DATABASE books CHARACTER SET gbk;
    #3、库的删除
    DROP DATABASE IF EXISTS books;
    
    #二、表的管理
    #1.表的创建 ★
    /*
    语法:
    create table 表名(
    	列名 列的类型【(长度) 约束】,
    	列名 列的类型【(长度) 约束】,
    	列名 列的类型【(长度) 约束】,
    	...
    	列名 列的类型【(长度) 约束】
    )
    */
    #案例:创建表Book
    
    CREATE TABLE book(
    	id INT,#编号
    	bName VARCHAR(20),#图书名
    	price DOUBLE,#价格
    	authorId  INT,#作者编号
    	publishDate DATETIME#出版日期
    );
    DESC book;
    
    #案例:创建表author
    CREATE TABLE IF NOT EXISTS author(
    	id INT,
    	au_name VARCHAR(20),
    	nation VARCHAR(10)
    )
    DESC author;
    
    #2.表的修改
    /*
    语法
    alter table 表名 add|drop|modify|change column 列名 【列类型 约束】;
    */
    
    #①修改列名
    
    ALTER TABLE book CHANGE COLUMN publishdate pubDate DATETIME;
    
    #②修改列的类型或约束
    ALTER TABLE book MODIFY COLUMN pubdate TIMESTAMP;
    
    #③添加新列
    ALTER TABLE author ADD COLUMN annual DOUBLE; 
    
    #④删除列
    ALTER TABLE book_author DROP COLUMN  annual;
    
    #⑤修改表名
    ALTER TABLE author RENAME TO book_author;
    
    DESC book;
    
    #3.表的删除
    DROP TABLE IF EXISTS book_author;
    
    SHOW TABLES;
    
    #通用的写法:
    		#如果存在 if exists
    DROP DATABASE IF EXISTS 旧库名;
    CREATE DATABASE 新库名;
    
    DROP TABLE IF EXISTS 旧表名;
    CREATE TABLE  表名();
    
    #4.表的复制
    SELECT * FROM Author;
    SELECT * FROM copy2;
    
    #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;
    
    

    数据的增删改DML

    插入:insert
    修改:update
    删除:delete
    truncate与delete的区别
    truncate清空表,不支持回滚
    delete可加条件删除指定
    
    #DML语言
    #一、插入语句
    #方式一:经典的插入
    /*
    语法:
    insert into 表名(列名,...) values(值1,...);
    */
    SELECT * FROM beauty;
    #1.插入的值的类型要与列的类型一致或兼容
    INSERT INTO beauty(id,NAME,sex,borndate,phone,photo,boyfriend_id)
    VALUES(13,'唐艺昕','女','1990-4-23','1898888888',NULL,2);
    
    #2.不可以为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.修改多表的记录【补充】
    
    update 表1 别名
    inner|left|right join 表2 别名
    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 筛选条件
    
    2、多表的删除【补充】
    
    delete 表1的别名,表2的别名
    from 表1 别名
    inner|left|right join 表2 别名 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删除可以回滚.
    
    */
    
    SELECT * FROM boys;
    
    DELETE FROM boys;
    TRUNCATE TABLE boys;
    INSERT INTO boys (boyname,usercp)
    VALUES('张飞',100),('刘备',100),('关云长',100);
    
    

    数据类型

    #常见的数据类型
    #一、整型
    /*
    分类:
    tinyint、smallint、mediumint、int/integer、bigint
    1	 		2		 3				4		8
    
    特点:
    ① 如果不设置无符号还是有符号,默认是有符号,如果想设置无符号,需要添加unsigned关键字
    ② 如果插入的数值超出了整型的范围,会报out of range异常,并且插入临界值
    ③ 如果不设置长度,会有默认的长度
    长度代表了显示的最大宽度,如果不够会用0在左边填充,但必须搭配zerofill使用!
    */
    
    #1.如何设置无符号和有符号
    DROP TABLE IF EXISTS tab_int;
    CREATE TABLE tab_int(
    	t1 INT(7) ZEROFILL,
    	t2 INT(7) ZEROFILL 
    
    );
    DESC tab_int;
    
    INSERT INTO tab_int VALUES(-123456);
    INSERT INTO tab_int VALUES(-123456,-123456);
    INSERT INTO tab_int VALUES(2147483648,4294967296);
    INSERT INTO tab_int VALUES(123,123);
    
    SELECT * FROM tab_int;
    
    
    #二、小数
    /*
    分类:
    1.浮点型
    float(M,D)
    double(M,D)
    2.定点型
    dec(M,D)
    decimal(M,D)
    
    特点:
    
    ①
    M:整数部位+小数部位
    D:小数部位
    如果超过范围,则插入临界值
    ②
    M和D都可以省略
    如果是decimal,则M默认为10,D默认为0
    如果是float和double,则会根据插入的数值的精度来决定精度
    ③定点型的精确度较高,如果要求插入数值的精度较高如货币运算等则考虑使用
    */
    #测试M和D
    
    DROP TABLE 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);
    #原则:
    /*
    所选择的类型越简单越好,能保存数值的类型越小越好
    */
    
    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_set;
    
    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');
    
    
    #四、日期型
    
    /*
    
    分类:
    date只保存日期
    time 只保存时间
    year只保存年
    
    datetime保存日期+时间
    timestamp保存日期+时间
    
    
    特点:
    				字节		范围		时区等的影响
    datetime	   8	   1000——9999	     不受
    timestamp	   4	   1970-2038         受
    */
    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';
    
    

    事务TCL

    #TCL
    /*
    Transaction Control Language 事务控制语言
    事务:
    一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。
    
    案例:转账
    张三丰  1000
    郭襄	1000
    update 表 set 张三丰的余额=500 where name='张三丰'
    中间出现了意外 (例如数据库挂掉了)
    update 表 set 郭襄的余额=1500 where name='郭襄'
    
    事务的特性:
    ACID
    原子性:一个事务不可再分割,要么都执行要么都不执行
    一致性:一个事务执行会使数据从一个一致状态切换到另外一个一致状态
    隔离性:一个事务的执行不受其他事务的干扰
    持久性:一个事务一旦提交,则会永久的改变数据库的数据.
    
    事务的创建
    隐式事务:事务没有明显的开启和结束的标记
    比如insert、update、delete语句
    
    delete from 表 where id =1;
    
    显式事务:事务具有明显的开启和结束的标记
    前提:必须先设置自动提交功能为禁用
    
    set autocommit=0;
    
    步骤1:开启事务
    set autocommit=0;
    start transaction;可选的
    步骤2:编写事务中的sql语句(select insert update delete)
    语句1;
    语句2;
    ...
    
    步骤3:结束事务
    commit;提交事务
    rollback;回滚事务
    
    savepoint 节点名;设置保存点
    
    
    事务的隔离级别:
    		        脏读	不可重复读	幻读
    read uncommitted:√		    √	     √
    read committed:  ×		    √		 √
    repeatable read: ×		    ×		 √
    serializable	  ×         ×        ×
    
    
    mysql中默认 第三个隔离级别 repeatable read
    oracle中默认第二个隔离级别 read committed
    查看隔离级别
    select @@tx_isolation;
    设置隔离级别
    set session|global transaction isolation level 隔离级别;
    
    开启事务的语句;
    update 表 set 张三丰的余额=500 where name='张三丰'
    
    update 表 set 郭襄的余额=1500 where name='郭襄' 
    结束事务的语句;
    */
    
    SHOW VARIABLES LIKE 'autocommit';
    SHOW ENGINES;
    
    #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;
    
    
    #2.演示事务对于delete和truncate的处理的区别
    
    SET autocommit=0;
    START TRANSACTION;
    
    DELETE FROM account;
    ROLLBACK;
    
    #3.演示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;
    
    

    视图

    #视图
    /*
    含义:虚拟表,和普通表一样使用
    mysql5.1版本出现的新特性,是通过表动态生成的数据
    
    比如:舞蹈班和普通班级的对比
    	创建语法的关键字	是否实际占用物理空间	使用
    
    视图	create view		只是保存了sql逻辑	增删改查,只是一般不能增删改
    
    表	    create table		保存了数据		    增删改查
    */
    
    #案例:查询姓张的学生名和专业名
    SELECT stuname,majorname
    FROM stuinfo s
    INNER JOIN major m ON s.`majorid`= m.`id`
    WHERE s.`stuname` LIKE '张%';
    
    CREATE VIEW v1
    AS
    SELECT stuname,majorname
    FROM stuinfo s
    INNER JOIN major m ON s.`majorid`= m.`id`;
    
    SELECT * FROM v1 WHERE stuname LIKE '张%';
    
    
    #一、创建视图
    /*
    语法:
    create view 视图名
    as
    查询语句;
    
    */
    
    
    #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 视图名,视图名,...;
    */
    
    DROP VIEW emp_v1,emp_v2,myv3;
    #四、查看视图
    DESC myv3;
    SHOW CREATE VIEW myv3;
    #五、视图的更新
    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 myv1 VALUES('张飞','zf@qq.com');
    
    #2.修改
    UPDATE myv1 SET last_name = '张无忌' WHERE last_name='张飞';
    
    #3.删除
    DELETE FROM myv1 WHERE last_name = '张无忌';
    
    #具备以下特点的视图不允许更新
    #①包含以下关键字的sql语句:分组函数、distinct、group  by、having、union或者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;
    
    #④join
    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';
    
    

    函数

    #函数
    /*
    含义:一组预先编译好的SQL语句的集合,理解成批处理语句
    1、提高代码的重用性
    2、简化操作
    3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
    
    区别:
    
    存储过程:可以有0个返回,也可以有多个返回,适合做批量插入、批量更新
    函数:有且仅有1 个返回,适合做处理数据后返回一个结果
    
    */
    
    #一、创建语法
    CREATE FUNCTION 函数名(参数列表) RETURNS 返回类型
    BEGIN
    	函数体
    END
    /*
    
    注意:
    1.参数列表 包含两部分:
    参数名 参数类型
    
    2.函数体:肯定会有return语句,如果没有会报错
    如果return语句没有放在函数体的最后也不报错,但不建议
    
    return 值;
    3.函数体中仅有一句话,则可以省略begin end
    4.使用 delimiter语句设置结束标记
    
    */
    
    #二、调用语法
    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 myf3;
    
    #四、删除函数
    DROP FUNCTION myf3;
    
    #案例
    #一、创建函数,实现传入两个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)$
    
    

    存储过程

    #存储过程和函数
    /*
    存储过程和函数:类似于java中的方法
    好处:
    1、提高代码的重用性
    2、简化操作
    
    */
    #存储过程
    /*
    含义:一组预先编译好的SQL语句的集合,理解成批处理语句
    1、提高代码的重用性
    2、简化操作
    3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
    
    */
    
    #一、创建语法
    
    CREATE PROCEDURE 存储过程名(参数列表)
    BEGIN
    
    	存储过程体(一组合法的SQL语句)
    END
    
    #注意:
    /*
    1、参数列表包含三部分
    参数模式  参数名  参数类型
    举例:
    in stuname varchar(20)
    
    参数模式:
    in:该参数可以作为输入,也就是该参数需要调用方传入值
    out:该参数可以作为输出,也就是该参数可以作为返回值
    inout:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值
    
    2、如果存储过程体仅仅只有一句话,begin end可以省略
    存储过程体中的每条sql语句的结尾要求必须加分号。
    存储过程的结尾可以使用 delimiter 重新设置
    语法:
    delimiter 结束标记
    案例:
    delimiter $
    */
    
    #二、调用语法
    CALL 存储过程名(实参列表);
    
    #--------------------------------案例演示-----------------------------------
    #1.空参列表
    #案例:插入到admin表中五条记录
    
    SELECT * FROM admin;
    
    DELIMITER $
    
    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#(into 赋值给result)
    	FROM admin
    	WHERE admin.username = username
    	AND admin.password = PASSWORD;
    	SELECT IF(result>0,'成功','失败');#使用
    END $
    
    #调用
    CALL myp3('张飞','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 $
    
    
    #案例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 $
    
    
    #调用
    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;
    
    
    展开全文
  • CNN学习笔记:卷积卷积核的作用

    千次阅读 2019-06-02 20:51:45
    我们可以看到小鼠各部分卷积的结果。图形越相似,所得结果也就越大。 由此可以看出卷积的作用,即在于滤波,或特征提取。所以CNN的核心所在也就是找到这个卷积核kernal. 也就是...

    卷积:

    根据离散二维卷积公式,有:

    但在图像处理的计算中,一般将卷积核翻转后再计算,可以直接看成是对位相乘。

    卷积核的作用:滤波/特征提取

    假设有一个卷积核如下图所示:

    我们可以看到小鼠各部分和它做卷积的结果。图形越相似,所得结果也就越大。

     

    由此可以看出卷积的作用,即在于滤波,或特征提取。所以CNN的核心所在也就是找到这个卷积核kernal.

    也就是神经网络中的核心公式:y=wx+b

    展开全文
  • Spring 学习笔记

    万次阅读 多人点赞 2019-01-07 10:38:47
    此种方式虽然在接口实现类之间没有耦合,但是接口工厂之间存在耦合。 使用工厂+反射+配置文件的方式,实现解耦, 这也是 Spring 框架 IOC 的底层实现。 //xml配置文件 //...

    好记忆不如烂笔头, 能记下点什么, 就记下点什么, 方便后期的巩固

    Spring介绍

    Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。

    所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。

    • WEB层:SpringMVC

    • Service层:Spring的Bean管理,声明式事务

    • DAO层:Spring的JDBC模板,ORM模板

    优点:

    • IOC:方便解耦合

    • AOP:对程序进行扩展

    • 轻量级框架

    • 方便与其他框架整合

    Spring使用

    Spring开发包解压后的目录介绍:

    • docs: Spring 开发规范和API

    • libs: Spring jar 包和源代码

    • schema: Spring 配置文件的约束

     

    DataAccess 用于数据访问,WEB 用于页面显示,核心容器也就是IOC部分。

     

    控制反转(IOC)

    控制反转(Inversion of Control)是指将对象的创建权反转(交给)Spring。

    使用IOC就需要导入IOC相关的包,也就是上图中核心容器中的几个包:beans,context,core,expression四个包。

     

    实现原理

    传统方式创建对象:

    UserDAO userDAO=new UserDAO();

    进一步面向接口编程,可以多态:

    UserDAO userDAO=new UserDAOImpl();

    这种方式的缺点是接口和实现类高耦合,切换底层实现类时,需要修改源代码。程序设计应该满足OCP元祖,在尽量不修改程序源代码的基础上对程序进行扩展。此时,可以使用工厂模式:

    class BeanFactory{

        public static UserDAO getUserDAO(){

            return new UserDAOImpl();

        }

    }

    此种方式虽然在接口和实现类之间没有耦合,但是接口和工厂之间存在耦合。

    使用工厂+反射+配置文件的方式,实现解耦,这也是 Spring 框架 IOC 的底层实现。

    //xml配置文件

    //<bean id="userDAO" class="xxx.UserDAOImpl"></bean>

    class BeanFactory{

        public static Object getBean(String id){

            //解析XML

            //反射

            Class clazz=Class.forName();

            return clazz.newInstance();

        }

    }

    IOC XML 开发

    在 docs 文件中包含了 xsd-configuration.hmtl 文件。其中定义了 beans schema。

    <beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        //在此配置bean

        <bean id="userService" class="x.y.UserServiceImpl">

        </bean>

    </beans>

    调用类:

    ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

    UserService userService=(UserService)applicationContext.getBean("userService");

    userService.save();

    IOC 和 DI

    DI 指依赖注入,其前提是必须有 IOC 的环境,Spring 管理这个类的时候将类的依赖的属性注入进来。

    例如,在UserServiceImpl.java中:

    public class UserServiceImpl implements UserService{

    private String name;

    public void setName(String name){

    this.name=name;

    }

    public void save(){

    System.out.println("save "+name);

    }

    }

    在配置文件中:

    <beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <bean id="userService" class="spring.demo1.UserServiceImpl">

        <!--配置依赖的属性-->

         <property name="name" value="tony"/>

        </bean>

    </beans>

    测试代码:

    @Test

    public void demo2(){

    //创建Spring工厂

    ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

    UserService userService=(UserService)applicationContext.getBean("userService");

    userService.save();

    }

    运行结果:

    save tony

    可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。

    Spring 的工厂类

    • BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。

    • ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类:

      1. ClassPathXmlApplicationContext: 加载类路径下的配置文件

      2. FileSystemXmlApplicationContext: 加载磁盘下的配置文件

    bean标签配置

    • id: 唯一约束,不能出现特殊字符

    • name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符

    生命周期:

    • init-method: bean被初始化的时候执行的方法

    • destroy-method: bean被销毁的时候执行的方法

    作用范围:

    • scope: bean的作用范围,有如下几种,常用的是前两种

      • singleton: 默认使用单例模式创建

      • prototype: 多例

      • request: 在web项目中,spring 创建类后,将其存入到 request 范围中

      • session: 在web项目中,spring 创建类后,将其存入到 session 范围中

      • globalsession: 在web项目中,必须用在 porlet 环境

    属性注入设置

    1. 构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。

    <bean id="car" class="demo.Car">

        <constructor-arg name="name" value="bmw">

        <constructor-arg name="price" value="123">

    </bean>

    1. set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或  name)。

    <bean id="employee" class="demo.Employee">

        <property name="name" value="xiaoming">

        <property name="car" ref="car">

    </bean>

    1. P名称空间的属性注入: 首先需要引入p名称空间:

    <beans xmlns="http://www.springframework.org/schema/beans"

        //引入p名称空间

        xmlns:p="http://www.springframework.org/schema/p"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    </beans>

    如果是普通属性:

    <bean id="car" class="demo.Car" p:name="bmv" p:price="123">

    </bean>

    如果是引用类型:

    <bean id="employee" class="demo.Employee" p:name="xiaoming" p:car-ref:"car">

    </bean>

    1. SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)

    <bean id="car" class="demo.Car">

        <property name="name" value="#{'xiaoming'}">

        <property name="car" ref="#{car}">

    </bean>

    1. 集合类型属性注入:

    <bean id="car" class="demo.Car">

        <property name="namelist">

            <list>

                <value>qirui</value>

                <value>baoma</value>

                <value>benchi</value>

            </list>

        </property>

    </bean>

    多模块开发配置

    1. 在加载配置文件的时候,加载多个配置文件

    2. 在一个配置文件中引入多个配置文件,通过实现

    IOC 注解开发

    示例

    1. 引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。

    2. 创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html)

    <beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="

            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 

            <!-- bean definitions here -->

    </beans>

    1. 组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。

    <context:component-scan base-package="demo1">

    1. 在类上添加注解

    2. 使用注解设置属性的值

    属性如果有set方法,将属性注入的注解添加到set方法

    属性没有set方法,将注解添加到属性上。

    @Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类

    public class UserDAOImpl implements UserDAO {

    @Override

    public void save() {

    // TODO Auto-generated method stub

    System.out.println("save");

    }

    注解详解

    1. @Component

    组件注解,用于修饰一个类,将这个类交给 Spring 管理。

    有三个衍生的注解,功能类似,也用来修饰类。

    • @Controller:修饰 web 层类

    • @Service:修饰 service 层类

    • @Repository:修饰 dao 层类

    2.属性注入

    • 普通属性使用 @Value 来设置属性的值

    • 对象属性使用 @Autowired  ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用

    • 实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入

    3.其他注解

    • @PostConstruct 相当于 init-method,用于初始化函数的注解

    • @PreDestroy 相当于 destroy-method,用于销毁函数的注解

    • @Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")

    IOC 的 XML 和注解开发比较

    • 适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。

    • 可以使用 XML 管理 bean,使用注解来进行属性注入

    AOP开发

    AOP 是 Aspect Oriented Programming 的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,是OOP的延续。

    AOP 能够对程序进行增强,在不修改源码的情况下,可以进行权限校验,日志记录,性能监控,事务控制等。

    也就是说功能分为两大类,一类是核心业务功能,一类是辅助增强功能。两类功能彼此独立进行开发。比如登录功能是核心业务功能,日志功能是辅助增强功能,如果有需要,将日志和登录编制在一起。辅助功能就称为切面,这种能选择性的、低耦合的把切面和核心业务功能结合的编程思想称为切面编程。

    底层实现

    JDK 动态代理只能对实现了接口的类产生代理。Cglib 动态代理可以对没有实现接口的类产生代理对象,生成的是子类对象。

    使用 JDK 动态代理:

    public interface UserDao {

    public void insert();

    public void delete();

    public void update();

    public void query();

    }

    实现类:

    public class UserDaoImpl implements UserDao { @Override public void insert() { System.out.println("insert"); } @Override public void delete() { System.out.println("delete"); } @Override public void update() { System.out.println("update"); } @Override public void query() { System.out.println("query"); } }

    JDK 代理:

    public class JDKProxy implements InvocationHandler{

    private UserDao userDao;

    public JDKProxy(UserDao userDao){

    this.userDao=userDao;

    }

    public UserDao createProxy(){

    UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),

    userDao.getClass().getInterfaces(), this);

    return userDaoProxy;

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    if("update".equals(method.getName())){

    System.out.println("权限校验");

    return method.invoke(userDao, args);

    }

    return method.invoke(userDao, args);

    }

    }

    通过动态代理增强了 update 函数。 测试类:

    public class Demo1 {

    @Test

    public void demo1(){

    UserDao userDao=new UserDaoImpl();

    UserDao proxy=new JDKProxy(userDao).createProxy();

    proxy.insert();

    proxy.delete();

    proxy.update();

    proxy.query();

    }

    }

    运行结果为:

    insert

    delete

    权限校验

    update

    query

    CglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。

    与上边JDK代理不同,Cglib的使用方式如下:

    public class CglibProxy implements MethodInterceptor{

    //传入增强的对象

    private UserDao customerDao;

    public CglibProxy(UserDao userDao){

    this.userDao=userDao;

    }

    public UserDao createProxy(){

    Enhancer enhancer=new Enhancer();

    enhancer.setSuperclass(userDao.getClass());

    enhancer.setCallback(this);

    UserDao proxy=(UserDao)enhancer.create();

    return proxy;

    }

    @Override

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

    if("save".equals(method.getName())){

    System.out.println("enhance function");

    return methodProxy.invokeSuper(proxy, args);

    }

    return methodProxy.invokeSuper(proxy, args);

    }

    }

    如果实现了接口的类,底层采用JDK代理。如果不是实现了接口的类,底层采用 Cglib代理。

    IOC与传统方式的比较

    1. 获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。

    Spring 的 AOP 开发(AspectJ 的 XML 方式)

    AspectJ 是一个 AOP 的框架,Spring 引入 AspectJ,基于 AspectJ 进行 AOP 的开发。

    相关术语

    • Joinpoint: 连接点,可以被拦截到的点。也就是可以被增强的方法都是连接点。

    • Pointcut: 切入点,真正被拦截到的点,也就是真正被增强的方法

    • Advice: 通知,方法层面的增强。对某个方法进行增强的方法,比如对 save 方法进行权限校验,权限校验的方法称为通知。

    • Introduction: 引介,类层面的增强。

    • Target: 目标,被增强的对象(类)。

    • Weaving: 织入,将 advice 应用到 target 的过程。

    • Proxy: 代理对象,被增强的对象。

    • Aspect: 切面,多个通知和多个切入点的组合。

    使用方法

    1. 引入相关包

    2. 引入配置文件

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="

            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->

    </beans>

    1. 编写目标类并配置:

    public class ProductDaoImpl implements ProductDao {

    @Override

    public void save() {

    System.out.println("save");

    }

    @Override

    public void update() {

    System.out.println("update");

    }

    @Override

    public void find() {

    System.out.println("find");

    }

    @Override

    public void delete() {

    System.out.println("delete");

    }

    }

    <bean id="productDao" class="demo1.ProductDaoImpl"></bean>

    1. 编写切面类,假设用于权限验证并配置

    public class MyAspectXML {

    public void checkPri(){

    System.out.println("check auth");

    }

    }

    <bean id="myAspect" class="demo1.MyAspectXML"></bean>

    1. 通过AOP配置完成对目标类的增强

    <aop:config>

    <aop:pointcut expression="execution(* demo1.ProductDaoImpl.save(..))" id="pointcut1"/>

    <aop:aspect ref="myAspect">

    <aop:before method="chechPri" pointcut-ref="pointcut1"/>

    </aop:aspect> 

    </aop:config>

     

    通知类型

    1. 前置通知:在目标方法执行前操作,可以获得切入点信息

    <aop:before method="chechPri" pointcut-ref="pointcut1"/>

    public void checkPri(JoinPoint joinPoint){

    System.out.println("check auth "+joinPoint);

    }

    1. 后置通知:在目标方法执行后操作,可以获得方法返回值

    <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>

    public void writeLog(Object result){

        System.out.println("writeLog "+result);

    }

    1. 环绕通知:在目标方法执行前和后操作,可以阻止目标方法执

    <aop:around method="around" pointcut-ref="pointcut3"/>

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{

    System.out.println("before");

    Object result=joinPoint.proceed();

    System.out.println("after");

    return result;

    }

    1. 异常抛出通知:程序出现异常时操作

    <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>

    public void afterThrowing(Throwable ex){

    System.out.println("exception "+ex.getMessage());

    }

    1. 最终通知:相当于finally块,无论代码是否有异常,都会执行

    <aop:after method="finallyFunc" pointcut-ref="pointcut4"/>

    public void finallyFunc(){

    System.out.println("finally");

    }

    1. 引介通知:不常用

    Spring 切入点表达式

    基于 execution 函数完成

    语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)

    其中任意字段可以使用*代替表示任意值

    Spring 的 AOP 基于 AspectJ 注解开发

    开发步骤

    1. 引入jar包

    2. 设置配置文件:

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="http://www.springframework.org/schema/beans 

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop.xsd

    http://www.springframework.org/schema/tx 

    http://www.springframework.org/schema/tx/spring-tx.xsd">

    </beans>

    1. 编写配置目标类

    <bean id="orderDao" class="demo1.OrderDao"></bean>

    public class OrderDao {

    public void save(){

    System.out.println("save order");

    }

    public void update(){

    System.out.println("update order");

    }

    public void delete(){

    System.out.println("delete order");

    }

    public void find(){

    System.out.println("find order");

    }

    }

    1. 开启aop注解自动代理

    <aop:aspectj-autoproxy/>

    1. 编写切面类并配置

    @Aspect

    public class MyAspectAnno {

    @Before(value="execution(* demo1.OrderDao.save(..))")

    public void before(){

    System.out.println("before");

    }

    }

    <bean id="myAspect" class="demo1.MyAspectAnno">

    注解通知类型

    • @Before: 前置通知

    • @AfterReturning: 后置通知

    @AfterReturning(value="execution(* demo1.OrderDao.save(..))",returning="result")

    public void after(Object result){

    System.out.println("after "+result);

    }

    • @Around:环绕通知

    @Around(value="execution(* demo1.OrderDao.save(..))")

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{

    System.out.println("before");

    Object obj=joinPoint.proceed();

    System.out.println("after");

    return obj;

    }

    • @AfterThrowing: 抛出异常

    @AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e")

    public void afterThrowing(Throwable e){

    System.out.println("exception:"+e.getMessage();

    }

    • @After: 最终通知

    @After(value="execution(* demo1.OrderDao.save(..))")

    public void after(){

    System.out.println("finally");

    }

    • @PointCut:切入点注解

    @PointCut(value="execution(* demo1.OrderDao.save(..))")

    private void pointcut1(){}

    此时,在上述通知的注解中,value可以替换为该函数名,例如:

    @After(value="MyAspect.pointcut1()")

    public void after(){

    System.out.println("finally");

    }

    这个注解的好处是,只需要维护切入点即可,不用在修改时修改每个注解。

    Spring 的 JDBC 模板

    Spring 对持久层也提供了解决方案,也就是 ORM 模块和 JDBC 的模板。针对 JDBC ,提供了 org.springframework.jdbc.core.JdbcTemplate 作为模板类。

    使用 JDBC 模板

    1. 引入jar包,数据库驱动,Spring 的 jdbc 相关包。

    2. 基本使用:

    public void demo1(){

        //创建连接池

    DriverManagerDataSource dataSource=new DriverManagerDataSource();

    dataSource.setDriverClassName("com.mysql.jdbc.Driver");

    dataSource.setUrl("jdbc:mysql:///spring4");

    dataSource.setUsername("root");

    dataSource.setPassword("123456");

    //创建JDBC模板 

    JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);

    jdbcTemplate.update("insert into account values (null,?,?)", "xiaoming",1000d);

    }

    1. 将连接池和模板交给 Spring 管理

    • 配置文件:

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource;">

    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

    <property name="url" value="jdbc:mysql:///spring4"></property>

    <property name="username" value="root"></property>

    <property name="password" value="123456"></property>

    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate;"> 

    <property name="dataSource" ref="dataSource"></property>

    </bean>

    • 测试文件:

       

    @RunWith(SpringJUnit4ClassRunner.class)

    @ContextConfiguration("classpath:applicationContext.xml")

    public class JdbcDemo2 {

    @Resource(name="jdbcTemplate")

    private JdbcTemplate jdbcTemplate;

    @Test

    public void demo2(){

    jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d);

    }

    }

    使用开源数据库连接池

    1. 使用 DBCP 的配置:

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">

    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

    <property name="url" value="jdbc:mysql://192.168.66.128/spring4"></property>

    <property name="username" value="root"></property>

    <property name="password" value="123456"></property>

    1. 使用 C3P0 的配置:

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>

    <property name="jdbcUrl" value="jdbc:mysql://192.168.66.128/spring4"></property>

    <property name="user" value="root"></property>

    <property name="password" value="123456"></property>

    </bean>

    1. 引入外部属性文件

    首先建立外部属性文件:

    jdbc.driverClass=com.mysql.jdbc.Driver

    jdbc.url=jdbc:mysql://192.168.66.128/spring4

    jdbc.username=root

    jdbc.password=123456

    然后对属性文件进行配置:

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

    <property name="driverClass" value="${jdbc.driverClass}"></property>

    <property name="jdbcUrl" value="${jdbc.url}"></property>

    <property name="user" value="${jdbc.username}"></property>

    <property name="password" value="${jdbc.password}"></property>

    </bean>

    CRUD操作

    insert, update, delete 语句都借助模板的 update 方法进行操作。

    public void demo(){

    jdbcTemplate.update("insert into account values (null,?,?)", "xiaoda",1000d);

    jdbcTemplate.update("update account set name=?,money=? where id=?", "xiaoda",1000d,2);

    jdbcTemplate.update("delete from account where id=?", 6);

    }

    查询操作:

    public void demo3(){

    String name=jdbcTemplate.queryForObject("select name from account where id=?",String.class,5);

    long count=jdbcTemplate.queryForObject("select count(*) from account",Long.class);

    }

    将返回的结果封装成为类:

    public void demo4(){

    Account account=jdbcTemplate.queryForObject("select * from account where id=?", new MyRowMapper(),5);

    }

    其中:

    class MyRowMapper implements RowMapper<Account>{

    @Override

    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {

    Account account=new Account();

    account.setId(rs.getInt("id"));

    account.setName(rs.getString("name"));

    account.setMoney(rs.getDouble("money"));

    return account;

    }

    }

    Spring的事务管理

    事务

    事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。

    具有四个特性:

    • 原子性:事务不可分

    • 一致性:事务执行前后数据完整性保持一致

    • 隔离性:一个事务的执行不应该受到其他事务干扰

    • 持久性:一旦事务结束,数据就持久化到数据库

    如果不考虑隔离性会引发安全性问题:

    • 读问题:

      • 脏读:一个事务读到另一个事务未提交的数据

      • 不可重复读:一个事务读到另一个事务已经提交的 update 数据,导致一个事务中多次查询结果不一致

      • 幻读:一个事务读到另一个事务已经提交的 insert 数据,导致一个事务中多次查询结果不一致

    • 写问题:

      • 丢失更新

    解决读问题:设置事务隔离级别

    • Read uncommitted: 未提交读,无法解决任何读问题

    • Read committed: 已提交读,解决脏读问题

    • Repeatable read: 重复读,解决脏读和不可重复读问题

    • Serializable:序列化,解决所有读问题

    事务管理API

    1. PlatformTransactionManager: 平台事务管理器

    这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。

    1. TransactionDefinition: 事务定义信息

    用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等

    1. TransactionStatus: 事务的状态

    用于记录在事务管理过程中,事务的状态的对象。

    上述API的关系: Spring 在进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务管理,在事务管理过程当中,产生各种此状态,将这些状态信息记录到事务状态的对象当中。

    事务的传播行为

    事务的传播行为主要解决业务层(Service)方法相互调用的问题,也就是不同的业务中存在不同的事务时,如何操作。

    Spring 中提供了7种事务的传播行为,分为三类:

    • 保证多个操作在同一个事务中

      • PROPAGATION_REQUIRED: B方法调用A方法,如果A中有事务,使用A中的事务并将B中的操作包含到该事务中;否则新建一个事务,将A和B中的操作包含进来。(默认)

      • PROPAGATION_SUPPORTS:如果A中有事务,使用A的事务;否则不使用事务

      • PROPAGATION_MANDATORY:如果A中有事务,使用A的事务;否则抛出异常

    • 保证多个操作不在同一个事务中

      • PROPAGATION_REQUIRES_NEW:如果A中有事务,将其挂起,创建新事务,只包含自身操作。否则,新建一个事务,只包含自身操作。

      • PROPAGATION_NOT_SUPPORTED:如果A中有事务,挂起,不使用事务。

      • PROPAGATION_NEVER:如果A中有事务,抛出异常,也即不能用事务运行。

    • 嵌套事务

      • PROPAGATION_NESTED:如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,然后执行B的操作。如果出现异常,可以回滚到最初状态或保存点状态。

    实例

    以转账为例,业务层的DAO层类如下:

    public interface AccountDao {

    public void outMoney(String from,Double money);

    public void inMoney(String to,Double money);

    }

    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{

    @Override

    public void outMoney(String from, Double money) {

     this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);

    }

    @Override

    public void inMoney(String to, Double money) {

    this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,to);

    }

    }

    public interface AccountService {

    public void transfer(String from,String to,Double money);

    }

    public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {

    this.accountDao = accountDao;

    }

    @Override

    public void transfer(String from, String to, Double money) {

    accountDao.outMoney(from, money);

    accountDao.inMoney(to, money);

    }

    }

    在xml中进行类的配置:

    <bean id="accountService" class="tx.demo.AccountServiceImpl">

    <property name="accountDao" ref="accountDao"/>

    </bean>

    <bean id="accountDao" class="tx.demo.AccountDaoImpl">

    <property name="dataSource" ref="dataSource"/>

    </bean>

     

    事务管理1: 编程式事务管理

    1. 配置平台事务管理器

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"></property>

    </bean>

    1. 配置事务管理模板类

    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">

    <property name="transactionManager" ref="transactionManager"></property>

    </bean>

    1. 在业务层注入事务管理模板

    <bean id="accountService" class="tx.demo1.AccountServiceImpl">

    <property name="accountDao" ref="accountDao"/>

    <property name="transactionTemplate" ref="transactionTemplate"/>

    </bean>

    1. 编码实现事务管理

    //ServiceImpl类中:

    private TransactionTemplate transactionTemplate;

    @Override

    public void transfer(String from, String to, Double money) {

    transactionTemplate.execute(new TransactionCallbackWithoutResult() {

    @Override

    protected void doInTransactionWithoutResult(TransactionStatus arg0) {

    accountDao.outMoney(from, money);

    accountDao.inMoney(to, money);

    }

    });

    }

    声明式事务管理(配置实现,基于AOP思想)

    1. XML 方式的声明式事务管理

    • 配置事务管理器

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"></property>

    </bean>

    • 配置事务通知

    <tx:advice id="txAdvice" transaction-manager="transactionManager">

    <tx:attributes>

    <tx:method name="transfer" propagation="REQUIRED"/>

    </tx:attributes>

    </tx:advice>

    • 配置aop事务

    <aop:config>

    <aop:pointcut expression="execution(* tx.demo2.AccountServiceImpl.*(..))" id="pointcut1"/>

    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>

    </aop:config>

    1. 注解方式

    • 配置事务管理器,和上方一致

    • 开启事务管理的注解:

    <tx:annotation-driven transaction-manager="transactionManager"/>

    • 在使用事务的类上添加一个注解@Transactional

     

    以后还有的内容,再去补充!

    文章地址:https://mp.weixin.qq.com/s?__biz=MzU0NTk2MjQyOA==&mid=2247483971&idx=1&sn=cbfb9895543257282f68c8009db23d14&chksm=fb65a290cc122b86891abfcabb67a9fdffe3cb9b9d97ce6572f1e1f3c772b843da30d49ceedf&mpshare=1&scene=23&srcid=0106VNIBwZLqKJLN4B8PbGXY#rd

    展开全文
  • 超硬核!数据结构学霸笔记,考试面试吹牛就靠它

    万次阅读 多人点赞 2021-03-26 11:11:21
    上次发操作系统笔记,很快浏览上万,这次数据结构比上次硬核的多哦,同样的会发超硬核代码,关注吧。
  • JQuery 基础笔记

    万次阅读 多人点赞 2019-08-28 22:01:58
    JQuery笔记及面试题
  • Kerberos笔记

    千次阅读 2018-12-08 13:59:47
    本文以《Hadooop Security》等资料为基础,罗列关于Kerberos的一些重要内容的笔记。 文章目录Kerberos的基本思想没有Kerberos时,Hadoop是如何工作的?Kerberos概述1. Principal2. Realm3. KDC3.1 Kerberos数据库3.2...
  • 笔记

    千次阅读 2013-12-31 22:14:43
    举例来说:就是:比如说你要去饭店吃饭,你只需要饭店,找到饭店的服务员,跟她说你要吃什么,然后叫会给你出来让你吃,你并不需要知道这个饭是怎么错的,你只需要面向这个服务员,告诉他你要吃什么,然后他也只...
  • 读书笔记-精准努力-应急学习的好处

    千次阅读 2021-01-15 23:22:25
    我们来总结一下从急用性出发学习的好处。 第一,能够帮助你及时应用你的所学,并且得到反馈,它符合学习的正确步骤,完成了知识实践的结合。 从急用性出发学习的第二个好处,就是因为这个问题很着急解决,并且一直...
  • 6、application.propertiesapplication.yml的区别是yml不能被@PropertySource注解加载 四、自动载入外化属性到Bean(Boot更推荐的方式) 1、当 @EnableConfigurationProperties 注解 应 用到你的 @...
  • C++工作笔记-枚举类型的作用

    千次阅读 2018-04-02 23:30:18
    原文:http://blog.csdn.net/lzkit/article/details/7846045 学C的候老师没有很详细讲到枚举类型,但在学C++和做一些实践的时候发现,枚举类型有时候是必要的。 有时我们希望某些常量只在类中有效。由于#define...
  • kafka笔记

    千次阅读 多人点赞 2020-07-19 15:37:38
    笔记来源:尚硅谷视频笔记2.0版+2.1版 黑马视频:Kafka深入探秘者来了 笔记、代码下载地址:https://me.csdn.net/download/hancoder ...消息队列的好处 1)解耦: 允许你独立的扩展或修改两边的处理过程,只要确保
  • 一、线程池的作用: 线程池作用就是限制系统中执行线程的数量。 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他...
  • with 原理及其上下文管理器python with的原理with 语句的作用简述with执行原理with支持对象with语句的自我实现01with语句的自我实现02python 上下文管理协议python 上下文管理器(Contextor)运行的执行原理: ...
  • latex教程详细笔记

    万次阅读 多人点赞 2018-05-07 20:41:11
    本文是笔者初学WinEdt用以编辑Latex的笔记,只涉及一些简单问题,详细请参考百度文库(点点这几个字看看~~) 本文的主要参考文献是ta 1.字体大小 options—options interface—Font schemes—Font双击从右边...
  • c#学习笔记之Invoke()作用分析

    万次阅读 2016-09-19 17:13:10
    Invoke()的作用是:在应用程序的主线程上执行指定的委托。一般应用:在辅助线程中修改UI线程( 主线程 )中对象的属性时,调用this.Invoke(); 1. //测试的窗体 2. public class TestForm : Form 3. { 4. //...
  • 学习笔记整理

    万次阅读 多人点赞 2020-05-16 22:05:54
    在数据结构中,有大根堆小根堆的概念 概念:叶子节点大于(或小于)父节点 从内存分区的角度来看: 在C++中内存分为五大分区,分别是栈区,堆区,自由存储区,常量存储区,全局静态存储区 栈的使用与回收都是系统...
  • JavaWeb笔记

    千次阅读 多人点赞 2019-09-06 10:42:23
    Javabean是一个Java类,拥有特殊的用途,自己的封装方式 通过getset对业务数据进行封装 二、JavaBean的作用? 1、封装业务逻辑 2、减少代码,代码的重复使用 三、JavaBean所拥有的特征? 1、是Java类 2、get...
  • SpringMVC笔记

    千次阅读 2020-10-12 23:51:53
    框架:研究官方文档,锻炼自学能力,锻炼笔记能力,锻炼项目能力 Spring: IOC AOP SpringMVC:SpringMVC的执行流程 1. 什么是MVC? 模型(Model):dao,service 视图(View):jsp 控制器(Controller):...
  • Java 复习笔记

    万次阅读 多人点赞 2018-08-18 21:20:20
    Java复习笔记 持续更新 1.讲解一下Java跨平台原理 由于操作系统的的指令集不是完全一致的,就会让我们的程序在不同的操作系统上执行不同的程序代码 Java通过不同的系统,不同版本不同位数的java虚拟机来屏蔽...
  • Spring框架对于很多Java开发人员来说都不陌生。...如此多的子项目组件,一方面方便了开发人员的使用,另外一个方面也带来了使用方面的问题。每个子项 目都有一定的学习曲线。开发人员需要了解这些子项目组件的
  • Mysql笔记

    万次阅读 2020-07-30 11:14:10
    数据库基础知识 为什么要使用数据库 数据保存在内存 优点: 存取速度快 缺点: 数据不能永久保存 数据保存在文件 ...缺点:1)速度比内存操作...作用:用于存取数据、查询、更新管理关系数据库系统。 什么是MySQL
  • Python学习笔记

    千次阅读 多人点赞 2019-10-19 07:54:33
    简介Python 编程第一步模块与包错误异常注释1. 数据类型与运算符数据类型操作符2. 控制流分支语句循环语句3. 函数函数声名函数调用变量作用域调用预设函数4. 列表声名与使用二维列表(矩阵)5. 字符串定义与语法...
  • 以下是我看过最好的全连接层的解释!...注:上图我们要的下面运算无联系 并且不考虑激活函数bias 当我第一次看到这个全连接层,我的第一个问题是: 它是怎么样把3x3x5的输出,转换成1x4096的形式? ...
  • 迭代器---不必知道序列底层是怎么实现的,就可以利用迭代器来访问一个序列。任何容器类,都必须有某种方式可以插入元素并将...能够将遍历序列的操作序列底层相分离迭代器是一个对象,它的工作是遍历并选择序列中的...
  • 从零开始学习Linux笔记

    万次阅读 多人点赞 2020-05-15 19:12:14
    从零开始学习Linux,记录笔记,担心自己以后会忘,也供大家茶余饭后,闲来无事看看,自己的理解只能到这,也希望大家可以指出我的错误 让我可以有一点点进步,以后会一直更新
  • 爬虫笔记

    千次阅读 2016-04-11 20:46:38
    cookielib模块的主要作用是提供可存储cookie的对象,以便于与urllib2模块配合使用来访问Internet资源。Cookielib模块非常强大,我们可以利用本模块的CookieJar类的对象来捕获cookie并在后续连接请求时重新发送,比如...
  • Linux笔记

    千次阅读 2017-04-18 10:54:46
    Linux笔记 第一章 首次登录在线求助 1、在命令行模式下执行命令 (1)开始执行命令 命令格式:command [-options] parameter1 parameter2 ... 如:ls -al install.log ——列出文件install.log的信息 命令  选项...
  • 时钟中断:是指在计算机CMOS中一个芯片晶片中(常被称为实时时钟)由电池供电,用于...2.现在流行的操作系统linuxwindows他们都是多进程并发执行的,为什么多进程可以并发,这是由中断支持的。一个进程执行的脚本是
  • 通信笔记

    千次阅读 2020-07-04 23:55:10
    文章目录 1....两个不同站的码片序列正交,就是向量ST的规格化内积都是S * T = 0 任何一个码片向量该码片向量自己的规格化内积都是S * S = 1 任何一个码片向量该码片的反码的向量的规格化内积...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,140
精华内容 20,456
关键字:

做笔记的好处和作用