less基础

less基础

再摸一个less。

编译

使用node.js中less编译。

npm i -D less
lessc test.less test.css

注释

//不会出现在css文件中,/* */会。

变量

支持选择器、属性和属性值。选择器和属性在引用时要使用{},但一般只使用值作为变量。

@mainColor: aliceblue;
@attr: color;
@selector: body;

@{selector} {
  @{attr}: @mainColor;
}

less中变量的作用域是块级,并且会延迟加载,即会在块执行完毕后才看变量值。

嵌套

less可以实现类似html中的嵌套。

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root">
    outer
    <h1>test word.</h1>
    <h1 class="green">hello, world!</h1>
</div>
</body>
</html>
@mainColor: aliceblue;

body {
  color: @mainColor;
  h1 {
    color: coral;
  }
  .green {
    color: aqua;
  }
}

设置元素状态时需要修正嵌套级别。

@mainColor: aliceblue;

body {
  color: @mainColor;
  h1 {
    color: coral;
  }
  .green {
    color: aqua;
    &:hover {
      color: #66cccc;
    }
  }
}

混合

可以理解为less的方法?

可以省去大段重复css代码。

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root">
    outer
    <h1 id="text1">test word.</h1>
    <h1 id="text2">hello, world!</h1>
</div>
</body>
</html>
@mainColor: aliceblue;

.textHover {
  color: aqua;
  &:hover {
    color: #66cccc;
  }
}
body {
  color: @mainColor;
  h1 {
    color: coral;
  }
  #text1, #text2 {
    .textHover;
  }
}

混合的css会输出到编译后的css文件里,会有重复,可以使用如下的方式来避免。

@mainColor: aliceblue;

.textHover() {
  color: aqua;
  &:hover {
    color: #66cccc;
  }
}
body {
  color: @mainColor;
  h1 {
    color: coral;
  }
  #text1, #text2 {
    .textHover;
  }
}

混合可以带有参数。

@mainColor: aliceblue;

.textHover(@origin, @hover) {
  color: @origin;
  &:hover {
    color: @hover;
  }
}
body {
  color: @mainColor;
  h1 {
    color: coral;
  }
  #text1 {
    .textHover(aqua, #44cccc);
  }
  #text2 {
    .textHover(pink, #dda0ba)
  }
}

混合中参数可以有默认值。

@mainColor: aliceblue;

.textHover(@origin: aqua, @hover: #44cccc) {
  color: @origin;
  &:hover {
    color: @hover;
  }
}

body {
  color: @mainColor;

  h1 {
    color: coral;
  }

  #text1 {
    .textHover;
  }

  #text2 {
    .textHover(pink, #dda0ba)
  }
}

参数有默认值时可以指定部分参数。


Webpack配置

Webpack配置

Kotlin看不动了,摸鱼学学前端。

先安装node.js和npm,此处略。

初始化

初始化package描述文件,在项目路径中生成package.json文件。

npm init

或者使用npm init --yes省去中间配置步骤。

安装Webpack

npm i webpack webpack-cli -D

–save-dev(-D)参数意思是把模块版本信息保存到devDependencies(开发环境依赖)中,即package.json的devDependencies字段中

配置build启动脚本

package.json中,对scripts添加build字段:

{
  "scripts": {
    "build": "webpack --mode production",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

此外在项目目录中新建文件夹src作为源码目录,在src中新建文件index.js作为程序入口,内容随便写点。

此时可以测试能否build成功:

npm run build

项目目录中会出现一个dist文件夹,里面有一个main.js,即转换生成的js文件。

使用webpack.config.js

可以使用webpack.config.js来自定义build的入口和出口。该文件可以使用node.js的模块。

const path = require("path");

module.exports = {
    entry: './src/home.js',
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: 'output.js'
    }
}

requirenode.js引入模块的方法。此处引入的path是用于处理操作系统中文件路径的模块。path.resolve()方法将路径转换为绝对路径。__dirname为当前执行文件所在目录的完整目录名。

如果需要有多个入口和多个出口,可以如下配置:

const path = require("path");

module.exports = {
    entry: {
        main: './src/home.js',
        about: './src/about.js'
    },
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: '[name].js'
    }
};

output可以省略,生成的文件名使用entry中对应的key值。[name]是由webpack自动填充的字段。还可以使用[hash][chunkHash]来生成hash值。

