diff --git a/README.md b/README.md index b9e3f71a340f694fa6ff4a2b5a8875c0c7e70460..a06d975d3d4f7100c15e757a637f25bd4574dbc1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# KnowledgeNote +# KnowledgeNote1 #### 介绍 知识笔记仓库 源代码 文章放到这个仓库 diff --git a/lixiaoyu/JAVA/1595866443956.png b/lixiaoyu/JAVA/1595866443956.png new file mode 100644 index 0000000000000000000000000000000000000000..87ef5b94c88ab0458f938cd5af9bd613badcc1ef Binary files /dev/null and b/lixiaoyu/JAVA/1595866443956.png differ diff --git a/lixiaoyu/JAVA/1595866558512.png b/lixiaoyu/JAVA/1595866558512.png new file mode 100644 index 0000000000000000000000000000000000000000..1674d0dca40e01dcd585e300d27d48c693d6ab8d Binary files /dev/null and b/lixiaoyu/JAVA/1595866558512.png differ diff --git a/lixiaoyu/JAVA/1595866582549.png b/lixiaoyu/JAVA/1595866582549.png new file mode 100644 index 0000000000000000000000000000000000000000..b6f800c7477d7c525d0d9e698c9a77c3899a1b15 Binary files /dev/null and b/lixiaoyu/JAVA/1595866582549.png differ diff --git a/lixiaoyu/JAVA/1595866618760.png b/lixiaoyu/JAVA/1595866618760.png new file mode 100644 index 0000000000000000000000000000000000000000..a19a4da49ab13e1827e6e629da27af997c6fd7c4 Binary files /dev/null and b/lixiaoyu/JAVA/1595866618760.png differ diff --git "a/lixiaoyu/JAVA/java\351\233\206\345\220\210.md" "b/lixiaoyu/JAVA/java\351\233\206\345\220\210.md" new file mode 100644 index 0000000000000000000000000000000000000000..0739ad21d97553f80ea1ca92537cc8d6ed24c97c --- /dev/null +++ "b/lixiaoyu/JAVA/java\351\233\206\345\220\210.md" @@ -0,0 +1,1215 @@ +# 数据结构 + +###### 十进制转二进制 + +![1595867838201](C:\Users\28768\AppData\Roaming\Typora\typora-user-images\1595867838201.png) + +###### 二进制转10进制 + +![1595868021125](C:\Users\28768\AppData\Roaming\Typora\typora-user-images\1595868021125.png) + + + +\>>表示右移,如果该数为正,则高位补0,若为负数,则高位补1; + +\>>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。 + + + + + +## 数组 + +> ``` +> char[] ch = new char[] {'l','x','y','a','n'}; +> System.out.println(ch[5]); +> +> //会出现 索引越界异常本来就5个,下标最大是4 +> Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 +> ``` +> +> 特点: +> +> ​ 1.内存地址连续,使用之前必须要制定数组长度。 +> +> ​ 2.可以通过下标访问的方式访问成员,查询效率比较高。 +> +> ​ 3.增删操作会带来性能消耗。(如果想在一个数组上再加一个数据,那么就得继续扩容:数组的扩容就是重新创建一个下标是5的数组 把原来的拿过来 再在最后面添加上第六个) + + + +## 链表 + +###### 双向链表 + +> ``` +> LinkedList 源码 中的内部类 +> +> private static class Node { +> E item; //节点元素 +> Node next;//下一个节点 +> Node prev;//上一个节点 +> +> Node(Node prev, E element, Node next) { +> this.item = element; +> this.next = next; +> this.prev = prev; +> } +> } +> ``` +> +> 特点: +> +> ​ 1.灵活的空间要求,存储空间不要求连续。 +> +> ​ 2.不支持下标访问,支持顺序遍历检索。 +> +> ​ 3.针对增删效果更高一些,只和操作节点的前后节点有关系,无需移动元素。 + + + +## 树 + +###### 红黑树 + +> 红黑树Red-Black-Tree[RBT] 是一个**自平衡**【不是绝对的】的二叉树。树上的节点需满足如下规则。 +> +> 1.每个节点要么是红色,要么是黑色。 +> +> 2.根节点必须是黑色。 +> +> 3.每个叶子节点【NIL】是黑色。 +> +> 4.每个红色节点的两个子节点必须是黑色。 +> +> 5.任意节点到每个叶子节点的路劲包含相同数量的黑节点。 +> +> + +![1596063769934](C:\Users\28768\AppData\Roaming\Typora\typora-user-images\1596063769934.png) + + + +## 集合 + +###### Collection 接口: + +​ Collection分为List和Set,List集合中的元素是有序的、可重复的,而Set集合中的元素是无序的、不能重复的。List集合强调的是有序,Set集合强调的是不重复。 + +###### Iterator 迭代: + +###### Collections: + +​ collections Java官方的提供的工具类 + +###### Arrays: + +​ 主要包含了操纵数组的各种方法 + +###### 比较器: + +​ Comparable + +```java +Comparable comparable = new Comparable() { + @Override + //compareTo方法接受任意类型的参数,来进行比较 + public int compareTo(Object o) { + return 0; + } + }; +``` + + + + + + Comparator + + + +## List接口 + +### ArrayList + +> 本质就是动态数组,动态扩容 + +``` + /** + * Default initial capacity. + //默认的数组长度 + */ + private static final int DEFAULT_CAPACITY = 10; + + + /** + * Shared empty array instance used for empty instances. + //空数组 + */ + private static final Object[] EMPTY_ELEMENTDATA = {}; + + /** + * Shared empty array instance used for default sized empty instances. We + * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when + * first element is added. + //集合中存储数据的 数组对象 + */ + private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; + + /** + * The array buffer into which the elements of the ArrayList are stored. + * The capacity of the ArrayList is the length of this array buffer. Any + * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA + * will be expanded to DEFAULT_CAPACITY when the first element is added. + */ + transient Object[] elementData; // non-private to simplify nested class access + + /** + * The size of the ArrayList (the number of elements it contains). + * + * @serial + //集合中元素的个数 + */ + private int size; +``` + +#### 初始化操作 + +> 无参构造 +> +> ``` +> public ArrayList() { +> this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; +> } +> ``` +> + +##### add添加源码分析 + +> 第一次添加 + +``` +public boolean add(E e) { + //确定容量 动态扩容 size初始0 + ensureCapacityInternal(size + 1); // ensureCapacityInternal(1); + //将 要添加的元素添加到数组中 + elementData[size++] = e; + return true; + } +``` + +``` +private void ensureCapacityInternal(int minCapacity) { //1 + //ensureExplicitCapacity(10) + ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); + } + +//elementData {} 为空 mincapacity为1 +private static int calculateCapacity(Object[] elementData, int minCapacity) { + //如果elementdata == {}空 + if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { + //DEFAULT_CAPACITY 默认10 + //返回下面2个参数的最大值 + return Math.max(DEFAULT_CAPACITY, minCapacity); + } + //不等于空返回 + return minCapacity; + } + +``` + +``` +private void ensureExplicitCapacity(int minCapacity) { //10 + modCount++; // 自增长 + + // 10 - 0 + if (minCapacity - elementData.length > 0) + grow(minCapacity); + } +``` + +```java +private void grow(int minCapacity) { // 10 + // oldCapacity 0 + int oldCapacity = elementData.length; // 0 + //newCapacity 0 + int newCapacity = oldCapacity + (oldCapacity >> 1);// 向右移动一位 + // 0 - 10 + if (newCapacity - minCapacity < 0) + //newCapacity 10 + newCapacity = minCapacity; + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + // 返回一个新的长度数组 + //elementData {} 付给了 newCapacity {,,,,,,,,,} + elementData = Arrays.copyOf(elementData, newCapacity); +} +``` + +> 第二次添加 + +```java +public boolean add(E e) { + ensureCapacityInternal(size + 1); // 2 + elementData[size++] = e; + return true; +} +``` + +```java +private void ensureCapacityInternal(int minCapacity) { // 2 + // ensureExplicitCapacity(10,2) + ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); +} + +private static int calculateCapacity(Object[] elementData, int minCapacity) { + // 10 == {} + if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { + return Math.max(DEFAULT_CAPACITY, minCapacity); + } + //无效返回 2 + return minCapacity; + } +``` + +```java +private void ensureExplicitCapacity(int minCapacity) { //2 + modCount++; + + // 2 - 10 不成立 继续往下执行 + if (minCapacity - elementData.length > 0) + grow(minCapacity); + } + +``` + +```java +public boolean add(E e) { + ensureCapacityInternal(size + 1); // 2 + elementData[size++] = e; // elementData[2] = e; + return true; + } +``` + +> 第11次添加 + +```java +public boolean add(E e) { + ensureCapacityInternal(size + 1); // 12 + elementData[size++] = e; + return true; + } + +``` + +```java +private void ensureCapacityInternal(int minCapacity) { //12 + ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); + } + +//参数是 10 12 +private static int calculateCapacity(Object[] elementData, int minCapacity) { + if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { + return Math.max(DEFAULT_CAPACITY, minCapacity); + } + //返回12 + return minCapacity; + } +``` + +```java +private void ensureExplicitCapacity(int minCapacity) { //12 + modCount++; + + // 12 - 10 > 0 + if (minCapacity - elementData.length > 0) + grow(minCapacity); + } + +private void grow(int minCapacity) { //12 + // oldCapacity 10 + int oldCapacity = elementData.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); // 10 + 5 + // 15 - 10 + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + // MAX_ARRAY_SIZE 是指integer的最大值 这个地方始终都是小于0 + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + //elementData 10 {1,2,3,4,5,6,7,8,9,10} + //newCapacity 15 {1,2,3,4,5,6,7,8,9,10,,,,,} + //也就是在原来的基础上复制到另一个新的数组中 + elementData = Arrays.copyOf(elementData, newCapacity); + } + +public boolean add(E e) { + ensureCapacityInternal(size + 1); // 12 + elementData[size++] = e; elementData[12] = e; + return true; + } + +``` + + + +##### get获取源码分析 + +```java +public E get(int index) { // 1 + //检查下标是否合法 + rangeCheck(index); + + //返回指定下标处的数据 + return elementData(index); + } + +private void rangeCheck(int index) { + if (index >= size) // 下标如果大于 长度 + //就抛出异常索引越界 + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } +``` + + + +##### set获取源码分析 + +```java +// 下标为1 数据 11 +public E set(int index, E element) { // 1 11 + //检查下标是否合法 否则抛出索引越界 + rangeCheck(index); + //获取到索引处数据 + E oldValue = elementData(index); + //把索引处数据进行赋值 + elementData[index] = element; + //返回原来的数据 + return oldValue; + } +``` + + + +##### remove获取源码分析(数组长度不变) + +```java +public E remove(int index) { // 1 + //检查索引是否合法 + rangeCheck(index); + + modCount++; + //获取到索引处数据 + E oldValue = elementData(index); + //15 - 1 - 1 = 13 + int numMoved = size - index - 1; + //13 > 0 + if (numMoved > 0) + //源数组{1,2,3,4,5,6,7,8,9} + //目标数组{1,3,4,5,6,7,8,9,null} + //这边有一个这样的操作 就是覆盖掉了 + // 源数组 开始下标 目标数组 开始下标 长度 + System.arraycopy(elementData, index+1, elementData, index, + numMoved); + elementData[--size] = null; // 把原来的数组 下标为 14的赋值为null + //返回删除的数据 + return oldValue; + } +``` + + + +##### FailFast机制 + +​ 快速失败的机制,java集合类为了应对并发访问在集合迭代过程中,内部结构发生变化的一种防护措施,这种错误检查的机制为这种可能发生错误通过抛出java.util.ConcurrentModificationException . + +实例: + +```java +// 创建2个线程类 +public class ThreadIterator extends Thread { + + private List list; + + public ThreadIterator(List list){ + this.list = list; + } + + @Override + public void run() { + while (true){ + for (Iterator iteratorTmp = list.iterator();iteratorTmp.hasNext();){ + iteratorTmp.next(); + try { + Thread.sleep(5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } +} + + +public class ThreadAdd extends Thread{ + + private List list; + + public ThreadAdd(List list){ + this.list = list; + } + + + + @Override + public void run() { + for (int i = 0; i < 100 ; i++){ + System.out.println("循环执行"+i); + try { + Thread.sleep(5); + list.add(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + } +} +``` + +```java +//测试类 +public class JavaTest { + + private static List list = new ArrayList(); + + public static void main(String[] args) { + + new ThreadIterator(list).start(); + new ThreadAdd(list).start(); + + } +} + +``` + +![1595866443956](C:\Users\28768\AppData\Roaming\Typora\typora-user-images\1595866443956.png) + +![1595866558512](C:\Users\28768\AppData\Roaming\Typora\typora-user-images\1595866558512.png) + +![1595866582549](C:\Users\28768\AppData\Roaming\Typora\typora-user-images\1595866582549.png) + +![1595866618760](C:\Users\28768\AppData\Roaming\Typora\typora-user-images\1595866618760.png) + +2条线程导致 modCount != expectedModCount 就会报错 + + + +> 有参构造 +> +> ``` +> public ArrayList(int initialCapacity) { +> if (initialCapacity > 0) { +> //出事长度大于0 就创建制定大小的数组 +> this.elementData = new Object[initialCapacity]; +> } else if (initialCapacity == 0) { +> //否则就赋值一个空数组 +> this.elementData = EMPTY_ELEMENTDATA; +> } else { +> //否则抛出异常 +> throw new IllegalArgumentException("Illegal Capacity: "+ +> initialCapacity); +> } +> } +> ``` +> + + + +#### LinkedList + +LinkedList是通过双向链表去实现的。他的数据结构具有双向链表的优缺点,既然是双向链表,那么他的顺序访问效率会非常高,而且随机访问的效率会比较低,它包含一个非常重要的私有内部静态类:node; + +```java +private static class Node { + E item;//节点的元素 + Node next;//下一个节点 + Node prev;//上一个节点 + + Node(Node prev, E element, Node next) { + this.item = element; + this.next = next; + this.prev = prev; + } + } +``` + +```java +public void push(E e) { //push往链表的头部去加 + addFirst(e); + } + +public void addFirst(E e) { + linkFirst(e); + } + +private void linkFirst(E e) { + final Node f = first; //头部 + final Node newNode = new Node<>(null, e, f);//把我们的数据e放在头部 + first = newNode; + if (f == null) + last = newNode; + else + f.prev = newNode; + size++; + modCount++; + } +``` + +```java +public void add(int index, E element) { //add 从尾部去加 + checkPositionIndex(index); + + if (index == size) + linkLast(element); + else + linkBefore(element, node(index)); + } + +void linkLast(E e) { + final Node l = last; + final Node newNode = new Node<>(l, e, null); + last = newNode; + if (l == null) + first = newNode; + else + l.next = newNode; + size++; + modCount++; + } + +``` + + + +get方法:本质上还是便利 + +```java +public E get(int index) {//获取对应下标的值 + checkElementIndex(index); + return node(index).item; + } + +Node node(int index) { + // assert isElementIndex(index); + + if (index < (size >> 1)) {//如果index < 长度的一般 + Node x = first; //从头开始循环 + for (int i = 0; i < index; i++) + x = x.next; + return x; + } else { + Node x = last; // 从尾部开始循环 + for (int i = size - 1; i > index; i--) + x = x.prev; + return x; + } + } +``` + +set方法: + +```java +public E set(int index, E element) { + checkElementIndex(index);//检查下标是否合法 + Node x = node(index);//根据下标获取node对象 + E oldVal = x.item;//记录原来的值 + x.item = element;//赋予新的值 + return oldVal;//返回修改之前的值 + } +``` + +#### Vector + +和ArrayList很相似,都是以动态数组的形式存储数据。 + +vector是线程安全的。 + +每个方法都有加synchronized关键字所修饰。对性能有比较大的影响慢慢就被淘汰了。 + + + +Collections + +```java +collecitons中的synchronized可以增加代码的灵活度,需要同步的时候就通过下面的代码实现 + +List list1 = Collections.synchronizedList(list); + +``` + + + +## set接口 + +### 1.HashSet + +>概述: +> +>​ HashSet 实现set接口,由哈希表支持,他不保证set的迭代顺序,特别是他不保证该顺序永久不变。允许使用null作为元素。 +> +>特点: +> +>​ 底层数据结构是哈希表,HashSet的本质是一个“没有重复元素的集合”,他是通过`HashMap`实现的HashSet中包含一个HashMap类型的成员变量`map`. + +```java +private transient HashMap map; //成员变量map + +public HashSet() {//无参构造器 + map = new HashMap<>(); + } + +``` + + + +add方法:本质上试讲数据保持在HashMap中,key是我们添加的内容,value就是我们定义的一个object对象。 + +```java +public boolean add(E e) { + return map.put(e, PRESENT)==null; + } +``` + + + + + +### 2.TreeSet + +> 概述: +> +> ​ 基于TreeMap的NavigableSet实现,使用元素的自然顺序进行排序,后者根据创建set时提供的Comparator进行排序,具体取决于使用的构造方法。 +> +> +> +> 本质是将数据保存在TreeMap中,key是我们添加的内容,value是定义的一个object对象。 + +```java +public TreeSet() { + this(new TreeMap()); + } +``` + + + +## Map 接口: + +> Map集合的特点: +> +> ​ 1.能够存储唯一的列的数据key(唯一不可重复)set +> +> ​ 2.能够存储可以重复的数据(可重复)List +> +> ​ 3.值的顺序取决于键的顺序 +> +> ​ 4.键和值都可以存储null元素 + +### TreeMap + +本质上就是红黑树的实现: + +```` +1.每个节点要么是红色,要么是黑色。 + +2.根节点必须是黑色。 + +3.每个叶子节点【NIL】是黑色。 + +4.每个红色节点的两个子节点必须是黑色。 + +5.任意节点到每个叶子节点的路劲包含相同数量的黑节点。 +```` + +```java +private transient Entry root; //根节点就是entry对象 + + +K key;//键 +V value;//值 +Entry left;//左子节点 +Entry right;//右子节点 +Entry parent;//父节点 +boolean color = BLACK;//节点的颜色 默认黑色 +``` + +map.put: + +```java +public V put(K key, V value) { + //将root赋值给局部变量 初始为null + Entry t = root; + //第一次添加 root为空 + if (t == null) { + //初始操作 + //检测key是否为空 + compare(key, key); // type (and possibly null) check + //将要添加的key.value封装成一个entry对象,并赋值给root + root = new Entry<>(key, value, null); + size = 1; + modCount++; + return null; + } + int cmp; + Entry parent;//父节点 + // split comparator and comparable paths + Comparator cpr = comparator;//获得比较器 + if (cpr != null) { + //一直找到插入节点的父节点 + do { + //将root赋值给parent + parent = t; + //和root节点比较值的大小 + cmp = cpr.compare(key, t.key); + + if (cmp < 0) + //将父节点的左子节点赋值给t + t = t.left; + else if (cmp > 0) + //将父节点的右节点赋值给t + t = t.right; + else + //直接和父节点的key相等 修改值 + return t.setValue(value); + } while (t != null); + } + else { + //比较器不存在的情况 + + if (key == null) + throw new NullPointerException(); + @SuppressWarnings("unchecked") + Comparable k = (Comparable) key; + do { + parent = t; + cmp = k.compareTo(t.key); + if (cmp < 0) + t = t.left; + else if (cmp > 0) + t = t.right; + else + return t.setValue(value); + } while (t != null); + }//t 就是我们要插入节点的父节点 parent + + //将我们要插入的key value 封装成一个entry对象 + Entry e = new Entry<>(key, value, parent); + if (cmp < 0) + parent.left = e;//插入节点在父节点左侧 + else + parent.right = e;//插入节点在父节点右侧 + fixAfterInsertion(e);//实现红黑树的平衡 + size++; + modCount++; + return null; + } +``` + +fixAfterInsertion:实现平衡的方法 + +```java +private void fixAfterInsertion(Entry x) { + //设置添加节点的颜色为红色 + x.color = RED; + //循环的条件 x添加的节点不是空 不是root节点 父节点颜色为红色 + while (x != null && x != root && x.parent.color == RED) { + //父节点 是否 是祖父节点 的左侧节点 + if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { + //获取父节点的兄弟节点(叔叔节点) + Entry y = rightOf(parentOf(parentOf(x))); + if (colorOf(y) == RED) { //叔叔节点是红色 + //变色 + setColor(parentOf(x), BLACK);//设置父节点为黑色 + setColor(y, BLACK);//设置叔叔节点的颜色为黑色 + setColor(parentOf(parentOf(x)), RED);//设置祖父节点的颜色是红色 + x = parentOf(parentOf(x));//将祖父节点设置为插入的节点 + } else {//叔叔节点是黑色 + if (x == rightOf(parentOf(x))) {//判断插入节点是否是父节点的右侧节点 + x = parentOf(x);//将父节点作为插入节点 + rotateLeft(x);//左旋 进行位置的调整 + } + setColor(parentOf(x), BLACK);//设置父节点为黑色 + setColor(parentOf(parentOf(x)), RED);//设置祖父节点为红色 + rotateRight(parentOf(parentOf(x)));//以祖父节点做右旋 + } + } else {//父节点是祖父节点的右侧子节点 + //获取叔叔节点 + Entry y = leftOf(parentOf(parentOf(x))); + if (colorOf(y) == RED) {//叔叔节点为红色 + //变色 + setColor(parentOf(x), BLACK); + setColor(y, BLACK); + setColor(parentOf(parentOf(x)), RED); + x = parentOf(parentOf(x)); + } else { + //插入几点在父节点的右侧 + if (x == leftOf(parentOf(x))) { + x = parentOf(x); + rotateRight(x);//右旋操作 + } + setColor(parentOf(x), BLACK); + setColor(parentOf(parentOf(x)), RED); + rotateLeft(parentOf(parentOf(x)));//左旋 + } + } + } + //根节点的颜色为黑色 + root.color = BLACK; + } +``` + + + +​ + + + + + + + +### HashMap + +> 1.HashMap底层结构 +> +> ​ jdk1.7以及以前是采用数组加 链表 +> +> ​ jdk1.8之后 采用数组+链表 或者是 数组+红黑树的方式进行元素的存储 +> +> 2.保存在hashmap集合中的元素都是将一个map.entry内部接口的实现 + +```java + //默认的hashmap中数组的长度默认是16 + static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 + + //hashmap中数组的最大容量 + static final int MAXIMUM_CAPACITY = 1 << 30; + + //默认的扩容平衡因子 + static final float DEFAULT_LOAD_FACTOR = 0.75f; + + //链表转红黑树的临界值 + static final int TREEIFY_THRESHOLD = 8; + + //红黑树转链表的临界值 + static final int UNTREEIFY_THRESHOLD = 6; + + //链表转红黑树的数组长度 的临界值 + static final int MIN_TREEIFY_CAPACITY = 64; + + + //hashmap中的数组结构 + transient Node[] table; + + // + transient Set> entrySet; + + //hashmap中的元素个数 + transient int size; + + //对hashmap的操作次数 + transient int modCount; + + //扩容的临界值 + int threshold; + + //实际的扩容值 + final float loadFactor; +``` + + + +```java +public V put(K key, V value) { + return putVal(hash(key), key, value, false, true); + } + +//hash(key) 获取key对应的哈希值 +static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//为什么要右移16位 + } + + +final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; Node p; int n, i; + //初始判断 + if ((tab = table) == null || (n = tab.length) == 0) + //resize()第一次 初始数组 n 数组的长度 初始的时候获取 了一个容量为16的数组 + n = (tab = resize()).length; + //确定插入的key在数组中的下标 + if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + else { + Node e; K k; + if (p.hash == hash && + ((k = p.key) == key || (key != null && key.equals(k)))) + e = p; + else if (p instanceof TreeNode) + //表示数组中存放的节点是一个红黑树节点 + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + else { + for (int binCount = 0; ; ++binCount) { + //p.next 就是下一个节点 + if ((e = p.next) == null) { + //将新的节点添加到了链表的尾部 + p.next = newNode(hash, key, value, null); + //判断是否满足 链表转红黑树的 条件(链表长度 等于8 链表转红黑树) + if (binCount >= TREEIFY_THRESHOLD - 1) + //转红黑树 + treeifyBin(tab, hash); + break; + } + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + if (e != null) { // existing mapping for key + V oldValue = e.value; + if (!onlyIfAbsent || oldValue == null) + e.value = value; + afterNodeAccess(e); + return oldValue; + } + } + ++modCount; + if (++size > threshold) + resize(); + afterNodeInsertion(evict); + return null; + } + + +//转红黑树 +final void treeifyBin(Node[] tab, int hash) { + int n, index; Node e; + //如果tab为空 或者 数组的长度小于 64 + if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) + //就扩容 + resize(); + //否则 + else if ((e = tab[index = (n - 1) & hash]) != null) { + //链表转红黑树的逻辑 + TreeNode hd = null, tl = null; + do { + TreeNode p = replacementTreeNode(e, null); + if (tl == null) + hd = p; + else { + p.prev = tl; + tl.next = p; + } + tl = p; + } while ((e = e.next) != null); + if ((tab[index] = hd) != null) + hd.treeify(tab); + } + } +``` + + + +第一次resize() + +```java +final Node[] resize() { + Node[] oldTab = table; + int oldCap = (oldTab == null) ? 0 : oldTab.length; + int oldThr = threshold; + int newCap, newThr = 0; + if (oldCap > 0) { + if (oldCap >= MAXIMUM_CAPACITY) { + threshold = Integer.MAX_VALUE; + return oldTab; + } + else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && + oldCap >= DEFAULT_INITIAL_CAPACITY) + newThr = oldThr << 1; // double threshold + } + else if (oldThr > 0) // initial capacity was placed in threshold + newCap = oldThr; + else { // zero initial threshold signifies using defaults + newCap = DEFAULT_INITIAL_CAPACITY; + newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); + } + if (newThr == 0) { + float ft = (float)newCap * loadFactor; + newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? + (int)ft : Integer.MAX_VALUE); + } + threshold = newThr; + @SuppressWarnings({"rawtypes","unchecked"}) + Node[] newTab = (Node[])new Node[newCap]; + table = newTab; + if (oldTab != null) { + for (int j = 0; j < oldCap; ++j) { + Node e; + if ((e = oldTab[j]) != null) { + oldTab[j] = null; + if (e.next == null) + newTab[e.hash & (newCap - 1)] = e; + else if (e instanceof TreeNode) + ((TreeNode)e).split(this, newTab, j, oldCap); + else { // preserve order + Node loHead = null, loTail = null; + Node hiHead = null, hiTail = null; + Node next; + do { + next = e.next; + if ((e.hash & oldCap) == 0) { + if (loTail == null) + loHead = e; + else + loTail.next = e; + loTail = e; + } + else { + if (hiTail == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + } + } while ((e = next) != null); + if (loTail != null) { + loTail.next = null; + newTab[j] = loHead; + } + if (hiTail != null) { + hiTail.next = null; + newTab[j + oldCap] = hiHead; + } + } + } + } + } + return newTab; + } +``` + + + +动态扩容 + +```java +final Node[] resize() { + Node[] oldTab = table; + int oldCap = (oldTab == null) ? 0 : oldTab.length; + int oldThr = threshold; + int newCap, newThr = 0; + if (oldCap > 0) { + if (oldCap >= MAXIMUM_CAPACITY) { + threshold = Integer.MAX_VALUE; + return oldTab; + } + else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && + oldCap >= DEFAULT_INITIAL_CAPACITY) + //扩容 的临界值 + newThr = oldThr << 1; // double threshold + } + else if (oldThr > 0) // initial capacity was placed in threshold + newCap = oldThr; + else { // zero initial threshold signifies using defaults + newCap = DEFAULT_INITIAL_CAPACITY; + newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); + } + if (newThr == 0) { + float ft = (float)newCap * loadFactor; + newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? + (int)ft : Integer.MAX_VALUE); + } + threshold = newThr; + @SuppressWarnings({"rawtypes","unchecked"}) + //创建数组的长度是32 + Node[] newTab = (Node[])new Node[newCap]; + table = newTab; + if (oldTab != null) {//初始的时候 是不需要复制的 + for (int j = 0; j < oldCap; ++j) { + Node e; + if ((e = oldTab[j]) != null) { + oldTab[j] = null; + if (e.next == null) + newTab[e.hash & (newCap - 1)] = e; + else if (e instanceof TreeNode) + //移动红黑树的节点 + ((TreeNode)e).split(this, newTab, j, oldCap); + else { // preserve order + //普通链表的移动 + Node loHead = null, loTail = null; + Node hiHead = null, hiTail = null; + Node next; + do { + next = e.next; + if ((e.hash & oldCap) == 0) { + if (loTail == null) + loHead = e; + else + loTail.next = e; + loTail = e; + } + else { + if (hiTail == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + } + } while ((e = next) != null); + if (loTail != null) { + loTail.next = null; + newTab[j] = loHead; + } + if (hiTail != null) { + hiTail.next = null; + newTab[j + oldCap] = hiHead; + } + } + } + } + } + return newTab; + } +``` + + + + + + + + + +## + + + + + + + + + + + + + + + + + + + + + diff --git "a/lixiaoyu/JAVA/\344\272\214\350\277\233\345\210\266\350\275\254\345\215\201\350\277\233\345\210\266.png" "b/lixiaoyu/JAVA/\344\272\214\350\277\233\345\210\266\350\275\254\345\215\201\350\277\233\345\210\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..19b28a32bf9cc4e1e2e9b6c5cbec5bdf88226f1b Binary files /dev/null and "b/lixiaoyu/JAVA/\344\272\214\350\277\233\345\210\266\350\275\254\345\215\201\350\277\233\345\210\266.png" differ diff --git "a/lixiaoyu/JAVA/\345\215\201\350\277\233\345\210\266\350\275\254\344\272\214\350\277\233\345\210\266.png" "b/lixiaoyu/JAVA/\345\215\201\350\277\233\345\210\266\350\275\254\344\272\214\350\277\233\345\210\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..d8454061b24291dff6e1de31829dc2ae00224995 Binary files /dev/null and "b/lixiaoyu/JAVA/\345\215\201\350\277\233\345\210\266\350\275\254\344\272\214\350\277\233\345\210\266.png" differ diff --git "a/lixiaoyu/JAVA/\347\272\242\351\273\221\346\240\221.png" "b/lixiaoyu/JAVA/\347\272\242\351\273\221\346\240\221.png" new file mode 100644 index 0000000000000000000000000000000000000000..17319dbfb4a5ff9c5b26d09ebc32609581d415db Binary files /dev/null and "b/lixiaoyu/JAVA/\347\272\242\351\273\221\346\240\221.png" differ diff --git a/lixiaoyu/mysql/sqlFunction.md b/lixiaoyu/mysql/sqlFunction.md new file mode 100644 index 0000000000000000000000000000000000000000..5043feb9129d05dcbf89405437c2e4b98df7cdd9 --- /dev/null +++ b/lixiaoyu/mysql/sqlFunction.md @@ -0,0 +1,373 @@ +## 变量 + +### 系统变量 + +``` +说明:变量由系统提供,不是用户自定义,属于服务器层面。(比如用户个隔离级别...等等) + +全局变量: + 语法: + 1.查看所有的系统变量 + show global|session variables; + 2.查看满足条件的部分变量 + show global|session variables like '%char%'; -- '%char%' 模糊查询 char某字段. + 3.查看指定的某个系统变量的值 + select @@系统变量名; + 4.为某个系统变量赋值 + set global|session 系统变量名 = 值; + + set @@global|session 系统变量 = 值; + +会话变量: +作用域:仅仅针对于 当前会话(连接)有效 + + + 1.查看所有的变量 + show varibales; + show session variables; + 2.查看部分的会话变量 + show variables like '%char%'; + show session variables like '%char%'; + 3.查询某个指定的会话变量 + select @@tx_isolation; + select @@session.tx_isolation; + 4.为某个会话赋值 + set @@session.tx_isolation = 'read-uncommitted'; + + set session tx_isolation = 'read-uncommitted''; + + +``` + +### 自定义变量 + +``` +说明:由用户自定义 + +应用在任何地方 ,函数存储过程里面 后者是 外面。 + +使用步骤: + + + + 用户变量:针对于当前会话(连接)有效,同于会话变量 + 1.声明并初始化 + set @用户变量名 = 值;或者 + set @用户变量名:=值;或者 + select @用户变量名:=值; + 2.更新用户变量的值 + 1.set / select + set @用户变量名 = 值;或者 + set @用户变量名:=值;或者 + select @用户变量名:=值; + 2.select into + select 字段 into 变量名 + from 表; + 3.使用(查看用户变量的值) + select @用户变量名; + 示例: + 声明: set @m = 1; set @n = 2; + 赋值:set @sum = @m + @n; + 查看:select @sum; + 结果:@sum = 3; + + + + 局部变量: + + 作用域:仅仅在定义它的函数或者是存过过程中 的第一句话 + + 1.声明: + declare 变量名 类型; + declare 变量名 类型 default 值; + 2.赋值: + 1.set / select + set 局部变量名 = 值;或者 + set 局部变量名:=值;或者 + select 局部变量名:=值; + 2.select into + select 字段 into 变量名 + from 表; + 3.使用 + select 局部变量名; + + + + + + + + + +``` + + + +## 存储过程和函数 + +``` +存储过程和函数就类似于java中的方法。 +好处: + 1.提高代码的重用 + 2.简化操作 +``` + + + +### 存储过程 + +> 含义:一组预先编译好的sql语句的集合。 +> +> ``` +> 1.提高代码的重用性。 +> 2.简化操作 +> 3.减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率。 +> ``` +> +> + +#### 创建语法 + +``` +create procedure 存储过程名(参数列表) +begin + + 存储过程体(一组合法有效的sql语句) + +end; + +注意: + 1.参数列表包含3部分 + 参数模式 参数名 参数类型 +举例: in sname varchar(20) +参数模式: + in:该参数模式可以作为输入(也就是说该参数需要调用方传入值) + out:该参数模式可以作为输出(也就是说该参数只可以作为返回值) + inout:该参数模式可以作为输入也可以作为输出(也就是说该参数既可以作为传入值也可以作为返回值) + + 2.如果存储过程体 里面只有一句话 那么,begin end;可以省略 + 存储过程体重每一条sql语句的结尾要求必须加分号。 + 存储过程结果可以使用delimiter 重新设置 + 语法: + delimiter 结束标记 + 案例: + delimiter $ +``` + +#### 调用语法 + +``` +call 存储过程名(实参列表); + +空参列表的存储过程: +示例: + 创建: + create procedure guocheng_1() + begin + insert into student(id,name,sex,age) + values('1','李晓钰','男','22'); + end $ + 调用: + call guocheng_1()$ +``` + +> 参数模式 in:参数模式可以作为输入 +> +> ``` +> 参数模式不写默认是in +> 示例1: +> 创建: +> create procedure guocheng_1(in agentcode varchar(20)) +> begin +> select name from laagent where agentcode = agentcode; +> end $ +> 调用: +> call guocheng_1('1231212121')$ +> 示例2: +> 创建: +> create procedure guocheng_1(in agentcode varchar(20),in ssex varchar(10)) +> begin +> declare ssname varchar(10) default '0'; +> select +> name into ssname +> from +> laagent +> where agentcode = agentcode and sex = ssex; +> +> select if(ssname<>0,ssname,'没有查到数据'); +> -- 调用存储过程以后 就会打印查询出的值 +> +> end $ +> 调用: +> call guocheng_1('1231212121','0')$ +> ``` +> +> + +> 参数模式out:该参数只可以作为返回值 +> +> ``` +> 示例1: +> 创建: +> create procedure guocheng_1(in agentcode varchar(20),out ssex varchar(10)) +> begin +> select +> name into ssex +> from +> laagent +> where agentcode = agentcode; +> end $ +> 调用: +> 第一种: +> call guocheng_1('1231212121',@bname)$ +> select @bname$ +> 第二种: +> set @bname$ -- 定义一个变量 +> call guocheng_1('1231212121',@bname)$ +> select @bname$ +> ``` +> +> + +> 参数模式inout:该参数模式可以作为输入也可以作为输出 +> +> ``` +> 示例1: +> 创建: +> create procedure guocheng_1(inout a int,inout b int) +> begin +> set a = a*2; +> set b = b*2; +> end $ +> 调用: +> 在此过程中首先得创建两个变量 +> set @m = 10; +> set @n = 20; +> 然后传入存储过程 +> call guocheng_1(@m,@n)$ +> 其次进行查询返回的内容 +> select @m,@n; +> ``` +> +> + + + +#### 删除存储过程 + +``` +语法:存储过程一次只能删除一个 + drop procedure 存储过程名 +``` + + + +#### 查看存储过程的信息 + +``` +查看表的信息的时候我们会用到 + desc 表名; +查看存储过程desc是不管用的这里要用到 + show create procedure 存储过程名; +``` + +#### 修改存储过程 + +``` +修改存储过程sql语句是不支持修改的。可以使用数据库常用的工具进行修改例如:navicat。 +``` + + + +### 函数 + +``` +1.提高代码的重用性。 +2.简化操作 +3.减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率。 +``` + +> 函数和存储过程的区别 +> +> ``` +> 存储过程:可以没有返回值,也可以有多个返回值。 +> 使用场景:适合做批量插入,更新。 +> +> 函数:有且只有一个返回值。 +> 使用场景:适合做处理数据后返回一个结果。 +> ``` +> +> + +#### 创建语法 + +> 注意: +> +> ​ 1.参数列表包含两个部分: 参数名 参数类型 +> +> ​ 2.函数体:一定会有return。没有会报错。建议放在最后面。 +> +> ​ 3.函数体中仅一句话,可以省略 begin end +> +> ​ 4.使用delimiter语句设置结束标记。 + +``` +create function 函数名(参数列表) return 返回值类型 +begin + 函数体 +end; +``` + +#### 调用语法 + +``` +select 函数名(参数列表); +``` + +#### 案例 + +``` +案例一: + 无参有返回值 + create function demo() return int + begin + declare num int default 0; + + select + count(*) into num + from + laagent; + return num; + end $ ($ 是结束语) + + select demo()$ +案例二: + 有参有返回值 + create function demo2(name varchar(20)) return double + begin + set @money = 0; -- 定义用户变量 + select + money into @money + from + laorderinfo + where username = name; + return @money; + end $ + + select demo2('lixiaoyu') $ +``` + + + +#### 查看函数 + +``` +show create function 函数名; +``` + +#### 删除函数 + +``` +drop function 函数名; +``` + diff --git a/lixiaoyu/mysql/sqlNote.md b/lixiaoyu/mysql/sqlNote.md new file mode 100644 index 0000000000000000000000000000000000000000..fbe2857f6cc6ccfeea2058b9b0d279f2bdf87271 --- /dev/null +++ b/lixiaoyu/mysql/sqlNote.md @@ -0,0 +1,900 @@ +### 按照年代分类 sql分为92和99 推荐99 +#### SQL92仅仅支持内连接 就是多表关联用逗号连接 +#### SQL99 支持 内连接 + 外连接(左外和右外不支持全外) + 交叉连接 + +#### sql执行顺序 +select ---> from ---> where ---> group by ---> having ---> order by ---> limit + + + +#### 获取当前日期 + +``` +获得当前日期 +SELECT NOW(),CURDATE(),CURTIME() + +获取前一天 +SELECT DATE_SUB(CURDATE(),INTERVAL 1 DAY); + +获取后一天 +SELECT DATE_SUB(CURDATE(),INTERVAL -1 DAY); + +根据录入日期获取当月最后一天 +select LAST_DAY(20200316) + +根据录入日期获取当月第一天一天 +select first_day(20200316) + +-- 根据录入日期得到周一 +select subdate('20200611',weekday('20200611')); + +-- 根据录入日期得到周天 +select subdate('20200611',weekday('20200611')-6); + + +年份差 +SELECT TIMESTAMPDIFF(YEAR,'2017-05-01', DATE_FORMAT(now(), '%Y-%m-%d')) + +月份差 +SELECT TIMESTAMPDIFF(MONTH,'2017-05-01', DATE_FORMAT(now(), '%Y-%m-%d')) + +根据年月获取到月份差 +select PERIOD_DIFF('202004',DATE_FORMAT(now(), '%Y%m')) + +天数差 +(1)SELECT datediff(DATE_FORMAT(now(), '%Y-%m-%d'),DATE_FORMAT('2018-09-10','%Y-%m-%d')) +(2)SELECT TIMESTAMPDIFF(DAY,'2017-05-01', DATE_FORMAT(now(), '%Y-%m-%d')) + +``` + +#### sql 在 ... 和 ... 之间 (between ... and ...) +#### 查询 入职时间在 2019-08-09 和 2020-03-04 之间的人员数据 >=2019-08-09 和 <=2020-03-04 + +``` +select * from laagent where employdate between '2019-08-09' and '2020-03-04' + +``` + + +#### update 用and 连接错误用法 + +``` +表: a b c + 1 1 2 + +例如:1>、update 表 set a = 2 and b = 2 where c = 2; 结果:a = 0 , b = 1; + 2>、update 表 set a = 2 and b = 1 where c = 2; 结果:a = 1 , b = 1; + +``` + +#### 常用函数的使用 + +``` + select version() -- 当前数据库版本 + select DATABASE() -- 当前库 + select user() -- 当前用户 + + //大小写转换 + >select upper('asdf') 结果:ASDF + + >select lower('ASDF') 结果:asdf + + + //拼接 concat(a,b,c,d) + >select concat('wo 是','_','li',':','xiaoyu') 结果:wo 是_li:xiaoyu + + //mysql索引是从 1 开始的 + + //返回第一次出现的索引 + instr(12312312,3) 结果:3 + + //去掉前后空格 + >select trim(' li li ') 结果:li li + >select trim('a' from 'aaaa张aaa无忌aaa') 结果:张aaa无忌(去掉前后a) + + //填充 左填充:lpad(字段名,填充的长度,填充的字符) 右填充:rpad(字段名,填充的长度,填充的字符) + >select lpad('张无忌的师傅',10,'张三丰是') 结果:张三丰是张无忌的师傅 + >select rpad('张三丰',10,'是张无忌的师傅') 结果:张三丰是张无忌的师傅 + + //替换 replace() + >select replace('abcdefg','bcd','BCD') 结果:aBCDefg + + //ceil():向上取整。返回>=该参数的最小整数 + + //floor():向下取整。返回<=该参数的最大整数 + + //mod():取余 同java中的%。 mod(a,b) a-a/b*b + + //count(字段): 统计该字段非空值的个数 + //count(*): 统计结果集的行数 + + //now():当前日期时间 + //curdate():当前日期不包括时间 + //curtime():当前时间不包括日期 + //year年 month月 monthname(now()):返回英文当前月份 + + + //1.mysql日期和字符相互转换方法 + date_format(date,’%Y-%m-%d’) ————–>oracle中的to_char(); + str_to_date(date,’%Y-%m-%d’) ————–>oracle中的to_date(); + + %Y:代表4位的年份 + %y:代表2为的年份 + + %m:代表月, 格式为(01……12) + %c:代表月, 格式为(1……12) + + %d:代表月份中的天数,格式为(00……31) + %e:代表月份中的天数, 格式为(0……31) + + %H:代表小时,格式为(00……23) + %k:代表 小时,格式为(0……23) + %h: 代表小时,格式为(01……12) + %I: 代表小时,格式为(01……12) + %l:代表小时,格式为(1……12) + + %i: 代表分钟, 格式为(00……59) 【只有这一个代表分钟,大写的I 不代表分钟代表小时】 + + %r:代表 时间,格式为12 小时(hh:mm:ss [AP]M) + %T:代表 时间,格式为24 小时(hh:mm:ss) + + %S:代表 秒,格式为(00……59) + %s:代表 秒,格式为(00……59) + + //2.例子: + select str_to_date(‘09/01/2009’,’%m/%d/%Y’) + + select str_to_date(‘20140422154706’,’%Y%m%d%H%i%s’) + + select str_to_date(‘2014-04-22 15:47:06’,’%Y-%m-%d %H:%i:%s’) + +``` +## 加密函数 + +``` +select password(字符串); + +select md5(字符串); + +``` + + + + +#### case when 使用: + +``` +1:case 判断字段 + when 常量1 then 值 + when 常量2 then 值 + when 常量3 then 值 + when 常量4 then 值 + end; +``` + +``` +2:case when 判断条件1 + then 值 when 判断条件2 + then 值 when 判断条件3 + then 值 when 判断条件4 + else 值 + end; +``` + +#### if...else.... + +``` +1、if 判断条件1 then + 值; + elseif 判断条件2 then + 值; + elseif 判断条件3 then + 值; + else + 值; + end if; + +2、if 判断条件 then + 值; + else + 值; + end if; +``` + +#### union 和 union all + +``` + +union:两个表都有的数据(就是union all取出冗余的数据) + +union all:两个表的数据全部展示 + +如果需要展示不同的数据比如count只有一条数据:就可以使用union 或者 union all +(表的别名一定要不同 否则数据有问题) + +应用场景:要查询的结果来自于多个表,并且并且多个表没有直接的连接关系,但是查询的数据是一致的。 + +特点: + 1、要求多条查询语句的查询列数是一致的。 + 2、要求多条查询语句查询的列数是一致的。 + 3、union默认是去重,union all 可以包含重复项 + +案例:查询每个国家的人数的总个数 + select concat(*) as '个数',name as '国家' from country where name = '中国' + union + select concat(*),name from country where name = '美国' + union + select concat(*),name from country where name = '韩国' + + + +``` +#### group by 使用 + +``` +group by :select 分组函数(avg,max,count,sum,min) + from 表 + [where 筛选条件] + group by 分组列表 + [order by 子句] 排序 默认:asc + +``` + +#### having使用 + +```mysql +having可以筛选分组后的各组数据 +示例: + -- 查询每个工资大于2000的事业部的工资 + select sum(money) from laagent group by managecom having sum(money) > 2000; + +having也可以单独使用 +示例: + -- 查询工资大于 2000 的人 + select * from laagent having money > 2000; + #与下面查询的查询结果是一致的 ↓ + select * from laagent where money > 2000; + +``` + +#### order by使用 + +```mysql +#ORDER BY 默认的排序是升序的 asc +示例: + select * from laagent order by agentcode; #升序 + select * from laagent order by agentcode desc;#降序 + + #班级号升序排序,然后再在班内按学号降序排序的操作如下: + SELECT * FROM stu ORDER BY Cno,Sno DESC; +``` + +#### limit使用 + +```mysql +1.limit 可以被用于强制select查询返回的记录数。 +2.limit 语句放在查询语句的最后 +3.select * from 表 limit 0,5; -- 0 从下标为0的开始 也就是第一条数据。5 是一页显示5条 +公式: 页数page , 每页的条数size select * from 表 limit (page-1)*size,size; + + 例如:每页10条,找第8页 + limit (8-1)*10,10; +``` + +#### exists 和 not exists + +```mysql +-- 表stud1中 存在 stud1中name 和 stud2 中name相等数据的 +select * from stud1 a where exists (select 1 from stud2 b where a.name = b.name) + +-- 表stud1中 存在stud1中name 和 stud2 中name不相等数据的 +select * from stud1 a where not exists (select 1 from stud2 b where a.name = b.name) + +-- 表stud2中 不存在 表1和表2 相等的name数据 +select * from stud2 a where not exists (select 1 from stud1 b where a.name = b.name) + +``` + +#### cast(属性值 as 数据类型) 用来转换类型 + +``` +可以转换的类型是有限制的。这个类型可以是以下值其中的一个: + + 二进制,同带binary前缀的效果 : BINARY + 字符型,可带参数 : CHAR() + 日期 : DATE + 时间: TIME + 日期时间型 : DATETIME + 浮点数 : DECIMAL + 整数 : SIGNED + 无符号整数 : UNSIGNED + +``` + +#### 向上取整:ceiling() 向下取整:floor() 返回最接近的函数:ROUND(23.3222,3) + +``` +select ceiling(23.4) ----- 24 + + +select floor(23.4) ------ 23 + + +1、select round(23.3222,3) ------ 23.322 保留小数点后3位 +2、select round(23.3222) ----- 23 四舍五入 + + +``` + + + +## 多表关联 + +### 等值连接 内连接 SQL99 inner join + +``` +SQL92: + +例如:select + a.id,a.name,a.age,a.sex,b.class + from + Student a,Classes b + where + a.id = b.id; + [有筛选条件:where 条件;] + +``` + +``` +SQL99: + +例如:select + a.id,a.name,a.age,a.sex,b.class + from + Student a + inner join + Classes b + on + a.id = b.id; + [有筛选条件:where 条件;] + + +``` + + +### 非等值连接 + +``` +SQL92: + +例如:select + a.id,a.name,a.age,a.sex,b.class + from + Student a,Classes b + where + between a.id and b.id; + +``` + +``` +SQL99: + +例如:select + a.id,a.name,a.age,a.sex,b.class + from + Student a + inner join + Classes b + on + between a.id and b.id; + +``` + +### 自连接 + +``` +SQL92: + +例如: 假如说同一张表里面 有id pid是id的上级,想查询id 对应的它的上级的编号和名字 + select + a.id,a.name,b.id,b.name + from + Student a,Student b + where a.id = b.pid; + +``` + +``` +SQL99: + +例如: 假如说同一张表里面 有id pid是id的上级,想查询id 对应的它的上级的编号和名字 + select + a.id,a.name,b.id,b.name + from + Student a + inner join + Student b + on + a.id = b.pid; + +``` + +### 外连接 一般的应用场景就是查询:一个表中有,另一个表中没有的数据 + +>外连接查询 = 内连接结果 + 主表中有而从表中没有的数据(null); + +``` + +left join 左连接 左边是主表 + + +right join 右连接 右边是主表 + + +pull 全外连接 = 内连接结果 + 表1中有表2中没有 + 表2中有表1中没有 (mysql不支持) +``` + +### 交叉连接 cross join(测试) + +``` + + +``` + + + + +## 子查询 出现在其他语句中的select语句 叫做子查询或者是内查询 +### 子查询出现的位置 + +>select 后面 + +``` + +支持标量子查询 + + +案例:查询每个班级的学生个数 +select a.*,(select count(*) from student where id = a.pid) where class a ; + + +``` + +>from 后面 + +``` +支持表子查询 + +案例:查询每个员工的工资和所在的班级 +select + a.money,c.name +from + (select money a,id b from laagent ) la +inner join + class c +on + c.pid = la.b + + + +``` + + +>where 或者是having后面 + + +``` +支持标量子查询 +支持列子查询 +支持行子查询 + + +1、子查询放在小括号内 +2、子查询一般放在条件右侧 +3、标量子查询 一般搭配单行操作符使用(> < >= <= <>) + ①:标量子查询 + 案例:查询 谁的工资比tom高; + select * from laagent where money > (select money from laagnt where name = 'tom') + 案例:查询工资最少的人 工号,姓名,工资 + select id ,name ,money where money = (select min(money) from laagent); + ②:列子查询 + any 和some 一样: where a > any(11,22,33) 只要大于其中任意一个就行 + all: where a > all(11,22,33) 必须大于all里面的所有 + + 案例:查询 工资是 2000 和 6000的员工的信息 + select * from laagent where money in (2000,6000); + 或者是 + select * from laagent where money = any(2000,6000); -- 等于其中的任意一个 + + 案例:查询工资在 2000 或者 6000 的 员工 + select * from laagent where money any (2000,6000); + ③:行子查询(结果集一行多列 或者是 多行多列) + 案例:查询员工编号最小 并且工资最高的员工 + select * from laagent where id = (select min(id) from laagent) and money =(select max(money) from laagent); + 或者 + select * from laagent where (id,money) = (select min(id),max(money) from laagent); + + +``` + +>exists后面 存在返回1 不存在返回0 + +``` +支持表子查询 + +案例:查询不在班级101 的学生信息 +select * from laagent a where nont exists (select 1 from class where pid = a.id and cla = 101); + +``` + + + +>案结果集行列不同 + +``` + +标量子查询(结果集只有一行一列) +列子查询(结果集只有一列多行) +行子查询(结果集有一行多列) +表子查询(结果集一般为多行多列) + +``` + + + + +## DML 语言 数据操作语言(增删改) +### 增 +``` +1.insert into 表 (id,name,age,sex)values('1','李夏雨','22','女'); + +2.insert into 表 set id = '1',name = '李夏雨'; + +3.insert into 表(id,name,age,sex)values('1','李夏雨','22','女'),('2','李夏雨2','22','女'),('3','李夏雨3','22','女'); + +4.insert into 表(id,name,age,sex) select id,name,age,sex from laagent; + +``` +### 改 + + +>单表 + +``` + +1.update 表 set name = '',age = '' where id = ''; + +``` + +>多表 + +``` + +2.udpate 表1 a,表2 b set 列 = '' where 连接条件 and 筛选条件 + +3.udpate 表1 a inner|left|right join 表2 b on 连接条件 set 列= '' where 筛选条件 + + +``` + +### 删 如果有自增列 用delete删除的话再添加 从断点处自增,但是 用truncate 刪除 自增列从1开始 + +>单表 + +``` + +1.delete from 表 where id = ''; + +2.truncate table 表; 不能加where条件 执行 就整个表全部删除了 + +``` + +>多表 delete那个表就删那个表 + +``` + +1.delete 表1 from 表1 a,表2 b where 连接条件 and 筛选条件 + +2.delete 表1 from 表1 a inner|left|right join 表2 b on 连接条件 where 筛选条件 + +``` + + +## DDL语言 数据定义语言(库和表的管理) + +>创建*create* 修改*alter* 删除*drop* + +### 库的管理 + +``` +创建:create database '库名'; //默认创建在data文件夹下 + create database if not exists '库名'; //如果有这个库名了就不创建 + +修改:rename database '库名' to '新库名'; //因为不安全 已经不能使用 + + alter database '库名' character set gbk; // 改变库的字符集 + +删除:drop database '库名'; //删除库 + drop database if not exists '库名'; //如果存在就删除库 + + +``` + +### 表的创建 + +``` +create table 表名( + 列名 列的类型【(长度)约束】, + 列名 列的类型【(长度)约束】, + 列名 列的类型【(长度)约束】, + 列名 列的类型【(长度)约束】, + ..... + 列名 列的类型【(长度)约束】 +) + +例如:create table Book( //创建book表 + id int, #编号 + bname varchar(20),#图书名 + price double,#价格 + authorid int,#作者编号 + publishdate datetime #出版日期 + + ) + create table Author( //创建作者表 + id int, #编号 + auname varchar(20),#作者名名 + nation varchar(20) #国籍 + ) + + +``` + +### 表的修改 + +``` +基本语法: 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 author drop colum annual; + +``` + + +>修改表名 + +``` +alter table author rename to book_author; + +``` + +### 表的删除 + +``` +drop table book; + +drop table if exists book; -- 如果存在就删除 + +``` + +### 仅仅复制表的结构 + +``` + +create table 新表 like 复制的表 + +``` + +### 复制表结构 + 数据 + +``` +create table 新表 select * from 复制的表 + +``` + +## DCL管理用户并对用户进行授权 + +#### 管理用户 + +> 1.查询用户 +> +> ``` +> 1.首先切换mysql数据库 +> use mysql +> 2.查询user表 +> select * from user +> 百分号 % 可以在任意主机使用用户登录数据库 +> ``` + +> 2.创建用户 +> +> ``` +> create user '用户名'@'主机名' identified by '密码'; +> +> 例如: +> create user 'lixiaoyu'@'localhost' identified by '123123'; +> +> create user 'lixiaoyu'@'%' identified by '123123'; +> ``` + +> 删除用户 +> +> ``` +> drop user '用户名'@'主机名'; +> ``` + +> 修改用户 +> +> ``` +> 1.修改用户密码 +> update user set password = password('新密码') where user = '用户名'; -- password()加密函数 +> 2.修改用户密码 +> set password for '用户名'@‘主机名’ = password('新密码'); +> +> 3.假如忘记root用户的密码。 +> 1.cmd -- > net stop mysql 停止mysql服务(需要管理员权限) +> 2.使用无验证方式启动mysql:mysql --skip-grant-tables +> 3.打开新的cmd窗口 直接输入 mysql 回车 就直接登录了 +> 4.use mysql 使用mysql数据库 +> 5.update user set password = password('你的root新密码') where user = 'root'; +> 6.关闭2个cmd窗口 +> 7.打开任务管理器,手动结束 mysqld.exe的进程 +> 8.启动mysql的服务 +> 9.使用新密码登录 +> ``` +> +> + +#### 权限管理 + +> 查询权限 +> +> ``` +> show grants for '用户名'@'主机名'; +> ``` + +> 授予权限 +> +> ``` +> grant 权限列表 on 数据库名.表名 to '用户名'@'主机名'; +> +> 例如: +> grant select,delete,update,insert on xg.laagent to 'xg'@'localhost'; +> +> +> grant all on *.* to 'xg'@'localhost'; -- 给xg用户授权所有权限,在任意数据库任意表上 +> ``` + +> 撤销权限 +> +> ``` +> revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名'; +> +> revoke update on xg.laagent from 'xg'@'localhost'; -- 撤销掉用户xg在xg数据库中的laagent表中的修改权限。 +> ``` +> +> + +## 常见的数据类型 + +``` +数值型: + 整形: 字节 + tinyint 1 + smallint 2 + meditmint 3 + int,integer 4 + bigin 8 + 1、如果不设置无符号,默认有符号。设置有符号 unsigned. 符号就是指 负数 + 2、如果插入的数据超出了整形的范围,会报out of range 异常,并且插入的是临界值 + 3、如果不设置长度会有默认的长度 + 小数: + 定点型:dec(m,d) + decimal(m,d) + 浮点型: + float(m,d) 4 + double(m,d) 8 + m代表整数部位+小数部位 + d小数部位 + m,d都可以省略,如果是decimal,则m默认为10,d默认为0 + 如果是float和double会根据数值的精度来确定精度 + +字符型: + 较短的文本: + char 耗费空间 效率高 + varchar 比较节省空间 效率低 + 较长的文本:text blob(较长的二进制数据) + + 枚举:enum + 例如:crate table student( + aa enum('a','b','c','d','e') + ) + insert into student values('a') + insert into student values('b') + insert into student values('v') + insert into student values('d') + insert into student values('e') + 结果就是第三行是空的 因为 enum,只能插入固定的值 + + +日期型: + date 4 日期 + datetime 8 日期 时间 + timestamp 4 当前时区的日期 时间 + time 3 时间 + year 1 年 + + +``` + +## 约束 :一种限制,用于限制表中的数据为了保证表中的数据的准确和可靠性 + +``` +六大约束: + not null:非空 + default:默认,用于保证该字段有默认值 + primary key:主键约束 保证字段具有唯一性,并且非空 + unique:唯一,保证字段唯一 可以为空 + check:检查约束(mysql中不支持) + foreign key:外键约束,用于限制2个表的关系 + + 列级约束: + 例如:create table student( + id int primary key, -- 主键 + name varchar(20) not null,非空 + seat int unique,-- 唯一约束 + age int default 18,-- 默认约束 默认18 + pid int foreign key references 表2(id) -- 外键 + ) + + + 主键约束> 保证唯一 不允许为空 主键只允许一个 + + + 唯一约束> 保证唯一 只允许一个为空 多个 + + 外键:1.要求在从表设置外键关系 + 2.从表外键类型要和主表匹配字段类型一致 + 3.主表关联列必须是一个key(主键或者是唯一) + 4.插入数据先插主表,再插从表 + 删除先删从表 + + +自增长列:1.必须是一个key + 2.一个表只能有一个标识列 + 3.标识列的类型之能是数值型 + 4.标识列 可以通过手动插入值设置起始值,还可以设置步长来配置。set increment_increment = 3; +``` + + + + + + + diff --git a/lixiaoyu/mysql/sqlTimeTask.md b/lixiaoyu/mysql/sqlTimeTask.md new file mode 100644 index 0000000000000000000000000000000000000000..95f5cf7d5585399a56489f1b4d3cce9da6a2ff1e --- /dev/null +++ b/lixiaoyu/mysql/sqlTimeTask.md @@ -0,0 +1,168 @@ +# mysql定时任务 + +***mysql中事件调度器,event 也叫定时任务*** + +``` +event根据计划执行特定的操作,语句可以使begin...end。event可以是一次性的,也可以是重复性的。并且重复事件的日程分配特定的 开始日期和时间,结束日期和时间。(默认情况下,定期event在创建后立即开始,并且无期限的继续,直到它被警用或者删除) +``` + + + +### 查看数据库事件是否开启 + +```mysql +SELECT @@event_scheduler; OFF 关闭 默认是关闭的 on 启动 + +SHOW VARIABLES LIKE 'event_scheduler'; +``` + + + +### 开启数据库事件 + +```mysql + +set global event_scheduler=1; + +SET GLOBAL event_scheduler = ON; + +在my.cnf中的[mysqld]部分添加 event_scheduler=ON 然后重启mysql。 +``` + + + +### 关闭数据库事件 + +``` +SET GLOBAL event_scheduler = OFF; +``` + + + +### 创建事件的语法 + +``` +CREATE + [DEFINER = { user | CURRENT_USER }] -- DEFINER: 定义事件执行的时候检查权限的用户。 + EVENT + [IF NOT EXISTS] + event_name -- 事件名 + ON SCHEDULE schedule -- ON SCHEDULE schedule: 定义执行的时间和时间间隔。 + [ON COMPLETION [NOT] PRESERVE] -- ON COMPLETION [NOT] PRESERVE: 定义事件是一次执行还是永久执行,默认为一次执行,即NOT PRESERVE。 + [ENABLE | DISABLE | DISABLE ON SLAVE] -- ENABLE | DISABLE | DISABLE ON SLAVE: 定义事件创建以后是开启还是关闭,以及在从上关闭。如果是从服务器自动同步主上的创建事件的语句的话,会自动加上DISABLE ON SLAVE。 + [COMMENT 'comment'] -- COMMENT 'comment': 定义事件的注释。 + DO event_body; + + schedule: + AT timestamp [+ INTERVAL interval] ... | EVERY interval 开始[STARTS timestamp [+ INTERVAL interval] ...] 结束[ENDS timestamp [+ INTERVAL interval] ...] + 【AT timestamp】一般用于只执行一次,一般使用时可以使用当前时间加上延后的一段时间,例如:AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR。也可以定义一个时间常量,例如:AT '2006-02-10 23:59:00'; + 【EVERY interval】一般用于周期性执行,可以设定开始时间和结束时间。 + + interval: + quantity {YEAR (年) | QUARTER(季度) | MONTH(月) | DAY(日) | HOUR(时) | MINUTE(分) | WEEK(周) | SECOND (秒)| YEAR_MONTH (年月)| DAY_HOUR() | AY_MINUTE() |DAY_SECOND ()| HOUR_MINUTE() | HOUR_SECOND() | MINUTE_SECOND()} + + + +``` + + + +### 开启事件 + +``` +alter event event_name(事件名称) ON COMPLETION PRESERVE ENABLE; +``` + + + +### 关闭事件 + +``` +alter event event_name(事件名称) ON COMPLETION PRESERVE DISABLE; +``` + + + +### 删除事件 + +``` +DROP EVENT [IF EXISTS] event_name(事件名称) +``` + + + +### 事件案例 + +``` +案例1: + CREATE TABLE `test` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `t1` datetime DEFAULT NULL, + `id2` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) + ) ENGINE=InnoDB AUTO_INCREMENT=106 DEFAULT CHARSET=utf8 + + + CREATE EVENT IF NOT EXISTS e_test_1 -- 如果不存在就创建事件 + ON SCHEDULE EVERY 3 SECOND -- 永久执行 3秒执行一次 + ON COMPLETION PRESERVE -- on completion [not] preserve 不加not是指永久执行,默认加not 只执行一次 + DO INSERT INTO test(id,t1) VALUES(NULL,NOW()); + + +案例2: + + CREATE EVENT IF NOT EXISTS e_test_2 -- 如果不存在就创建事件 + ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 MINUTE -- 定义执行时间 at current_timestamp 一般用作只执行一次 (当前时间 + 1分钟 ) + DO TRUNCATE TABLE test; -- truncate 删除数据 + + +案例3: + + 1、创建过程 + CREATE PROCEDURE pro_test() -- 创建存储过程 + BEGIN + INSERT INTO test(id,t1,id2) VALUES(NULL,NOW(),'1000000'); -- 过程体内容 添加表内容 + END + 2、调用过程 + CREATE EVENT IF NOT EXISTS e_test_3 -- 如果不存在就创建事件 + ON SCHEDULE EVERY 3 SECOND -- 3秒执行一次 every 周期性执行 + ON COMPLETION PRESERVE -- ON COMPLETION [NOT] PRESERVE 加not是只执行一次,这句话不写默认只执行一次 + DO CALL pro_test(); -- 执行存储过程 + + +案例4: + + CREATE EVENT e_test -- 创建事件 + ON SCHEDULE EVERY 1 DAY -- 每天执行(every 代表周期性) + STARTS CURRENT_TIMESTAMP + INTERVAL 5 DAY -- (STARTS 开始)(CURRENT_TIMESTAMP: 当前时间)(INTERVAL 5 DAY: 5天) + DO TRUNCATE TABLE test.aaa; -- (TRUNCATE : 删除数据) + +案例5: + + CREATE EVENT e_test -- 创建事件 + ON SCHEDULE EVERY 1 DAY -- 每天执行(every 代表周期性) + ENDS CURRENT_TIMESTAMP + INTERVAL 5 DAY -- (ENDS 结束)(CURRENT_TIMESTAMP: 当前时间)(INTERVAL 5 DAY: 5天) + DO TRUNCATE TABLE test.aaa; -- (TRUNCATE : 删除数据) +``` + + + +### 关于事件权限问题(Access denied for user 'root'@'%' to database ‘xxxx’) + +``` +使用Naicat Premium远程连接的mysql上面创建了一个新数据库和新的用户后,给该用户添加这个新数据库权限时出现: +access denied for user 'root'@'%' to database xxxx的提示。 +错误的原因是root用户在远程连接的MYSQL上面,没有这个新数据库的授权。在本地使用mysql应该不存在这个问题。 +解决方法,执行授权: + UPDATE mysql.user SET Event_priv = 'Y' WHERE HOST='%' AND USER='root'; + FLUSH PRIVILEGES; + grant all PRIVILEGES on xxxx.* to root@'%' identified by 'password' with grant option; + grant all on xxxx.* to 'root'@'%' identified by 'password' with grant option; + xxxx为创建的数据库,password为root的密码。请按实际要求进行更改。 +``` + +https://www.cnblogs.com/jalja/category/687302.html + + + +https://dev.mysql.com/doc/refman/5.7/en/events-configuration.html \ No newline at end of file diff --git a/lixiaoyu/mysql/sqlTransactuib.md b/lixiaoyu/mysql/sqlTransactuib.md new file mode 100644 index 0000000000000000000000000000000000000000..a98d9f4d78fb08c842994bf5d4f8f1ea38e2cf09 --- /dev/null +++ b/lixiaoyu/mysql/sqlTransactuib.md @@ -0,0 +1,166 @@ +# 事务 + +## 事务就是一个或者是一组sql语句组成的一个执行单元,这个执行单元要么全部执行,要么就全部不执行。 + +``` +innodb 存储引擎支持事务,其他都不支持 + + +``` + +## 事务的特性 ACID + +``` +1.原子性A + 事务是一个不可分割的工作单位,要不全部执行要么全部不执行 + +2.一致性C + 事务执行完成之后 数据还是一直状态 + +3.隔离性I + 事务的执行不能被其他事务干扰, 也就是说一个事务能不操作和使用的数据对并发的其他事务 + 是隔离的,并发执行的每个事务之间不能互相干扰。 (需要看隔离的级别) + +4.持久性D + 事务一旦提交,就会永久改变。 + + + +``` + + +## 事务的创建 + +``` +隐式的事务: 事务没有明显的开启和结束的标志。 + + +显示的事务:事务具有明显的开启和结束的标志。 + 前提是必须先设置自动提交功能为禁用。 + + +关于事务的一些术语 + + 开启事务:Start Transaction + 事务结束:End Transaction + 提交事务:Commit Transaction + 回滚事务:Rollback Transaction + + + + 1.开启事务 + set autocommit = 0; + 2.编写事务中的sql语句(select insert update delete) + 3.结束事务 + commit;提交事务 + rollback;回滚 + + 案例:set autocommit = 0; + start transaction; -- 开启事务 + update biao set name = 'lelele' where id = 11; + delete from biao where id = 22; + commit; + + + 回滚: name = lixiaoyu,biao id 22 存在 + set autocommit = 0; + start transaction; -- 开启事务 + update biao set name = 'lelele' where id = 11; + delete from biao where id = 22; + rollback; + 因为是回滚,所以数据不改变 还是原来的数据 + +start transaction;?? + +``` + + +## 数据库隔离级别 + +>如果说多个事务同时执行去访问数据库,没有采取必要的隔离级别的话就可能产生一些并发问题。 +``` +脏读:假如有两个事物a,b ,a读取了已经被b修改过但是环没有提交的字段之后,如果b回滚了,那么a读取 + 到的内容就是临时且无效的。 + +不可重复读:例如两个事务a,b。a读取了一个字段,然后b更新了这个字段之后,a再次读取,字段值 + 就不同了 。 + +幻读:例如有2个事务a,b。a从表内读了一个字段是2行,然后b在这个表中插入了一些新的行,如果a再 + 次读取如同一个表,就会出现多行数据。(针对插入insert) + + +``` + +>查看隔离级别 + +``` + +select @@tx_isolation; + +``` + + +>如果去避免上面这些并发问题 设置隔离级别 + +``` + +隔离级别: 脏读 不重复读 幻读 + 1.read uncmmitted: √ √ √ + + 2.reade committed: × √ √ + + 3.repeatable read: × × √ + + 4.serializable: × × × + +mysql中默认repeatable read 隔离级别。 +oracle中默认read committed; + + + +set session | global transaction isolation level .....; +1.设置当前mysql连接的隔离级别: + + set transaction isolation level .......; (...为隔离级别) + + +2.设置数据库系统的全局隔离级别(重启才有效) + + set global transaction isolation level .....; (....为隔离级别) + + + +``` + +>回滚 saveplint + +``` + +例如: + set autocommit = 0; + start transaction; + delete from biao where id = 20; + savepoint a; -- 设置一个节点名 + delete from biao where id = 28; + rollback to a; -- 回滚到保存点 + + select * from biao ; 结果就是没有20的数据 有28的数据 + +``` + + + + + + + + + + + + + + + + + diff --git a/lixiaoyu/mysql/sqlTuning.md b/lixiaoyu/mysql/sqlTuning.md new file mode 100644 index 0000000000000000000000000000000000000000..5a810f46f0d28b331136388fe1cf5db66cd4b1d8 --- /dev/null +++ b/lixiaoyu/mysql/sqlTuning.md @@ -0,0 +1,277 @@ +# mysql调优 + +``` +MySql 在执行查询的时候一般情况下 只会用到一个索引 一条sql有多个索引的情况(由优化器来判断该用哪个比较快) +``` + +## 性能分析思路 +>1.首先需要使用【慢查询日志】功能,去获取所有查询时间比较长的sql语句。 +>2.其次【查看执行计划】查看有问题的sql的执行计划。 +>3.最后可以使用【show profile[s]】查看有问题的sql的性能使用情况。 + + +## mysql 默认innoDB存储引擎 + +## 回表:根据普通索引查询到主键值,根据主键值去主键索引去寻找需要的数据。 + +## 索引覆盖: + +## 索引下推: +``` +比如说有一个组合索引(name , age) +1、如果没有使用索引下推那么执行的时候就是:先根据name把所有的数据拉取到server层,在server层对age进行过滤。 +2、如果使用了索引下推那么执行的时候就是:根据name,age把对应的数据拉取到server层,取对应的数据。 + +``` + +## 最左匹配: + +## mysql索引: + +>1.索引是:高效获取数据的数据结构(B+树数据结构) +>>索引包括:聚集索引,覆盖索引,组合索引,前缀索引,唯一索引等。 +(索引会存磁盘,大了一定不好) + +>2.优势: + +``` +1.可以提高数据查询的效率,降低数据库的IO成本,相当于书的目录 +2.索引支持对数据进行排序。降低数据排序的成本,降低 cpu的消耗 +``` +>3.劣势: + +``` +1.索引会占据磁盘空间(磁盘空间占的越大,读取索引耗时越多) +2.索引虽然会提高查询效率,但是会降低更新表的效率(在增删改的时候就会对索引的表结构进行变更) +``` + +## 常用索引分类 + +**主键索引**效果最好 其次是 *组合索引* + +>1.单列索引: + *普通索引:mysql基本索引类型,没有限制,允许在定义索引的列中插入重复值和空值,数据更快一点。* + *唯一索引:索引列中的值必须是唯一的 但是可以为空。* + *主键索引:是一种特殊的唯一所有,不许有空值。* + +>2.组合索引: + *在表中的所个字段组合上创建的一个索引。* + *最左前缀原则(最左匹配原则)* + *建议使用组合索引,代替单个所引* + + + +### 索引使用(待测试) + +``` + +``` + + +## 索引存储结构 +*B树 和 B+树* +``` +B树3层可以存bigint类型10亿条 +``` + +>B树:非叶子节点和叶子节点都会存储数据 +>B+树:只有叶子节点会存数据 ,非叶子节点不存数据只存指针 + + +## 聚集索引 表数据和索引是存在一起的就交聚集索引 +``` +id 是主键------主键索引(有且只有一个) +name 非唯一索引 ---- 辅助索引(可以有多个) +例如:select * from student where id = 1 and name = '李晓钰' + + +(不要用特别长的字段作为主键) +(如果没有主键就会找一个唯一非空列,如果找不到就会默认创建一个隐藏列) +1.主键索引:数据就是指数据行,就是一行记录。 + +2.辅助索引:存的索引字段和主键值 + +``` +## 聚集索引 +**聚集索引(叶子节点存的是主键值),聚集索引(叶子节点存的是地址值)** + + + + +## 组合索引 创建的顺序特别重要 (创建顺序的选择也是优化的目标) + +>创建下面这个组合索引,相当于创建了c1,c1 c2 ,c1 c2 c3 3个索引 +> +>AL RABLE 'table_name' ADD INDEX index_name('c1','c2','c3') + + + +``` + +例如:AL RABLE 'table_name' ADD INDEX index_name('c1','c2','c3') 就是创建了一棵B+树 但是他相当于3棵索引树的功效 + +where c1 = ? and c2 = ? and c3 = ? +where c1 = ? +where c2 = ? and c3 = ? -- 不走组合索引 +where c2 = ? and c1 = ? and c3 = ? + +``` + +>最左匹配原则: + +``` +mysql 从左往右执行,oracle 从右往左执行 知道遇到范围查询停止(< , > , between , ....) +如果创建索引的顺序是 (a,b,c,d)顺序 a = 1 and b = 2 and c > 3 and d = 4 他到了abc就不走了 因为 +c是范围查询, +``` + +>(最左匹配 需进行测试) + +>1 = 1 索引优化就抛弃了 不是很好的写法 + +``` + +假如:建表语句 + create table user( + id int primary key, -- 主键 + name varchar(100), + age int, + sex char(1), + address varchar(100) + ); + + -- 创建组合索引 + -- 注意!:这里name属性值是varchar类型的字段长度是100,但是在创建 索引的时候name指定长度是10, + 这是使用了前缀索引这个概念。 前缀索引跟多针对字符串,假如说一个字符串特别长用他进行等值匹配 + 特别不靠谱,一般情况都是用字符串的前面几个进行匹配。(具体这个长度有一个字段区分度,由区分度去选则) + alter table user add index idx_name_age(name(10),age); + +``` + + + + + + + +## 如何使用索引 +### 那些情况需要创建索引 + +>1.主键自动创建唯一索引 **一般最好建立主键** +>2.**经常作为查询的字段 作为索引** +>3.多表关联的时候,**关联字段建为索引** +>4.**查询中统计或者分组字段** 应该创建索引 +>5.**查询中排序字段** 应该创建索引 + + + + +### 哪些情况不需要创建索引 + +>1.表记录太少 加索引浪费内存 +>2.经常增删改 +>3.频繁更新的字段 +>4.where 条件不常用的字段 + + + +## 查看执行计划 +*在我们查询语句的前面加上 ***explain*** 他会出来执行计划 +``` + +例如: explain select * from user; + +``` + +### 查询出来的计划的参数字段: +>id: 关联查询会用到多个表,多个表是有顺序的。顺序用id来标识 +>select_type: 每一个表的查询类型。(子查询呢,还是联合查询呢,还是.......) +>table: 执行的时候走的那个表 +>partitions: 匹配分区 +>type: 连接类型 +>possible_keys: 查询中可能用到的索引 +>key: 本次查询中所用到的索引 +>ref: 连接的时候用那个字段进行连接的 +>rows: 显示一共扫描多少行 +>filtered: 表示此次查询条件过滤数据的百分比 +>extra: 前面的信息体现不出来的都会在这个里面体现出来 + + + +#### 参数字段:重点 +>select_type + +>type:显示的是单位查询连接类型或者是显示类型 访问性能依次是 从好--差:system --> const --> eq_ref --> ref --> fulltext --> ref_or_null --> unique_subquery --> index_subquery --> range --> index_merge --> index --> ALL +>>(*除了ALL其它type类型 都可以用到索引) +>>(*index_merge之外,其它的type类型只可以用到一个索引) + +>system:表中只有一行数据或者是空表 +>const:使用唯一或主键索引的时候,并且长度的值是一个常量。select * from user where id = 1 ; id 是主键,id类型是int的长度固定就是4. +>eq_ref:针对唯一索引 +>ref:针对非唯一索引 +>range:使用>,<,null,between,in,like 后模糊 等运算符的查询 +>index:(跟where没啥关系,更多的就是对结果的索引覆盖)查询的数据没有进行条件搜索,是在索引树上直接取的。 比如:select sex from uesr; sex 是非唯一索引 +>all:全表扫描,就是主键索引里面所有的表数据 + +>extra: +>>using index : 用到了索引覆盖 +>>using index condition : 表示使用了索引下推 +>>using where : + + + + + +``` +有索引的都会在innodb存储引擎进行过滤 +其次剩下的 +去mysql server层进行 + +在mysql 5.6版本之后ICP 索引下推 + +``` + +## 索引失效 + +>1.最好是全值 +>2.最左前缀法则 +>3.不要在索引上做任何操作(计算,函数,类型转换) 会导致索引失效 +>4.索引范围右边的列用不到 +>5.尽量使用覆盖索引(只查询所有的列),减少select * +>6.!= 或者 <> 无法使用索引 +>7.is null ,is not null 无法使用索引 +>8.like %开头会变成全表扫描索引失效。(但是索引覆盖的情况他是会走索引的) +>9.字符串不加单引号 索引失效 +>10.少用or,用or连接索引会失效 + + + + + +## 数据量大 分区 分表 分库(待学习) +``` + +``` + +## 性能优化 + +>1、服务器层面的优化 +>>设置足够大的innodb_buffer_pool_size(设置缓存池的大小)将数据读取到内存中。 innodb_buffer_pool_size设置总内存大小的3/4或者4/5. +>>怎样确定innodb_buffer_pool_size足够大,数据是从内存读取而不是硬盘? show global status like 'innodb_buffer_pool_pages_%'; + +>2、内存预热 + + +>3、降低磁盘写入次数 +>>对于生产环境来说,很多日志不需要开启,比如:通用查询日志,慢查询日志,错误日志 +>>使用足够量打的写入缓存:innodb_log_file_size 推荐innodb_log_file_size 设置为 1/4 * innodb_log_file_size + +>>设置合适的innodb_flush_log_at_trx_commit,和日志落盘有关 +>4、提高磁盘读写 +>>ssd硬盘,虚考虑成本 + + + + + + diff --git a/lixiaoyu/mysql/sqlView.md b/lixiaoyu/mysql/sqlView.md new file mode 100644 index 0000000000000000000000000000000000000000..4ca1d24b84b0766afb4385607ee335d0985627c8 --- /dev/null +++ b/lixiaoyu/mysql/sqlView.md @@ -0,0 +1,129 @@ +## 视图 + +``` +含义:虚拟的表 + +使用:和普通的表一样使用 + +应用场景: + 1.多个地方用到了同样的查询结果。 + 2.查询结果使用的sql语句复杂。 + +mysql5.1版本出现的新特性,是通过表动态生成的数据。 + + +例如:创建视图 + + create view shitu + as + select + name,age,sex + from + student s + inner join + class c + on s.id = c.id ; + + + select * from shitu where name like '张%'; + + +``` + +### 语法 + +>视图的创建 + +``` + create view 视图名 + as + 查询语句; + + +``` + +>视图的修改 + +``` +第一种:如果该视图存在那么就修改,如果不存在就创建 + create or replace view 视图名 + as + 查询语句。 + + +第二种:直接修改视图 + alter view 视图名 + as + 查询语句; + +``` + +>删除视图 + +``` + drop view 视图名,视图名......; + +``` + +>查看视图(结构) + +``` + desc 视图名; + + show create view 视图名; + + +``` + +### 视图的更新 + +``` +视图是支持 update insert delete 的。 + +但是具备以下条件的视图不允许更新: + 1.包含关键字distince,group by,having,union 或者是union all + 2.常量视图 + 3.select 中包含子查询 + 4.from一个不能更新的视图 + 5.where 子句的子查询引用了from字句中的表 + + + +```` + + + +### 视图和表的区别 + +``` + + 创建语法 是否占用物理空间 使用 +视图 create view 只是保存了sql逻辑 增删改查 + (一般不能增删改) + + + +表 create table 保存了数据 增删改查 + + + + +```` + + +### 视图的好处 + +``` + + 1.重用sql + 2.简化复杂的sql操作,不必知道他的查询细节 + 3.保护数据,提高安全性。 + + +``` + + + + + + diff --git "a/lixiaoyu/mysql/sql\346\265\201\347\250\213\346\216\247\345\210\266\347\273\223\346\236\204.md" "b/lixiaoyu/mysql/sql\346\265\201\347\250\213\346\216\247\345\210\266\347\273\223\346\236\204.md" new file mode 100644 index 0000000000000000000000000000000000000000..d1987b0a3b9f8e45776ded42ec5bd6596380a239 --- /dev/null +++ "b/lixiaoyu/mysql/sql\346\265\201\347\250\213\346\216\247\345\210\266\347\273\223\346\236\204.md" @@ -0,0 +1,147 @@ +# 流程控制结构 + +> 顺序结构:程序上下一次执行 +> +> 分支结构:程序从多条路径选择一条去执行 +> +> 循环结构:程序在满足一定条件的基础上,重复执行一段代码 + + + +## 分支结构 + +> if结构 +> +> ``` +> 功能实现双分支: +> if(表达式1,表达式2,表达式3) +> 如果表达式1成立则返回'表达式2'否则返回表达式3 +> +> 功能实现多重分支:应用在begin end中 +> if 条件1 then 值 +> elseif 条件2 then 值 +> elseif 条件3 then 值 +> elseif 条件4 then 值 +> else 值 +> end if; +> ``` +> +> + +> case结构 +> +> ``` +> 1.类似于java中的switch语句。 +> case 表达式 when 判断的值 then 值 when 判断的值 then 值 end; +> 2.类似于java中的多重if。 +> case when 判断条件1 then 值 when 判断条件2 then 值 end; +> +> 可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end中或者是 begin end的外面。 +> 可以作为独立的语句去使用,只能放在begin end中。 +> +> 作为表达式去使用: +> create procedure test_demo(in money int) +> begin +> case +> when money > 20 then select '我爱你'; +> when money > 40 then select '我bu爱你'; +> when money > 60 then select '我很爱你'; +> when money > 80 then select '爱你'; +> else select 'ddddd'; +> end case; +> end $ +> +> call test_demo(50)$ +> ``` +> +> + +## 循环控制 + +> 分类:while 、loop、repeat +> +> 循环控制: +> +> ​ iterate类似于continue,结束本次循环,继续下一次 +> +> ​ leave类似于break,跳出,结束当前所在的循环 + +``` +标签如果不用到循环控制可以不加 +1.while +语法: + 标签:while 循环条件 do + 循环体 + end while 标签; + +2.loop +语法: + 标签:loop + 循环体 + end loop 标签; +可以用来模拟简单的死循环、 + +3.repeat +语法: + 标签:repeat + 循环体 + until 结束循环的条件 + end repeat 标签; + + +案例:while +批量插入到student多条记录 +create procedure pro_while(in insertcount int) +begin + declare i int default 1; + while i <= insertcount do + insert into student (username,password)values(concat('join',i),'123456789'); + set i = i + 1; + end while; +end $ + +call pro_while(100)$; + + +案例:leave 循环控制 + create procedure pro_while(in insertcount int) + begin + declare i int default 1; + a:while i <= insertcount do + insert into student (username,password)values(concat('join',i),'123456789'); + if i>20 then leave a; + end if; + set i = i + 1; + end while a; + end $ +call pro_while(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; //定义一个循环变量a,表示插入次数 + declare str varchar(26) default 'abcdefghijklmnopqrstuvwxyz'; + declare startindex int default 1; //代表起始索引 + declare len int default 1; //代表截取字符的长度 + while i <= insertcount do + set len = floor(rand()*(20-startindex+1)+1); //产生一个随机整数,代表 截取长度 + set startindex = floor(rand()*26+1) + insert into stringcontent(content)values(substr(str,startindex,len)); + set i = i + 1; //循环变量更新 +end $; +``` + diff --git "a/lixiaoyu/mysql/sq\350\247\246\345\217\221\345\231\250.md" "b/lixiaoyu/mysql/sq\350\247\246\345\217\221\345\231\250.md" new file mode 100644 index 0000000000000000000000000000000000000000..eda4042343762d41b6fdd1e530935848b9c149c0 --- /dev/null +++ "b/lixiaoyu/mysql/sq\350\247\246\345\217\221\345\231\250.md" @@ -0,0 +1,108 @@ +# 触发器 + +## 触发器概念: + +``` +触发器是与表相关的数据库对象,在满足定义条件的时候触发。并且执行触发器中定义的语句集合。(可以协助应用在数据库端确保数据的完整性)。 + +场景: + 比如现在有【用户表】和【日志表】,当一个用户被创建的时候,就需要在日志表中插入创建的log日志信息,(如果在不适用触发器的情况下就需要编写程序逻辑才可以实现。)触发器的作用就是当在【用户表】中插入一条数据之后,触发器帮你在【日志表】中也插入一条日志信息。(触发器可以执行增,删,改操作) +``` + + + +## 语法 + +``` +create trigger + trigger_name trigger_time trigger_event +on tb_name +for each row +trigger_stmt + +1、trigger_name:触发器名称 +2、tirgger_time:触发时机 BEFORE 或者 AFTER +3、trigger_event:触发事件 INSERT,DELETE 或者UPDATE + 触发器类型 激活触发器的语句 + insert insert,load data(相当于insert),replace(插入的数据和原来的primary key或者unique相同的时候,会删除旧数据插入新数据) + update update + delete delete,replace +4、tb_name:表示建立触发器的表名。(在那张表建立触发器) +5、trigger_stmt:触发器的程序体。(可以是一条sql语句或者是用BEGIN...END包含的多条sql。) + +可以说mysql可以创建6种触发器: + 在事件之前 在事件之后 + BEFORE INSERT AFTER INSERT + BEFORE DELETE AFTER DELETE + BEFORE UPDATE AFTER UPDATE + +for each row :表示任何一条记录上的操作满足触发事件都会触发该触发器。 +``` + + + +> 创建多条执行语句的触发器(所谓的多条执行语句就是 begin..end中可以写过个sql) +> +> ``` +> DELIMITER ||或$ +> create trigger 触发器名 before|after 触发事件 +> on 表名 for each row +> begin +> 执行语句列表 +> end ||或$ +> +> DELIMITER; 作为技术符号 +> ``` +> +> + +``` +案例: + CREATE TABLE `users` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL, + `add_time` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `name` (`name`(250)) USING BTREE +) ENGINE=MyISAM AUTO_INCREMENT=1000001 DEFAULT CHARSET=latin1; + + + +CREATE TABLE `logs` ( + `Id` int(11) NOT NULL AUTO_INCREMENT, + `log` varchar(255) DEFAULT NULL COMMENT '日志说明', + PRIMARY KEY (`Id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='日志表'; + + + +DELIMITER $ +CREATE TRIGGER user_log AFTER INSERT ON users FOR EACH ROW +BEGIN +DECLARE s1 VARCHAR(40)character set utf8; +DECLARE s2 VARCHAR(20) character set utf8;#后面发现中文字符编码出现乱码,这里设置字符集 +SET s2 = " is created"; +SET s1 = CONCAT(NEW.name,s2); #函数CONCAT可以将字符串连接 +INSERT INTO logs(log) values(s1); +END $ +DELIMITER ; + + + + +insert into users(name,add_time) values('周伯通',now()); +``` + + + +### 使用触发器的限制和注意事项 + +``` +限制: + 1、触发器不能调用将【数据返回客户端的存储程序】,也不能使用【采用call语句的动态sql】但是允许 存储过程或者是函数 通过out或者inout类型的参数将数据返回触发器。只是不能调用直接返回数据的过程。 + 2、不能再触发器中使用以显示或隐式方式开始或结束事务的语句,如START TRANS-ACTION,COMMIT或ROLLBACK。 + +注意事项: + MySQL的触发器是按照BEFORE触发器、行操作、AFTER触发器的顺序执行的,其中任何一步发生错误都不会继续执行剩下的操作,如果对事务表进行的操作,如果出现错误,那么将会被回滚,如果是对非事务表进行操作,那么就无法回滚了,数据可能会出错。 +``` +