feat:更新MySQL博客
This commit is contained in:
@@ -37,9 +37,9 @@ export default withMermaid({
|
||||
'/Others/': [routers[4]]
|
||||
},
|
||||
|
||||
// socialLinks: [
|
||||
// { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
|
||||
// ]
|
||||
socialLinks: [
|
||||
{ icon: 'gitea', link: 'https://cxxgit.iepose.cn/Cxx0822/blog-press' }
|
||||
],
|
||||
|
||||
footer: {
|
||||
message: '基于 VitePress 构建',
|
||||
|
||||
@@ -34,12 +34,15 @@ import { useData } from 'vitepress'
|
||||
const wordCount = ref(0)
|
||||
const imageCount = ref(0)
|
||||
|
||||
// 获取页面数据
|
||||
const { page } = useData()
|
||||
|
||||
// 文字阅读时间
|
||||
const wordTime = computed(() => {
|
||||
return ((wordCount.value / 275) * 60)
|
||||
})
|
||||
|
||||
// 图片阅读时间
|
||||
const imageTime = computed(() => {
|
||||
const n = imageCount.value
|
||||
if (imageCount.value <= 10) {
|
||||
@@ -54,13 +57,14 @@ const readTime = computed(() => {
|
||||
return Math.ceil((wordTime.value + imageTime.value) / 60)
|
||||
})
|
||||
|
||||
function analyze() {
|
||||
document.querySelectorAll('.meta-des').forEach(v => v.remove())
|
||||
const analyze = () => {
|
||||
// 选择文档内容区域
|
||||
const docDomContainer = window.document.querySelector('#VPContent')
|
||||
const imgs = docDomContainer?.querySelectorAll<HTMLImageElement>(
|
||||
'.content-container .main img'
|
||||
)
|
||||
// 统计图片数量
|
||||
const imgs = docDomContainer?.querySelectorAll<HTMLImageElement>('.content-container .main img')
|
||||
imageCount.value = imgs?.length || 0
|
||||
|
||||
// 统计文字数量(textContent:提取纯文本)
|
||||
const words = docDomContainer?.querySelector('.content-container .main')?.textContent || ''
|
||||
wordCount.value = countWord(words)
|
||||
}
|
||||
|
||||
@@ -43,12 +43,18 @@ export const routers = [
|
||||
{ text: '基于OAuth2的安全验证', link: '/Web-Backend/FastAPI/OAuth2' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '🐬 MySQL',
|
||||
items: [
|
||||
{ text: 'MyBatis简介和使用', link: '/Web-Backend/MySQL/MyBatis' },
|
||||
{ text: 'Flyway简单使用', link: '/Web-Backend/MySQL/Flyway' },
|
||||
{ text: 'MySQL知识点', link: '/Web-Backend/MySQL/MySQL-Knowledge' },
|
||||
{ text: 'SQL高阶用法', link: '/Web-Backend/MySQL/SQL-Advance' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '🔄 其他',
|
||||
items: [
|
||||
{ text: 'MySQL知识点', link: '/Web-Backend/Others/MySQL-Knowledge' },
|
||||
{ text: 'Flyway简单使用', link: '/Web-Backend/Others/Flyway' },
|
||||
{ text: 'MyBatis简介和使用', link: '/Web-Backend/Others/MyBatis' },
|
||||
{ text: 'RustFS简介和使用', link: '/Web-Backend/Others/RustFS' },
|
||||
{ text: 'AList简介和使用', link: '/Web-Backend/Others/AList' },
|
||||
{ text: '网络编程简介', link: '/Web-Backend/Others/Netword-Program' }
|
||||
|
||||
@@ -3,3 +3,533 @@
|
||||
date: 2025-12-18
|
||||
---
|
||||
|
||||
# 一、简介
|
||||
  [VitePress](https://vitepress.dev/zh/)是由 Vue 团队开发的静态网站生成器 (SSG),基于 Vite(前端构建工具)和 Vue 3 构建,专门用于快速搭建文档网站、博客或个人主页。它继承了 Vite 的极速开发体验,同时具备 Vue 的组件化能力,语法上兼容 Markdown 并支持扩展 Vue 组件,是替代 VuePress 的新一代工具。
|
||||
|
||||
# 二、快速开始
|
||||
## 2.1 安装
|
||||
```sh
|
||||
pnpm add -D vitepress@next
|
||||
```
|
||||
|
||||
## 2.2 初始化
|
||||
```sh
|
||||
pnpm vitepress init
|
||||
```
|
||||
|
||||
::: tip
|
||||
建议按照官网配置,文档目录设置为`./docs`。
|
||||
:::
|
||||
|
||||
## 2.3 运行
|
||||
```sh
|
||||
pnpm run docs:dev
|
||||
```
|
||||
|
||||
## 2.4 使用
|
||||
  在`docs`文件夹新建`md`文档,并在`.vitepress/config.mts`中配置`nav`和`sidebar`路径。
|
||||
|
||||
# 三、优化
|
||||
## 3.1 路由组件
|
||||
  每次新建文档时,都需要在`.vitepress/config.mts`重复`nav`和`sidebar`路径,有时还需要在文档目录的`index.md`配置导航链接,每次都要重复写3次,很麻烦,因此可以构造一个路由配置清单和路由组件,每次只需更新一次路由配置清单即可。
|
||||
  在`.vitepress/theme/router`文件夹下新建路由清单文件:
|
||||
```ts
|
||||
export const routers = [
|
||||
{
|
||||
text: '🌐 Web前端',
|
||||
items: [
|
||||
{
|
||||
text: '🌿 Vue',
|
||||
items: [
|
||||
{ text: 'Test', link: '/Web-Front/Vue/Test' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '🔄 其他',
|
||||
items: [
|
||||
{ text: 'Test', link: '/Web-Front/Others/Test' }
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '🖥️ Web后端',
|
||||
items: [
|
||||
{
|
||||
text: '🍃 SpringBoot',
|
||||
items: [
|
||||
{ text: 'Test', link: '/Web-Backend/SpringBoot/Test' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '🐍 FastAPI',
|
||||
items: [
|
||||
{ text: 'Test', link: '/Web-Backend/FastAPI/Test' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '🔄 其他',
|
||||
items: [
|
||||
{ text: 'Test', link: '/Web-Backend/Others/Test' }
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '🚀 DevOps',
|
||||
items: [
|
||||
{ text: 'Test', link: '/DevOps/Test' }
|
||||
]
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
  这里列举了有二级菜单和只有一级菜单的两种情况。
|
||||
  在`.vitepress/theme/components`文件夹下新建路由组件`MenuList.vue`:
|
||||
```vue
|
||||
<template>
|
||||
<div v-if="isNested">
|
||||
<div v-for="category in props.routers.items" :key="category.text">
|
||||
<h3>{{ category.text }}</h3>
|
||||
<ul>
|
||||
<li v-for="item in category.items" :key="item.text">
|
||||
<a :href="withBase(item.link)">{{ item.text }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<ul>
|
||||
<li v-for="item in props.routers.items" :key="item.text">
|
||||
<a :href="withBase(item.link)">{{ item.text }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { withBase } from 'vitepress'
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
routers: {
|
||||
required: true,
|
||||
type: Object
|
||||
},
|
||||
isNested: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
  这里需要使用`withBase`拼接路由,防止存在设置了`base`根路径的情况。通过`isNested`来控制是否存在二级菜单。
|
||||
::: warning
|
||||
这里只考虑了最多存在二级菜单的情况。
|
||||
:::
|
||||
|
||||
  在`.vitepress/theme/index.ts`注册组件:
|
||||
```ts
|
||||
import MenuList from './components/MenuList.vue'
|
||||
import type { EnhanceAppContext } from 'vitepress'
|
||||
|
||||
export default {
|
||||
enhanceApp({ app }: EnhanceAppContext) {
|
||||
app.component("MenuList", MenuList);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
  在`.vitepress/config.mts`中配置`nav`和`sidebar`:
|
||||
```ts
|
||||
export default defineConfig({
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: '🏠 Home', link: '/' },
|
||||
...routers
|
||||
],
|
||||
sidebar: {
|
||||
'/Web-Front/': [routers[0]],
|
||||
'/Web-Backend/': [routers[1]],
|
||||
'/DevOps/': [routers[2]]
|
||||
},
|
||||
},
|
||||
|
||||
lastUpdated: true,
|
||||
})
|
||||
```
|
||||
|
||||
  在文档所在目录的`index.md`中配置:
|
||||
```md
|
||||
---
|
||||
layout: doc
|
||||
title: Web前端
|
||||
description: Web前端开发技术文档
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import { routers } from '../.vitepress/theme/router'
|
||||
</script>
|
||||
|
||||
<MenuList :routers=routers[0] :isNested=true />
|
||||
```
|
||||
|
||||
  这样后续只需要维护路由配置清单文件即可自动生成路由菜单。
|
||||
|
||||
## 3.2 博客元数据
|
||||
  可以通过在每个博客中添加`frontmatter`元数据,在最顶部添加博客属性,例如创建日期、字数、时长和是否精品等信息。
|
||||
### 3.2.1 定义元数据
|
||||
  在每个博客最上面添加元数据:
|
||||
```md
|
||||
---
|
||||
title: Docker简介和安装
|
||||
date: 2025-12-15
|
||||
isGreat: true
|
||||
---
|
||||
```
|
||||
|
||||
  这里可以自定义添加任何`key: value`形式的字段信息。
|
||||
::: tip
|
||||
建议手动加上每个博客的创建日期
|
||||
:::
|
||||
|
||||
### 3.2.2 博客信息组件
|
||||
  在`.vitepress/theme/components`文件夹下新建博客信息组件`ArticleMetadata.vue`
|
||||
```vue
|
||||
<template>
|
||||
<div class="title">
|
||||
<span>{{ page.frontmatter.title }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="!page.filePath.includes('index.md')" class="blog-stats">
|
||||
<div v-if="page.frontmatter.isGreat" class="item">
|
||||
</svg>
|
||||
精品
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
</svg>
|
||||
日期: {{ page.frontmatter.date.split('T')[0] }}
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
</svg>
|
||||
字数: {{ formatNumberUnit(wordCount) }} 字
|
||||
</div>
|
||||
|
||||
<div>
|
||||
</svg>
|
||||
时长: {{ readTime }} 分钟
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, onMounted, watch } from 'vue'
|
||||
import { countWord, formatNumberUnit } from '../utils'
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
const wordCount = ref(0)
|
||||
const imageCount = ref(0)
|
||||
|
||||
// 获取页面数据
|
||||
const { page } = useData()
|
||||
|
||||
// 文字阅读时间
|
||||
const wordTime = computed(() => {
|
||||
return ((wordCount.value / 275) * 60)
|
||||
})
|
||||
|
||||
// 图片阅读时间
|
||||
const imageTime = computed(() => {
|
||||
const n = imageCount.value
|
||||
if (imageCount.value <= 10) {
|
||||
// 等差数列求和
|
||||
return n * 13 + (n * (n - 1)) / 2
|
||||
}
|
||||
return 175 + (n - 10) * 3
|
||||
})
|
||||
|
||||
// 阅读时间
|
||||
const readTime = computed(() => {
|
||||
return Math.ceil((wordTime.value + imageTime.value) / 60)
|
||||
})
|
||||
|
||||
const analyze = () => {
|
||||
// 选择文档内容区域
|
||||
const docDomContainer = window.document.querySelector('#VPContent')
|
||||
// 统计图片数量
|
||||
const imgs = docDomContainer?.querySelectorAll<HTMLImageElement>('.content-container .main img')
|
||||
imageCount.value = imgs?.length || 0
|
||||
|
||||
// 统计文字数量(textContent:提取纯文本)
|
||||
const words = docDomContainer?.querySelector('.content-container .main')?.textContent || ''
|
||||
wordCount.value = countWord(words)
|
||||
}
|
||||
|
||||
watch(() => page.value.title, () => {
|
||||
// 路径变化时执行
|
||||
analyze()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化时执行一次
|
||||
analyze()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.blog-stats {
|
||||
margin-bottom: 10px;
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 5px;
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
font-size: 32px;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
background: -webkit-linear-gradient(10deg, #3DC8F0 5%, #8036FA 15%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
transform: translate(0px , 2px);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
  这里统计了正文部分的文字和图片数量,并通过相应规则计算阅读时间。
|
||||
  通过`page.frontmatter`获取自定义的元数据信息并在博客最上面显示。
|
||||
|
||||
### 3.2.3 注册组件
|
||||
  在`.vitepress/theme/index.ts`注册组件(参考3.1节内容)。
|
||||
|
||||
### 3.2.4 自定义布局组件
|
||||
  需要将博客信息组件放在自定义布局组件中。
|
||||
  在`.vitepress/theme/components`文件夹下新建自定义布局组件`MyLayout.vue`。
|
||||
```vue
|
||||
<template>
|
||||
<DefaultTheme.Layout v-bind="$attrs">
|
||||
<template #doc-before>
|
||||
<ArticleMetadat />
|
||||
</template>
|
||||
</DefaultTheme.Layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import ArticleMetadat from './ArticleMetadata.vue';
|
||||
</script>
|
||||
```
|
||||
|
||||
  更多插槽可以参考[Github源码](https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/Layout.vue)。
|
||||
  同样需要注册组件。
|
||||
|
||||
### 3.2.5 配置布局组件
|
||||
  在`.vitepress/config.mts`中配置布局:
|
||||
```ts
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import MyLayout from './components/MyLayout.vue';
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout: MyLayout,
|
||||
};
|
||||
```
|
||||
|
||||
## 3.3 热力图
|
||||
  使用`vue3-calendar-heatmap`组件在首页绘制博客热力图。
|
||||
### 3.3.1 安装
|
||||
```sh
|
||||
pnpm install vue3-calendar-heatmap
|
||||
```
|
||||
|
||||
### 3.3.2 获取数据
|
||||
  使用`VitePress`提供的[createContentLoader](https://vitepress.dev/zh/guide/data-loading)来获取所有的`md`博客数据。
|
||||
  在`.vitepress/data`新建`posts.data.js`:
|
||||
```js
|
||||
import { createContentLoader } from 'vitepress'
|
||||
|
||||
export default createContentLoader('./**/*.md', {
|
||||
includeSrc: true, // 包含原始 markdown 源
|
||||
render: true, // 包含渲染的整页 HTML
|
||||
transform(rawData) {
|
||||
return rawData.filter(page => {
|
||||
return page.url.includes('.html') && page.frontmatter.date
|
||||
}).map((page) => {
|
||||
// 对每个页面进行处理
|
||||
return {
|
||||
// 返回你需要的页面数据
|
||||
title: page.frontmatter.title,
|
||||
date: page.frontmatter.date,
|
||||
url: page.url
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
  通过判断路径是否包含`html`来确认是博客文档,并且筛选出元数据中包含日期的博客,然后返回相应内容。
|
||||
|
||||
### 3.3.3 博客统计组件
|
||||
  在`.vitepress/theme/components`文件夹下新建博客信息组件`StatsChart.vue`:
|
||||
```vue
|
||||
<template>
|
||||
<div v-if="!isMobile()">
|
||||
<calendar-heatmap
|
||||
:values="heatmapData"
|
||||
:end-date="new Date()"
|
||||
no-data-text="暂无记录"
|
||||
tooltip-unit="篇"
|
||||
:round="2"
|
||||
:locale="{
|
||||
less: '少于',
|
||||
more: '多于',
|
||||
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
days: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
}"
|
||||
class="blog-heatmap"
|
||||
/>
|
||||
<div class="heatmap-footer">
|
||||
⏱️ 已坚持<span class="highlight">{{ getDaysDifference(new Date(), startBlogDate) }}</span> 天
|
||||
| 📝 已创作<span class="highlight">{{ data.length }}</span> 篇
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CalendarHeatmap } from 'vue3-calendar-heatmap';
|
||||
import 'vue3-calendar-heatmap/dist/style.css';
|
||||
import { data } from '../../data/posts.data.js'
|
||||
import { isMobile, getDaysDifference, startBlogDate } from "../utils";
|
||||
|
||||
const dateCountMap = new Map<string, number>();
|
||||
|
||||
// 按日期分组统计
|
||||
data.forEach(item => {
|
||||
const date = item.date
|
||||
|
||||
if (dateCountMap.has(date)) {
|
||||
dateCountMap.set(date, dateCountMap.get(date)! + 1);
|
||||
} else {
|
||||
dateCountMap.set(date, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// 转换为目标格式
|
||||
const heatmapData = Array.from(dateCountMap.entries()).map(([date, count]) => ({
|
||||
date,
|
||||
count
|
||||
}))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.blog-heatmap {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.blog-heatmap :deep(.vch__wrapper) {
|
||||
font-family: 'CustomFont', sans-serif !important;
|
||||
}
|
||||
|
||||
.blog-heatmap :deep(.vch__month__label) {
|
||||
font-size: 8px !important;
|
||||
}
|
||||
|
||||
.blog-heatmap :deep(.vch__day__label) {
|
||||
font-size: 8px !important;
|
||||
}
|
||||
|
||||
.heatmap-footer {
|
||||
font-size: 15px;
|
||||
color: #666;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
font-weight: 700;
|
||||
color: #6366f1;
|
||||
margin: 0 2px;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
  根据`posts.data.js`提供的博客数据进行日期分组,并统计已坚持天数和已创作博客数。
|
||||
|
||||
### 3.3.4 注册组件
|
||||
  在`.vitepress/theme/index.ts`注册组件(参考3.1节内容)。
|
||||
|
||||
### 3.3.5 使用组件
|
||||
  在`docs/index.md`最后添加组件。
|
||||
|
||||
::: tip
|
||||
如果在打包时遇到`Named export 'CalendarHeatmap' not found. The requested module 'vue3-calendar-heatmap' is a CommonJS module, which may not support all module.exports as named exports.`错误提示,说明`vue3-calendar-heatmap`是`CommonJS`模块,而你在代码中使用了`ES`模块的命名导入方式,需要在`config.mts`中添加以下配置:
|
||||
```ts
|
||||
export default defineConfig({
|
||||
vite: {
|
||||
ssr: {
|
||||
noExternal: ['vue3-calendar-heatmap']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
:::
|
||||
|
||||
## 3.4 Mermaid 图表
|
||||
### 3.4.1 安装
|
||||
```sh
|
||||
pnpm i vitepress-plugin-mermaid mermaid -D
|
||||
```
|
||||
|
||||
### 3.4.2 配置
|
||||
  在`.vitepress/config.mts`中配置:
|
||||
```ts
|
||||
import { withMermaid } from 'vitepress-plugin-mermaid'
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default withMermaid({
|
||||
title: "拾光记"
|
||||
})
|
||||
```
|
||||
|
||||
### 3.4.3 使用
|
||||
  在`Markdown`中使用:
|
||||
```md
|
||||
'''mermaid
|
||||
graph TD
|
||||
A[Vue 3] --> B[Compiler]
|
||||
A --> C[Runtime]
|
||||
A --> D[Reactivity]
|
||||
|
||||
B --> B1[Parser]
|
||||
B --> B2[Transformer]
|
||||
B --> B3[Codegen]
|
||||
|
||||
C --> C1[Virtual DOM]
|
||||
C --> C2[Renderer]
|
||||
C --> C3[Component]
|
||||
|
||||
D --> D1[Proxy]
|
||||
D --> D2[Effect]
|
||||
D --> D3[Dependency Graph]
|
||||
|
||||
B3 -->|生成| C1
|
||||
C1 -->|Diff/Patch| C2
|
||||
D3 -->|驱动更新| C1
|
||||
'''
|
||||
```
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
date: 2025-11-26
|
||||
---
|
||||
|
||||
# MySQL知识点
|
||||
## 一、基础知识
|
||||
### 1.1 数据类型
|
||||
#### 1.1.1 汇总
|
||||
# 一、基础知识
|
||||
## 1.1 数据类型
|
||||
### 1.1.1 汇总
|
||||
|类型|存储空间|范围|适用场景|
|
||||
| :-------------------------------------------------------------------------------------------------: | :-----------------: | :--------------------------: | :-----------------------: |
|
||||
|<span data-type="text" style="background-color: var(--b3-card-error-background); color: var(--b3-card-error-color);">TINYINT</span>|1字节|-128 \~ 127|状态码、年龄、布尔值|
|
||||
@@ -34,15 +33,15 @@
|
||||
|MEDIUMBLOB|16MB||中等二进制对象|
|
||||
|LONGBLOB|4GB||超大二进制对象|
|
||||
|
||||
#### 1.1.2 法则
|
||||
### 1.1.2 法则
|
||||
  **整数选择**:优先INT,大数量用BIGINT,布尔值用TINYINT(1)或BIT(1)
|
||||
  **小数选择**:金融金额必须用DECIMAL,非精确测量可用FLOAT/DOUBLE
|
||||
  **字符串选择**:定长编码用CHAR,变长文本用VARCHAR,大文本用TEXT系列
|
||||
  **时间选择**:日期用DATE,精确时间用DATETIME,自动更新用TIMESTAMP
|
||||
  **特殊场景**:多选项用SET,结构化数据用JSON
|
||||
|
||||
### 1.2 常用函数
|
||||
#### 1.2.1 字符串函数
|
||||
## 1.2 常用函数
|
||||
### 1.2.1 字符串函数
|
||||
1. CONCAT(str1, str2, ...):连接字符串
|
||||
```sql
|
||||
SELECT CONCAT('Hello', ' ', 'World');
|
||||
@@ -56,7 +55,7 @@ SELECT LENGTH('Hello');
|
||||
SELECT SUBSTRING('Hello World', 7, 5); -- 返回 'World'
|
||||
```
|
||||
|
||||
#### 1.2.2 日期/时间函数
|
||||
### 1.2.2 日期/时间函数
|
||||
1. NOW():返回当前日期和时间(yyyy-MM-DD HH:mm:ss)。
|
||||
2. CURDATE():返回当前日期,不带时间部分(yyyy-MM-DD)。
|
||||
3. DATE_FORMAT(date, format):按照指定的格式返回日期值。
|
||||
@@ -76,7 +75,7 @@ SELECT DATEDIFF('2024-12-31', '2024-01-01'); -- 返回364
|
||||
SELECT STR_TO_DATE('01-09-2024', '%d-%m-%Y'); -- 返回 '2024-09-01'
|
||||
```
|
||||
|
||||
#### 1.2.3 数值函数
|
||||
### 1.2.3 数值函数
|
||||
1. ROUND(x, d):将数值 x 四舍五入到 d 位小数。
|
||||
```sql
|
||||
SELECT ROUND(123.4567, 2); -- 返回 123.46
|
||||
@@ -87,13 +86,13 @@ SELECT FLOOR(2.9); -- 返回 2
|
||||
SELECT CEIL(2.1); -- 返回 3
|
||||
```
|
||||
|
||||
#### 1.2.4 聚合函数
|
||||
### 1.2.4 聚合函数
|
||||
1. COUNT(expression):返回某列中的记录数。
|
||||
2. SUM(expression):返回某列中数值的总和。
|
||||
3. AVG(expression):返回某列中数值的平均值。
|
||||
4. MAX(expression) / MIN(expression):返回某列的最大值或最小值。
|
||||
|
||||
#### 1.2.5 控制流函数
|
||||
### 1.2.5 控制流函数
|
||||
1. IF(condition, true_value, false_value):如果 condition 为真,返回 true_value,否则返回 false_value。
|
||||
```sql
|
||||
SELECT IF(1 > 0, 'Yes', 'No'); -- 返回 'Yes'
|
||||
@@ -109,11 +108,11 @@ SELECT
|
||||
FROM employees;
|
||||
```
|
||||
|
||||
## 二、索引
|
||||
# 二、索引
|
||||
  索引是对数据库表中的一列或多列值进行**排序**的一种结构,使用索引可以快速访问数据库表中的特定信息。
|
||||
  **索引相当于图书上的目录,可以根据目录上的页码快速找到所需的内容,提高性能(查询速度)**
|
||||
|
||||
### 2.1 优缺点
|
||||
## 2.1 优缺点
|
||||
优点:
|
||||
* **查询速度起飞 (主要目的)** :通过索引,数据库可以**大幅减少需要扫描的数据量**,直接定位到符合条件的记录,从而显著加快数据检索速度,减少磁盘 I/O 次数。
|
||||
* **保证数据唯一性**:通过创建**唯一索引 (Unique Index)** ,可以确保表中的某一列(或几列组合)的值是独一无二的,比如用户ID、邮箱等。**主键本身就是一种唯一索引**。
|
||||
@@ -124,7 +123,7 @@ FROM employees;
|
||||
* **占用存储空间**:索引本质上也是一种数据结构,需要以物理文件(或内存结构)的形式存储,因此会**额外占用一定的磁盘空间**。索引越多、越大,占用的空间也就越多。
|
||||
* **可能被误用或失效**:如果索引设计不当,或者查询语句写得不好,数据库优化器可能不会选择使用索引(或者选错索引),反而导致性能下降。
|
||||
|
||||
### 2.2 适用场景
|
||||
## 2.2 适用场景
|
||||
适用场景
|
||||
* **频繁作为查询条件的字段**应该创建索引
|
||||
* **查询中排序的字段**创建索引将大大提高排序的速度(索引就是排序加快速查找)
|
||||
@@ -135,7 +134,7 @@ FROM employees;
|
||||
* **表记录太少,不需要创建索引**;
|
||||
* **数据重复且分布平均的字段**,因此为经常查询的和经常排序的字段建立索引。注意某些数据包含大量重复数据,因此他建立索引就没有太大的效果,例如性别字段,只有男女,不适合建立索引。
|
||||
|
||||
### 2.3 数据结构
|
||||
## 2.3 数据结构
|
||||
  在 MySQL 中,MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构。
|
||||

|
||||
  例如要查找id=75的用户:`SELECT * FROM users WHERE id = 75`
|
||||
@@ -152,7 +151,7 @@ FROM employees;
|
||||
|叶子节点连接|支持范围查询|ORDER BY、分页查询快|
|
||||
|只在叶子存数据|内部节点小|更多索引数据放入内存|
|
||||
|
||||
### 2.4 索引类型
|
||||
## 2.4 索引类型
|
||||
1. 主键索引
|
||||
主键索引是最特殊的索引。
|
||||
```sql
|
||||
@@ -192,7 +191,7 @@ SELECT * FROM users WHERE city = '北京'; -- 无法使用索引
|
||||
  复合索引的使用最左前缀原则。
|
||||
  `CREATE INDEX idx_name_age_city ON users(name, age, city);`相当于创建了3个索引:`users(name),users(name, age),users(name, age, city)`。
|
||||
|
||||
### 2.5 索引设计
|
||||
## 2.5 索引设计
|
||||
* 为WHERE条件添加索引
|
||||
```sql
|
||||
SELECT * FROM orders WHERE user_id = 123;
|
||||
@@ -223,7 +222,7 @@ CREATE INDEX idx_city_age_create_time ON users(city, age, create_time);
|
||||
* 被频繁更新的字段应该慎重建立索引,虽然索引能带来查询上的效率,但是维护索引的成本也是不小的。 如果一个字段不被经常查询,反而被经常修改,那么就更不应该在这种字段上建立索引了。
|
||||
* 尽可能的考虑建立联合索引而不是单列索引。因为索引是需要占用磁盘空间的,可以简单理解为每个索引都对应着一颗 B+ 树。如果一个表的字段过多,索引过多,那么当这个表的数据达到一个体量后,索引占用的空间也是很多的,且修改索引时,耗费的时间也是较多的。如果是联合索引,多个字段在一个索引上,那么将会节约很大磁盘空间,且修改数据的操作效率也会提升。
|
||||
|
||||
### 2.6 索引优化
|
||||
## 2.6 索引优化
|
||||
1. 监控慢查询
|
||||
```sql
|
||||
-- 开启慢查询日志
|
||||
@@ -243,7 +242,7 @@ CREATE INDEX idx_title_prefix ON articles(title(20)); -- 只索引前20个字
|
||||
## 三、事务
|
||||
  事务是数据库操作的基本单位,它是一组原子性的 SQL 语句,或者说是一个独立的工作单元。事务内的所有操作**要么全部成功,要么全部失败**。
|
||||
|
||||
### 3.1 特性
|
||||
## 3.1 特性
|
||||
* **原子性**(`Atomicity`):原子性确保事务中的所有操作要么全部完成,要么全部不完成。如果事务执行过程中发生错误,所有已执行的操作都会回滚。
|
||||
```sql
|
||||
START TRANSACTION;
|
||||
@@ -286,13 +285,13 @@ COMMIT;
|
||||
-- 提交后,数据已经持久化到磁盘
|
||||
```
|
||||
|
||||
### 3.2 隔离级别
|
||||
#### 3.2.1 概念
|
||||
## 3.2 隔离级别
|
||||
### 3.2.1 概念
|
||||
* **脏读 (Dirty Read):** 事务A读取了事务B**尚未提交**的修改数据。如果事务B最终回滚,事务A读到的就是无效的"脏"数据。
|
||||
* **不可重复读 (Non-Repeatable Read):** 在同一个事务A中,**两次读取同一行数据**,得到的结果不同。这是因为在两次读取之间,该行数据被另一个提交了的事务B**修改**了。
|
||||
* **幻读 (Phantom Read):** 在同一个事务A中,**两次执行相同的查询**(通常是范围查询 `SELECT ... WHERE ...`),得到的结果集行数不同(出现了新的"幻影"行或原有行消失了)。这是因为在两次查询之间,另一个提交了的事务B**插入**了满足查询条件的新行或**删除**了原有的行。
|
||||
|
||||
#### 3.2.2 类型
|
||||
### 3.2.2 类型
|
||||
1. 读未提交 (READ UNCOMMITTED)
|
||||
* 特点: 这是最低的隔离级别,允许读取尚未提交的数据变更。
|
||||
* 允许的问题:
|
||||
@@ -333,7 +332,7 @@ COMMIT;
|
||||
* 数据一致性: **最好**。完全保证事务的隔离性,不会出现任何并发问题。
|
||||
* 使用场景: 仅在对数据一致性要求极高,且完全不能接受任何并发副作用(如金融核心系统的某些极端操作),并且能承受极低并发性能的情况下使用。**实践中很少使用。**
|
||||
|
||||
### 3.3 MySQL锁
|
||||
## 3.3 MySQL锁
|
||||
* **表级锁:** MySQL 中锁定粒度最大的一种锁(全局锁除外),是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。不过,触发锁冲突的概率最高,高并发下效率极低。表级锁和存储引擎无关,MyISAM 和 InnoDB 引擎都支持表级锁。
|
||||
* **行级锁:** MySQL 中锁定粒度最小的一种锁,是 **针对索引字段加的锁** ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存储引擎有关,是在存储引擎层面实现的。
|
||||
* **共享锁(S 锁)** :又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
|
||||
@@ -348,35 +347,36 @@ COMMIT;
|
||||
|UPDATE|排他锁(X)|自动获取|
|
||||
|DELETE|排他锁(X)|自动获取|
|
||||
|
||||
## 四、日志
|
||||
# 四、日志
|
||||
  MySQL 的三大核心日志系统是保证数据一致性、实现故障恢复和提供复制功能的关键组件。这三大日志分别是:二进制日志(binlog)、错误日志(error log) 和 重做日志(redo log)。
|
||||
### 4.1 二进制日志(Binary Log,简称 binlog)
|
||||
#### 4.1.1 概念
|
||||
## 4.1 二进制日志(Binary Log,简称 binlog)
|
||||
### 4.1.1 概念
|
||||
  二进制日志是 MySQL 记录所有修改数据或可能修改数据的语句(或数据变更)的日志文件。它记录了数据库执行的更改操作(如 INSERT、UPDATE、DELETE 等 DML 操作,以及 CREATE、ALTER、DROP 等 DDL 操作),但**不记录 SELECT 这类不修改数据的查询操作**。
|
||||
|
||||
#### 4.1.2 作用
|
||||
### 4.1.2 作用
|
||||
1. 主从复制(Replication):在主从架构中,主库将 binlog 发送给从库,从库通过读取并重放 binlog 来保持与主库的数据同步。
|
||||
2. 数据恢复(Point-in-Time Recovery):通过备份 + binlog 可以恢复到某个具体时间点。
|
||||
3. 审计:可以追踪数据库的所有变更操作。
|
||||
|
||||
### 4.2 重做日志(Redo Log)
|
||||
#### 4.2.1 概念
|
||||
## 4.2 重做日志(Redo Log)
|
||||
### 4.2.1 概念
|
||||
  重做日志是 InnoDB 存储引擎特有的日志,它记录的是 “物理级别” 上的页修改信息,主要用于 崩溃恢复(Crash Recovery)。
|
||||
  InnoDB 使用 WAL(Write-Ahead Logging,预写日志)技术,即 事务提交时,先写 redo log,再写磁盘数据页,从而保证即使系统崩溃,也能通过 redo log 恢复数据。
|
||||
|
||||
#### 4.2.2 作用
|
||||
### 4.2.2 作用
|
||||
1. 实现事务的持久性(Durability):确保事务提交后,即使发生宕机,数据也不会丢失。
|
||||
2. 提高写入性能:数据不是每次修改都直接写磁盘,而是先写 redo log(顺序写,速度快),后续再异步刷盘。
|
||||
3. 支持 crash-safe:MySQL 宕机重启后,可通过 redo log 恢复未刷盘的数据。
|
||||
|
||||
### 4.3 回滚日志(Undo Log)
|
||||
#### 4.3.1 概念
|
||||
## 4.3 回滚日志(Undo Log)
|
||||
### 4.3.1 概念
|
||||
  回滚日志也是 InnoDB 引擎特有 的日志,它记录的是 数据被修改前的原始值
|
||||
|
||||
#### 4.3.2 作用
|
||||
### 4.3.2 作用
|
||||
1. 支持事务回滚:如果事务执行失败或调用了 ROLLBACK,可以通过 undo log 将数据恢复到修改之前的状态。
|
||||
2. 实现 MVCC(多版本并发控制):在读已提交(RC)、可重复读(RR)隔离级别下,undo log 用于提供历史版本数据,使得不同事务能看到不同的数据快照,而不需要加锁。
|
||||
### 4.4 和事务、锁之间的关系
|
||||
|
||||
## 4.4 和事务、锁之间的关系
|
||||
  例如,当执行一条SQL语句时,事务、锁、日志等之间的关系:
|
||||
1. 发起一个事务
|
||||
2. 执行一系列的DML操作(如 INSERT/UPDATE/DELETE)
|
||||
@@ -389,7 +389,7 @@ COMMIT;
|
||||
  **undo log:用于实现事务回滚、MVCC。**
|
||||
4. **背后有日志系统默默记录一切,锁系统保障并发安全,隔离级别定义了“你能看到啥”。**
|
||||
|
||||
#### 4.4.1 相互关系
|
||||
### 4.4.1 相互关系
|
||||
1. 事务是锁的使用者,锁是事务实现隔离性的手段。当一个事务对某行数据执行 UPDATE/DELETE 操作时,为了防止其他事务同时修改相同数据,InnoDB 会自动对该行或索引加 排他锁(X锁)。如果事务只是读取数据,根据隔离级别,可能会加 共享锁(S锁) 或使用 MVCC(不加锁)。
|
||||
2. 隔离级别定义了事务之间的可见性规则,是事务“隔离性”的具体体现。事务的隔离性是通过锁 + MVCC(多版本并发控制,依赖 undo log)+ 隔离级别共同实现的。
|
||||
3. 日志是事务实现 持久性、崩溃恢复、回滚 等能力的基石。redo log(重做日志)保障事务的持久性(Durability),事务提交时,先将数据页的变更记录到 redo log(顺序写,高性能),随后再异步刷盘到磁盘数据页。即使系统崩溃,重启后也能通过 redo log 恢复已提交但尚未刷盘的数据。undo log(回滚日志)支持事务回滚 和 MVCC。事务修改数据前,会先把原始数据保存到 undo log,如果事务回滚,可以用它恢复旧值。同时,undo log 也是 MVCC(多版本控制)实现的基础,用于提供历史版本数据。binlog(二进制日志)用于主从复制、时间点恢复。虽然 binlog 是 Server 层的日志,不属于 InnoDB 事务引擎的一部分,但在事务提交时,binlog 与 redo log 通过两阶段提交(2PC)保证一致性。
|
||||
@@ -397,14 +397,14 @@ COMMIT;
|
||||
5. 锁保障并发安全,日志保障操作可恢复。两者从不同维度确保数据库的正确性。锁:是在运行时控制谁可以访问哪些数据,是 并发控制 的手段。日志:是在磁盘上记录操作过程,是 故障恢复 & 事务一致性 的手段。
|
||||
6. 隔离级别定义了事务间数据的可见性,而日志(尤其是 undo log 和 binlog)为这种“可见性”提供了实现基础。undo log 是实现 MVCC(多版本并发控制) 的基础,而 MVCC 是 REPEATABLE READ 等隔离级别的关键。binlog 虽不直接影响隔离性,但它记录了事务的变更历史,是构建主从环境、实现数据恢复的基础。
|
||||
|
||||
## 五、优化
|
||||
### 5.1 Explain 执行计划
|
||||
# 五、优化
|
||||
## 5.1 Explain 执行计划
|
||||
  `EXPLAIN`是 MySQL 自带的一个诊断工具,它可以模拟 MySQL 查询优化器的执行过程,对`SELECT`语句(在 MySQL 8.0 及以上版本,也支持对`UPDATE`、`DELETE`等语句使用)进行分析,并输出该语句的执行计划。
|
||||
#### 5.1.1 基本用法
|
||||
### 5.1.1 基本用法
|
||||
  在select语句前面加上EXPLAIN关键字即可,例如:`EXPLAIN SELECT * FROM users WHERE age > 25;
|
||||
`
|
||||
|
||||
#### 5.1.2 输出列说明
|
||||
### 5.1.2 输出列说明
|
||||
| 列名 | 说明 | 示例 |
|
||||
| :-: | :-: | :-: |
|
||||
| id | 查询标识符 | 相同 id 表示同组查询,执行顺序从上到下;不同 id 值越大优先级越高 |
|
||||
@@ -420,7 +420,7 @@ COMMIT;
|
||||
| filtered | 存储引擎返回数据后在 server 层过滤的比例 | |
|
||||
| **Extra** | 额外执行信息 | |
|
||||
|
||||
#### 5.1.3 重要指标:Type
|
||||
### 5.1.3 重要指标:Type
|
||||
| 类型 | 描述 | 性能 | 示例 |
|
||||
| - | - | - | - |
|
||||
| system | 系统表,仅一行 | 最优 | MyISAM 引擎的空表 |
|
||||
@@ -436,7 +436,7 @@ COMMIT;
|
||||
| index | 全索引扫描 | 差 | SELECT indexed_col FROM table |
|
||||
| ALL | 全表扫描 | 最差 | 无索引查询|
|
||||
|
||||
#### 5.1.4 重要指标:Extra
|
||||
### 5.1.4 重要指标:Extra
|
||||
| 值 | 含义 | 优化建议 |
|
||||
| - | - | - |
|
||||
| Using index | 覆盖索引(无需回表) | 优,保持 |
|
||||
@@ -449,7 +449,7 @@ COMMIT;
|
||||
| Impossible WHERE | WHERE 条件永不成立 | 查询逻辑错误 |
|
||||
| Distinct | 优化 DISTINCT 操作 | |
|
||||
|
||||
## 六、执行过程
|
||||
# 六、执行过程
|
||||

|
||||
  MySQL架构分为两层,Service层和存储引擎层。
|
||||
  Server 层负责建立连接、分析和执行 SQL。存储引擎层负责数据的存储和提取。
|
||||
@@ -458,4 +458,63 @@ COMMIT;
|
||||
3. 解析SQL,通过解析器对SQL查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段和语句类型等。
|
||||
4. 执行SQL:分为预处理阶段、优化阶段和执行阶段。
|
||||
|
||||
# 七、视图
|
||||
## 7.1 定义
|
||||
  视图(View)是 MySQL 中的一种虚拟表,它的数据来源于一个或多个实际的表(基表),**其结构和数据是通过 SQL 查询语句定义的**。简单来说,视图就像是一个 “查询窗口”,你看到的是基表的数据,但**视图本身并不存储实际数据,每次访问视图时都会执行对应的查询语句**。
|
||||
|
||||
## 7.2 作用
|
||||
1. 简化复杂查询:将多表关联、聚合等复杂的 SQL 逻辑封装到视图中,后续使用时只需查询视图即可。
|
||||
2. 数据安全:可以只暴露基表中的部分列或部分行给用户,隐藏敏感数据。
|
||||
3. 数据一致性:如果业务逻辑发生变化,只需修改视图的定义,而无需修改所有使用该逻辑的查询。
|
||||
|
||||
## 7.3 使用
|
||||
  创建视图
|
||||
```sql
|
||||
-- 创建视图v_student_basic
|
||||
CREATE VIEW v_student_basic
|
||||
AS
|
||||
SELECT name, age, gender FROM student;
|
||||
```
|
||||
|
||||
  使用视图和正常使用数据表一致:
|
||||
```sql
|
||||
-- 查询视图(和查询普通表的方式完全一样)
|
||||
SELECT * FROM v_student_basic;
|
||||
```
|
||||
|
||||
# 八、事件
|
||||
## 8.1 定义
|
||||
  MySQL 事件(Event)也被称为事件调度器(Event Scheduler),是 `MySQL 中一种定时执行的数据库对象`,可以理解为数据库层面的 “定时任务” 或 “计划任务”。它能根据你设定的时间规则(一次性执行、周期性执行),自动触发并执行指定的 SQL 逻辑(如数据清理、统计报表生成、数据同步等)。
|
||||
|
||||
## 8.2 特点
|
||||
1. 定时执行:支持一次性执行(如某个具体时间点)和周期性执行(如每天凌晨 3 点、每小时执行一次)。
|
||||
2. 自动运行:依赖 MySQL 的事件调度器线程,只要调度器开启,事件就会按规则自动触发。
|
||||
3. 与存储过程结合:事件的执行逻辑可以是单条 SQL,也可以是复杂的存储过程(推荐用存储过程封装复杂逻辑)。
|
||||
|
||||
## 8.3 作用
|
||||
- 数据清理:定期删除过期数据(如删除 30 天前的日志表数据)。
|
||||
- 数据统计:定时生成业务统计报表(如每天凌晨统计前一天的订单数据)。
|
||||
- 数据同步:定期将 A 表的数据同步到 B 表。
|
||||
- 定时备份:定期执行数据库备份脚本(配合存储过程)。
|
||||
|
||||
## 8.4 使用
|
||||
  开启事件调度器:
|
||||
```sql
|
||||
-- 查看事件调度器状态(ON表示开启,OFF表示关闭)
|
||||
SHOW VARIABLES LIKE 'event_scheduler';
|
||||
|
||||
-- 临时开启(MySQL重启后会恢复为默认状态)
|
||||
SET GLOBAL event_scheduler = ON;
|
||||
-- 永久开启(需要修改my.cnf/my.ini配置文件,添加以下内容,然后重启MySQL)
|
||||
event_scheduler = ON # 放在[mysqld]节点下
|
||||
```
|
||||
|
||||
  每天凌晨 1 点执行的事件,删除日志表中 7 天前的过期数据:
|
||||
```sql
|
||||
-- 创建周期性事件,每天凌晨1点执行,无结束时间
|
||||
CREATE EVENT IF NOT EXISTS event_clear_log
|
||||
ON SCHEDULE EVERY 1 DAY STARTS '2025-12-22 01:00:00'
|
||||
COMMENT '每天凌晨1点删除7天前的日志'
|
||||
DO
|
||||
DELETE FROM log WHERE create_time < DATE_SUB(NOW(), INTERVAL 7 DAY);
|
||||
```
|
||||
41
docs/Web-Backend/MySQL/SQL-Advance.md
Normal file
41
docs/Web-Backend/MySQL/SQL-Advance.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: SQL高阶用法
|
||||
date: 2025-12-21
|
||||
---
|
||||
|
||||
# 一、WITH
|
||||
## 1.1 定义
|
||||
  SQL 中的WITH子句也被称为**公用表表达式**(CTE,Common Table Expression),它的作用是**在执行主查询之前,先定义一个临时的结果集,这个结果集可以在后续的查询中被多次引用**,就像一个临时表一样。它能让复杂的 SQL 查询变得更清晰、更易读,还能简化嵌套查询的逻辑。
|
||||
|
||||
## 1.2 使用
|
||||
```sql
|
||||
-- 定义第一个CTE:数学高分学生
|
||||
WITH math_high_score AS (
|
||||
SELECT name, score FROM student_score WHERE subject = '数学' AND score > 85
|
||||
),
|
||||
-- 定义第二个CTE:语文高分学生
|
||||
chinese_high_score AS (
|
||||
SELECT name, score FROM student_score WHERE subject = '语文' AND score > 85
|
||||
)
|
||||
-- 主查询:查询既在数学高分又在语文高分的学生
|
||||
SELECT m.name
|
||||
FROM math_high_score m
|
||||
JOIN chinese_high_score c ON m.name = c.name;
|
||||
```
|
||||
|
||||
  递归 CTE:
|
||||
```sql
|
||||
WITH recursive dept_hierarchy AS (
|
||||
-- 锚点成员:查询顶级部门(parent_id为NULL)
|
||||
SELECT dept_id, dept_name, parent_id, 1 AS level
|
||||
FROM department
|
||||
WHERE parent_id IS NULL
|
||||
UNION ALL
|
||||
-- 递归成员:查询子部门,关联自身的dept_id和parent_id
|
||||
SELECT d.dept_id, d.dept_name, d.parent_id, dh.level + 1 AS level
|
||||
FROM department d
|
||||
JOIN dept_hierarchy dh ON d.parent_id = dh.dept_id
|
||||
)
|
||||
-- 主查询:获取所有部门的层级
|
||||
SELECT * FROM dept_hierarchy;
|
||||
```
|
||||
Reference in New Issue
Block a user