webpack.config.js的位置也是可配置的,需要修改package.json - scripts - build字段

{ 
  "scripts": {
    "build": "webpack --mode production --config scripts/webpack.config.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

此时webpack的当前路径变为./scripts/,因此还需要修改webpack.config.js

const path = require("path");
const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
        about: './src/about.js'
    },
    output: {
        path: dist,
        filename: '[name].js'
    }
};

或者

const path = require("path");

module.exports = {
    entry: {
        main: './src/home.js',
        about: './src/about.js'
    },
    output: {
        path: path.resolve(process.cwd(), "dist"),
        filename: '[name].js'
    }
};

process.cwd()为获得当前执行node命令时候的文件夹目录名。

自动生成html

如果生成js使用了hash值作为文件名,那么对应的html也需要自动生成。安装插件html-webpack-plugin

npm i -D html-webpack-plugin

webpack.config.js中加入插件:

const path = require("path");
// 引入模块
const HtmlWebpackPlugin =require("html-webpack-plugin");
const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: '[name].[chunkHash:8].js'
    },
    // 引入插件
    plugins: [
            new HtmlWebpackPlugin()
    ]
};

进行npm run build,在dist中生成了index.html,其中使用的js文件是打包的js文件。

html模版

使用html模版则需要在项目目录中建立public文件夹,在其中建立home.html,如下:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>自动生成模版</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

修改webpack.config.js,为插件添加配置:

const path = require("path");
const HtmlWebpackPlugin =require("html-webpack-plugin");
const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: '[name].[chunkHash:8].js'
    },
    plugins: [
            new HtmlWebpackPlugin({
                title: "模版测试",
                template: "public/home.html"
            })
    ]
};

可以使用模版语法来定制生成的html。

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>

再次打包则生成的html如下:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>模版测试</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="main.299861a9.js"></script></body>
</html>

css

安装style-loadercss-loaderstyle-loader用于插入css到js里。css-loader用于处理css。

npm i -D style-loader css-loader

修改webpack.config.js,添加loader:

const path = require("path");
const HtmlWebpackPlugin =require("html-webpack-plugin");
const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: '[name].[chunkHash:8].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [ 'style-loader', 'css-loader' ]
            }
        ]
    },
    plugins: [
            new HtmlWebpackPlugin({
                title: "模版测试",
                template: "public/home.html"
            })
    ]
};

loader的执行顺序从后向前。

在js文件中引入css。

import './main.css';
console.log("home");

此时进行打包,css会进入js并在执行的时候生效。

如果希望提取css到单独文件,则需要安装mini-css-extract-plugin插件。

npm i -D mini-css-extract-plugin

修改webpack.config.js,加入该插件的配置,修改位置已标出。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// [1]
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: '[name].[chunkHash:8].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                // [2]
                use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "模版测试",
            template: "public/home.html"
        }),
        // [3]
        new MiniCssExtractPlugin({
            filename: '[name].css'
        })
    ]
};

进行打包时css文件会单独到处,并在html中引入。

构建导出目录结构

修改webpack.config.js,直接在导出部分添加路径即可。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        // [1]
        filename: 'js/[name].[chunkHash:8].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "模版测试",
            template: "public/home.html"
        }),
        new MiniCssExtractPlugin({
            // [2]
            filename: 'css/[name].[chunkHash:8].css'
        })
    ]
};

配置开发服务器

安装插件webpack-dev-server

npm i -D webpack-dev-server

修改package.json,添加dev命令。

{
  "name": "test-react-2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --mode production --config scripts/webpack.config.js",
    "dev": "webpack-dev-server --mode development --config scripts/webpack.config.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^3.2.0",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "^0.8.0",
    "style-loader": "^1.0.0",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.9"
  }
}

修改webpack.config.js,可以配置端口号等。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: 'js/[name].[chunkHash:8].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "模版测试",
            template: "public/home.html"
        }),
        new MiniCssExtractPlugin({
            filename: 'css/[name].[chunkHash:8].css'
        })
    ],
    // [1]
    devServer: {
        port: 3000,
        open: true
    }
};

配置css预处理器

安装lessless-loader

npm i -D less less-loader

修改webpack.config.js,添加less文件规则。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: 'js/[name].[chunkHash:8].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
            },
            // [1]
            {
                test: /\.less$/,
                use: [ MiniCssExtractPlugin.loader, 'css-loader', 'less-loader' ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "模版测试",
            template: "public/home.html"
        }),
        new MiniCssExtractPlugin({
            filename: 'css/[name].[chunkHash:8].css'
        })
    ],
    devServer: {
        port: 3000,
        open: true
    }
};

添加一个less文件并引入。

body {
  color: aliceblue;
}
import './main.css'
// [1]
import './test.less'
console.log("home");

图片处理

安装file-loader

npm i -D file-loader

webpack.config.js中配置规则。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: 'js/[name].[chunkHash:8].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
            },
            // [1]
            {
                test: /\.(png|jpe?g|gif|jfif)$/i,
                use: [
                    {
                        loader: 'file-loader',
                    },
                ],
                options: {
                    name: '[path][name].[ext]',
                }
            },
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "模版测试",
            template: "public/home.html"
        }),
        new MiniCssExtractPlugin({
            filename: 'css/[name].[chunkHash:8].css'
        })
    ],
    devServer: {
        port: 3000,
        open: true
    }
};

字体处理

同样使用file-loader。在webpack.config.js中添加配置。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: 'js/[name].[chunkHash:8].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
            },
            {
                test: /\.(png|jpe?g|gif|jfif)$/i,
                loader: 'file-loader',
                options: {
                    name: 'static/images/[name].[ext]',
                    publicPath: '/'
                }
            },
            // [1]
            {
                test: /\.(eot|ttf|woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file-loader',
                options: {
                    name: 'static/fonts/[name].[ext]',
                    publicPath: '/'
                }
            },
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "模版测试",
            template: "public/home.html"
        }),
        new MiniCssExtractPlugin({
            filename: 'static/css/[name].[chunkHash:8].css'
        })
    ],
    devServer: {
        port: 3000,
        open: true
    }
};

修改less。

@mainColor: aliceblue;
.textHover(@origin: aqua, @hover: #44cccc) {
  color: @origin;
  &:hover {
    color: @hover;
  }
}

// [1]
@font-face {
  font-family: FFXIV_Lodestone_SSF;
  src: url("assets/fonts/FFXIV_Lodestone_SSF.woff") format('woff');
}
body {
  color: @mainColor;

  h1 {
    color: coral;
  }

  #text1 {
    .textHover;
  }

  #text2 {
    .textHover(pink, #dda0ba);
  }

  // [2]
  #text3 {
    font-family: FFXIV_Lodestone_SSF, serif;
  }
}

配置babel

babel可以把ES2015及更新版本的js转换为向前兼容的js,也支持转换jsx。

安装babel-loader

npm i -D babel-loader @babel/core @babel/preset-env

webpack.config.js中添加规则。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const dist = path.join(__dirname, '..', 'dist');

module.exports = {
    entry: {
        main: './src/home.js',
    },
    output: {
        path: dist,
        filename: 'js/[name].[chunkHash:8].js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
            },
            {
                test: /\.(png|jpe?g|gif|jfif)$/i,
                loader: 'file-loader',
                options: {
                    name: 'static/images/[name].[ext]',
                    publicPath: '/'
                }
            },
            {
                test: /\.(eot|ttf|woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file-loader',
                options: {
                    name: 'static/fonts/[name].[ext]',
                    publicPath: '/'
                }
            },
            // [1]
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "模版测试",
            template: "public/home.html"
        }),
        new MiniCssExtractPlugin({
            filename: 'static/css/[name].[chunkHash:8].css'
        })
    ],
    devServer: {
        port: 3000,
        open: true
    }
};

SQL基础教程笔记0x01

集合运算

表的加减法

union

  • 对表进行并集操作。可以使用union all使结果包含重复行。
select product_id, product_name
from product
union
select product_id, product_name
from product_2;
product_id product_name
0001 T恤
0002 打孔器
0003 运动T恤
0004 菜刀
0005 高压锅
0006 叉子
0007 擦菜板
0008 圆珠笔
0009 手套
0010 水壶
  • 注意:作为union的运算对象的记录的列数必须相同,列的类型必须一致;order by只能在最后使用一次。

intersect

  • 对表进行交集操作。MySQL不支持该操作。
/* postgreSQL */
select public.product.product_id, public.product.product_name
from public.product
intersect
select public.product_2.product_id, public.product_2.product_name
from public.product_2;
product_id product_name
0001 T恤
0002 打孔器
0003 运动T恤
/* MySQL用exists模拟 */
select product_id, product_name
from product as p
where exists(
              select product_id
              from product_2 as p2
              where p.product_id = p2.product_id
          );

except

  • 对表进行差集操作,注意该操作不满足交换律。
/* postgreSQL */
select public.product.product_id, public.product.product_name
from public.product
except
select public.product_2.product_id, public.product_2.product_name
from public.product_2
product_id product_name
0004 菜刀
0005 高压锅
0006 叉子
0007 擦菜板
0008 圆珠笔
/* MySQL用not exists模拟 */
select product_id, product_name
from product as p
where not exists(
              select product_id
              from product_2 as p2
              where p.product_id = p2.product_id
          );
product_id product_name
0004 菜刀
0005 高压锅
0008 圆珠笔
0006 叉子
0007 擦菜板

联结

内联结

  • 以一张表中的列为桥梁,将其他表中满足同样条件的列汇集到同一结果之中。
select sp.shop_id, sp.shop_name, sp.product_id, p.product_name, p.sale_price
from public.shop_product as sp inner join public.product as p
on sp.product_id = p.product_id
shop_id shop_name product_id product_name sale_price
000D 福冈 0001 T恤 1000
000A 东京 0001 T恤 1000
000B 名古屋 0002 打孔器 500
000A 东京 0002 打孔器 500
000C 大阪 0003 运动T恤 4000
000B 名古屋 0003 运动T恤 4000
000A 东京 0003 运动T恤 4000
000C 大阪 0004 菜刀 3000
000B 名古屋 0004 菜刀 3000
000C 大阪 0006 叉子 500
000B 名古屋 0006 叉子 500
000C 大阪 0007 擦菜板 880
000B 名古屋 0007 擦菜板 880

外联结

  • 和内联结基本相同,但是会选择主表的全部信息(见下面示例)。
select sp.shop_id, sp.shop_name, sp.product_id, p.product_name, p.sale_price
from public.shop_product as sp right outer join public.product as p
on sp.product_id = p.product_id
shop_id shop_name product_id product_name sale_price
000D 福冈 0001 T恤 1000
000A 东京 0001 T恤 1000
000B 名古屋 0002 打孔器 500
000A 东京 0002 打孔器 500
000C 大阪 0003 运动T恤 4000
000B 名古屋 0003 运动T恤 4000
000A 东京 0003 运动T恤 4000
000C 大阪 0004 菜刀 3000
000B 名古屋 0004 菜刀 3000
NULL NULL NULL 高压锅 6800
000C 大阪 0006 叉子 500
000B 名古屋 0006 叉子 500
000C 大阪 0007 擦菜板 880
000B 名古屋 0007 擦菜板 880
NULL NULL NULL 圆珠笔 100

有两条记录另一张表上没有。

select sp.shop_id, sp.shop_name, sp.product_id, p.product_name, p.sale_price
from public.shop_product as sp left join public.product as p
on sp.product_id = p.product_id
shop_id shop_name product_id product_name sale_price
000D 福冈 0001 T恤 1000
000A 东京 0001 T恤 1000
000B 名古屋 0002 打孔器 500
000A 东京 0002 打孔器 500
000C 大阪 0003 运动T恤 4000
000B 名古屋 0003 运动T恤 4000
000A 东京 0003 运动T恤 4000
000C 大阪 0004 菜刀 3000
000B 名古屋 0004 菜刀 3000
000C 大阪 0006 叉子 500
000B 名古屋 0006 叉子 500
000C 大阪 0007 擦菜板 880
000B 名古屋 0007 擦菜板 880

SQL基础教程笔记0x00

查询基础

逻辑运算符

含有NULL时的真值

  • SQL中的逻辑运算是三值逻辑,即包括真、假、不确定。

聚合与排序

为聚合结果指定条件

having子句

  • having子句用于对集合指定条件过滤。having子句位于group by子句后面。
select `product_type`, count(*)
from shop
group by `product_type`
having count(*) = 2;
  • having子句中能够使用的要素有:常数、聚合函数、group by子句中指定的列名(即聚合键)。

复杂查询

视图

视图的创建

create view shop_sum (`product_type`, `cnt_product`)
as
select  `product_type`, count(*)
from shop
group by `product_type`;

限制

  • 定义视图时不能使用order by
  • 对视图更新是有条件的:
    1. select子句中未使用distinct
    2. from子句中只有一张表。
    3. 未使用group by子句。
    4. 未使用having子句。

子查询

  • 子查询就是将用来定义视图的select子句直接用于from子句中。
select `product_type`, `cnt_product`
from (
         select `product_type`, count(*) as `cnt_product`
         from `shop`
         group by `product_type`
     ) as `product_sum`;

标量子查询

  • 标量子查询必须而且只能返回1行1列的结果。
/* where子句中无法使用聚合函数,可以使用标量子查询代替。*/
select `product_id`, `product_name`, `sale_price`
from `shop`
where sale_price > (
    select avg(sale_price)
    from `shop`
    );

关联子查询

  • 在普通子查询中添加where子句,使其变为标量子查询。
select `product_id`, `product_name`, `sale_price`
from `shop` as `s1`
where sale_price > (
    select avg(sale_price)
    from `shop` as `s2`
    where `s1`.`product_type` = `s2`.`product_type`
    group by `product_type`
    )
;

函数、谓词、case表达式

谓词

like

  • %代表0字符及以上的任意字符串,_代表任意一个字符。
/* pg */
select *
from test.like_sample.like_test
where strcol like 'abc%';

select *
from test.like_sample.like_test
where strcol like 'abc__';

between

  • 范围查询,包括两个边界。
select *
from `shop`
where sale_price between 100 and 1000;

is null、is not null

  • 判断是否为null

in

  • or的简便写法。
select `product_name`, `purchase_price`
from `shop`
where putchase_price in (320, 500, 5000);

使用子查询作为in的参数

select `product_name`, `sale_price`
from `shop`
where `product_id` in (
    select `product_id`
    from `all_shops`
    where `shop_id` = '000c'
);

exists

  • 判断是否存在满足条件的某种记录,存在则返回真,不存在则返回假。
select `product_name`, `sale_price`
from `product` as p
where exists (
    select *
    from shop_product as sp
    where sp.shop_id = '000c'
    and sp.product_id = p.product_id
);

case表达式

  • 满足when子句就执行对应的then子句,否则到下一个when子句,直到没有when子句后执行else子句。
select sum(
    case when `product_type` = 'A'
    then `sale_price` else 0 end
) as `sum_price_clothes`,
sum(
    case when `product_type` = 'b'
    then `sale_price` else 0 end
) as `sum_price_kitchen`,
sum(
    case when `product_type` = 'C'
    then `sale_price` else 0 end
) as `sum_price_office`
from `product`;

MySQL技术内幕笔记0x04

事务

事务简介

特性(ACID)

  • 原子性(Atomicity):指整个数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作都执行成功,才算整个事务成功。事务中任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库状态应该退回事务前的状态。
  • 一致性(Consistency):指事务将数据库从一种状态转变为下一种一致的状态。在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏。
  • 隔离性(Isolation):事务的隔离性要求每个读写事务的对象对其他事务的操作对象能互相分离,即该事务提交前对其他事务都不可见,通常使用锁来实现。
  • 持久性(Durability):事务一旦提交,其结果就是永久性的。即使发生宕机等故障,数据库也能将数据恢复。

分类

扁平事务

  • 是事务中最简单的一种,但在实际生产环境中可能是使用最为频繁的事务。在扁平事务,所有操作都处于同一层次,其由begin work开始,由commit workrollback work结束,其间的操作是原子的,要么都执行,要么都回滚。

带保存点的扁平事务

  • 除了支持扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务中较早的一个状态。这是因为某些事务可能在执行过程中出现错误并不会导致所有的操作都无效,放弃整个事务不合乎要求,开销也太大。
  • 保存点用来通知系统应该记住事务当前的状态,以便之后发生错误时,事务能够回到保存点当时的状态。
  • 保存点在事务内部是递增的,不会因为回滚而出现覆盖。
  • 保存点是易失的,即当发生系统崩溃后进行恢复时,事务需要从开始处重新执行,不能从最近的一个保存点继续。

链事务

  • 在提交一个事务时,释放不需要的数据对象,将必要的上下文隐式地传给下一个要开始的事务。提交事务的操作和开始下一个事务的操作将合并为一个原子操作,这意味着下一个事务将看到上一个事务的结果。
  • 链事务与带保存点的事务不同的是,带保存点的扁平事务能回滚到任意正确的保存点,而链事务中的回滚仅限于当前事务。此外,链事务在执行commit后即释放了当前事务所持有的锁,而带保存点的扁平事务不影响迄今为止所持有的锁。

嵌套事务

  • 是一个层次结构框架,由一个顶层事务控制各个层次的事务。顶层事务之下嵌套的事务称为子事务,其控制每一个局部变换。

分布式事务

  • 通常是一个在分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。

事务的实现

  • redo log用来保证事务的持久性,undo log用来保证事务的一致性。
  • redo通常是物理日志,记录的是页的物理修改操作,用于对页进行重做操作。undo是逻辑日志,根据每行记录进行记录,用于对事务进行回滚。
  • 当事务提交时,必须先将该事务的所有日志写入到redo日志文件进行持久化,待事务的提交成功后才算完成。
  • undo存放在数据库内部的一个特殊段中,称为undo段,位于共享表空间内。

事务的隔离级别

  • 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。
  • 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)。
  • 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读。
  • 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。

原文链接

分布式事务

  • XA是一种分布式事务的标准,多数数据库都实现了XA接口。
  • XA事务由一个或多个资源管理器、一个事务管理器以及一个应用程序组成。
  • 资源管理器:提供访问事务资源的方法。通常一个数据库就是一个资源管理器。
  • 事务管理器:协调参与全局事务中的各个事务。需要和参与全局事务的所有资源管理器进行通信。
  • 应用程序:定义事务的边界,指定全局事务中的操作。
  • 分布式事务使用两段式提交。在第一阶段,所有参与全局事务的节点都开始准备,告诉事务管理器它们准备好提交了。在第二阶段,事务管理器告诉资源管理器执行rollback还是commit。如果任何一个节点显示不能提交,在所有节点都被告知需要回滚。

剑指Offer面试题-正则表达式匹配

正则表达式匹配

请实现一个函数用来匹配包括.*的正则表达式。模式中的字符.表示任意一个字符,而*表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串aaa与模式a.aab*ac*a匹配,但是与aa.aab*a均不匹配

public class Q19 {
    public boolean match(char[] str, char[] pattern) {
        if (pattern == null || str == null) return false;
        return subMatch(str, 0, pattern, 0);
    }
    private boolean subMatch(char[] str, int strIndex, char[] pattern, int patternIndex) {
        // [1]
        if (strIndex >= str.length) {
            return patternIndex >= pattern.length ||
                    (patternIndex == pattern.length - 2 && pattern[patternIndex + 1] == '*');
        }
        if (patternIndex >= pattern.length) return false;
        // 匹配单个字符
        if (patternIndex == pattern.length - 1 || pattern[patternIndex + 1] != '*') {
            if (pattern[patternIndex] == '.' || str[strIndex] == pattern[patternIndex]) {
                return subMatch(str, strIndex + 1, pattern, patternIndex + 1);
            }
            return false;
        }
        // 匹配带*
        if (pattern[patternIndex] != '.' && str[strIndex] != pattern[patternIndex]) {
            return subMatch(str, strIndex, pattern, patternIndex + 2);
        }
        //[2]
        if (subMatch(str, strIndex, pattern, patternIndex + 2)) return true;
        //[3]
        while (strIndex < str.length) {
            if (str[strIndex++] != pattern[patternIndex] && pattern[patternIndex] != '.') {
                break;
            }
            if (subMatch(str, strIndex, pattern, patternIndex + 2)) return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Q19 s = new Q19();
        String str = "bcbbabab";
        String pattern = ".*a*a";
        s.match(str.toCharArray(), pattern.toCharArray());
    }
}
  • 思路:使用递归。首先是退出条件,如果strpattern都用完退出返回true,如果仅是pattern用完则返回false。注意[1]处有另一种退出返回true的情况,就是str用完,pattern剩余一个字符和*,此时按匹配0个可以返回true。然后是匹配单个字符,比较简单。最后是匹配带*,注意[2]先进行一次匹配0个,[3]进入循环的条件为str没有用完。

MySQL技术内幕笔记0x03

锁简介

  • 锁是数据库区别于文件系统的一个关键特性。数据库系统使用锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性。

lock与latch

  • latch一般成为闩锁(轻量级锁),因为其要求锁定的时间必须非常短。若持续时间长,则应用的性能会非常差。其目的是保证并发线程操作临界资源的正确性,并且通常没有死锁检测机制。在InnoDB中,latch又可以分为mutex(互斥锁)和rwlock(读写锁)。
  • lock的对象是是事务,用来锁定的是数据库中的对象,如表、页、行。一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放的时间可能不同)。此外,lock是有死锁机制的。

InnoDB存储引擎中的锁

锁的类型

  • InnoDB实现了如下两种标准的行级锁:
    • 共享锁(S Lock),允许事务读一行数据。
    • 排他锁(X Lock),允许事务删除或更新一行数据。
  • InnoDB支持多粒度锁定,这种锁定允许事务在行级锁与表级锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB支持一种额外的锁方式,称之为意向锁。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。InnoDB支持两种意向锁:
    • 意向共享锁(IS Lock),事务想要夺得一张表中某几行的共享锁。
    • 意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁。
  • 意向锁不会阻塞除全表扫描以外的任何请求。表级意向锁之间互相兼容,IS与S兼容,S与S兼容,其他情况都不兼容。

一致性非锁定读

  • 一致性非锁定读是指InnoDB通过行的多版本控制的方式来读取当前数据库中行的数据。如果读取的行正在执行delete或update操作,这时读取操作不会因此去等待行锁释放,而是去读取行的快照数据。该操作实现是通过undo段来完成的,而undo用来在事务中回滚数据,因此快照数据本身是没有额外开销的。此外,读取快照数据是不需要上锁的,因为没有事务需要对历史数据进行修改。由此,非锁定读机制极大地提高了数据库的并发性。

一致性锁定读

  • InnoDB对于select语句支持两种一致性锁定读操作,一种是select ... for update,对读取的行记录加一个X锁,其他事务不能对已锁定的行加上任何锁。另一种是select ... lock in share mode,对读取的行记录加一个S锁,其他事务可以向被锁定的行加S锁,但是如果加X锁则会被阻塞。

锁的算法

行锁的3种算法

  • Record Lock:单个行记录上锁。总是会去锁住索引记录,如果表在建立时没有设置索引,那么InnoDB会使用隐式的主键来进行锁定。
  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
  • Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身。InnoDB对于行的查询都是采用这种锁定算法。其设计的目的是为了解决幻象问题。
  • 当查询的索引含有唯一属性时,InnoDB会对Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住记录,从而提高并发性。

解决幻象问题

  • 幻象问题是指,在同一事务下,连续执行两次相同的SQL语句可能得到不同的结果,第二次SQL语句可能会返回之前不存在的的行。

锁问题

脏读

  • 脏数据是指事务对缓冲池中行记录的修改,并且还没有被提交。
  • 脏读是指在不同事务下,当前事务可以读到其他事务未提交的数据,即脏数据。

不可重复读

  • 不可重复读是指,在同一个事务中多次读取同一数据集合,但是存在两次结果不一致的情况。
  • 不可重复读和脏读的区别是,脏读得到的是未提交的数据,而不可重复读得到的是已经提交的数据,但是其违反了数据库事务一致性的要求。
  • 不可重复读的问题是可以接受的,因为其读到的是已经提交的数据,本身不会带来很大问题。

丢失更新

  • 丢失更新是指有些事务的更新操作被另一个事务的更新操作所覆盖,从而导致数据的不一致,例如:
    1. 事务t1将行记录r更新为v1,但是t1没有提交
    2. 事务t2将行记录r更新为v2,但是t2没有提交
    3. t1提交
    4. t2提交
  • 要避免丢失更新发生,需要让事务在这种情况下的操作变成串行化。在1.和2.中分别加上一个X锁,据此,2.需要等待1.和3.结束才能执行。

死锁

  • 死锁是指两个及以上的事务在执行过程中,因竞争锁资源而造成的一种互相等待的现象。
  • 解决死锁最简单的方式是不要有等待,让任何等待都转化为回滚,并且事务重新开始。但是这种方式可能导致并发性能下降,而这所带来的问题更为严重,因为很难被发现并且浪费资源。
  • 解决死锁的一种方法是超时,即当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另一个等待事务就能继续进行。
  • 当前数据库普遍采用等待图的方法进行死锁检测,这是一种更为主动的检测方法,主要包括锁的信息链表和事务的等待链表,以及两者构造的图,如果这张图中存在回路,就代表存在死锁。检测算法通常采用深度优先算法实现。

剑指Offer面试题-删除链表节点

删除链表节点

在O(1)时间内删除链表节点

给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。

class Node {
    int value;
    Node next;
    public Node(int value) {
        this.value = value;
    }
}
class Solution {
    public Node solute(Node head, Node p) {
        if (p.next != null) {
            p.value = p.next.value;
            p.next = p.next.next;
            return head;
        } else if (head == p) {
            return null;
        } else {
            Node next = head;
            while (next.next != p) {
                next = next.next;
            }
            next.next = null;
            return head;
        }
    }
}
  • 思路:把要删除的节点的下一个节点的值覆盖到当前节点上,再删除下一个节点。注意需要判断要删除节点是否为尾节点,以及链表中是否只有一个节点。

删除排序链表中重复的节点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

class Node {
    int value;
    Node next;

    public Node(int value) {
        this.value = value;
    }
}

class Solution {
    public Node solute(Node head) {
        if (head == null) return null;
        boolean deleteHead = false;
        if (head.next != null && head.value == head.next.value) deleteHead = true;
        Node last = head;
        Node next = head.next;
        int lastValue = head.value;
        while (next != null) {
            if (next.value == lastValue) {
                last.next = next.next;
            } else {
                lastValue = next.value;
                if (next.next == null || next.next.value != lastValue) {
                    last = last.next;
                } else {
                    last.next = next.next;
                }
            }
            next = next.next;
        }
        if (deleteHead) {
            while (true) {
                if (head.next == null || head.value != head.next.value) return head.next;
                head = head.next;
            }
        }
        return head;
    }
}
  • 思路:如要删除头节点最后处理(头指针向下到第一个值不同的节点),中间节点保存上一个值,如果重复则next直接向下,如果next的值发生变动则进行预判,相同则直接向下并更新上一个值,否则last向下。

剑指Offer面试题-打印从1到最大的n位数

打印从1到最大的n位数

输入数字n,按顺序打印从1到最大的n位数十进制数。

class Solution {
    StringBuilder num = new StringBuilder();
    int lastCount = 1;

    public Solution() {
        num.append('1');
    }

    public void check() {
        boolean c = true;
        for (int i = num.length() - 1; i >= 0; i--) {
            if (c) {
                if (num.charAt(i) == '9') num.setCharAt(i, '0');
                else {
                    num.setCharAt(i, (char) (num.charAt(i) + 1));
                    c = false;
                }
            } else break;
        }
        if (c) num.insert(0, '1');
    }

    public void increase() {
        lastCount++;
        if (lastCount >= 10) {
            lastCount = 0;
            check();
        } else {
            num.setCharAt(num.length() - 1, (char) (num.charAt(num.length() - 1) + 1));
        }
    }

    public int getSize() {
        return num.length();
    }

    public void print() {
        System.out.println(num.toString());
    }

    public static void solute(int n) {
        if (n <= 0) return;
        Solution solution = new Solution();
        while (solution.getSize() <= n) {
            solution.print();
            solution.increase();
        }
    }
}
  • 思路:使用书中第一种解法。使用StringBuilder更加方便。

剑指Offer面试题-数值的整数次方

数值的整数次方

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0。

class Solution {
    private double unsignedPower(double base, int exponent) {
        if (exponent == 0) return 1;
        if (exponent == 1) return base;
        double result = unsignedPower(base, exponent >> 1);
        result *= result;
        if (exponent & 1 == 1) result *= base;
        return result;
    }
    public double Power(double base, int exponent) {
        if (base == 0.0) return 0.0;
        if (exponent == 0) return 1.0;
        int exp = Math.abs(exponent);
        double result = unsignedPower(base, exp);
        if (exponent < 0) result = 1 / result;
        return result;
    }
}
  • 思路:求a^n,先求a^(n/2),然后平方,依次递归。注意指数为负数、0以及底数为0.0的情况。