<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>白开水船长</title>
  
  <subtitle>东隅已逝，桑榆非晚</subtitle>
  <link href="https://cq230.github.io/atom.xml" rel="self"/>
  
  <link href="https://cq230.github.io/"/>
  <updated>2026-03-24T15:40:31.350Z</updated>
  <id>https://cq230.github.io/</id>
  
  <author>
    <name>白开水船长</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>AI 对话延续提示词模板</title>
    <link href="https://cq230.github.io/posts/507bc0c3.html"/>
    <id>https://cq230.github.io/posts/507bc0c3.html</id>
    <published>2026-03-21T16:32:00.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h2 id="提示词-1：在当前对话末尾使用，生成总结"><a href="#提示词-1：在当前对话末尾使用，生成总结" class="headerlink" title="提示词 1：在当前对话末尾使用，生成总结"></a>提示词 1：在当前对话末尾使用，生成总结</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">我们的对话已经很长了，我想把内容带到新对话中继续。请帮我生成一份结构化的对话总结，要求如下：</span><br><span class="line"></span><br><span class="line">1. 【背景】我们在讨论什么？简要说明主题和目标</span><br><span class="line">2. 【已完成】已经达成的结论、决定、或已完成的工作成果</span><br><span class="line">3. 【关键内容】对话中产生的重要代码/数据/文本（请保留原文，不要概括）</span><br><span class="line">4. 【未解决】目前还没解决的问题或待办事项</span><br><span class="line">5. 【约定】我们之间约定好的规则、偏好、风格要求等</span><br><span class="line"></span><br><span class="line">请确保总结足够完整，让一个全新的 AI 对话仅凭这份总结就能无缝接手继续工作。如有遗漏请补充。</span><br></pre></td></tr></table></figure><hr><h2 id="提示词-2：在新对话开头使用，载入上下文"><a href="#提示词-2：在新对话开头使用，载入上下文" class="headerlink" title="提示词 2：在新对话开头使用，载入上下文"></a>提示词 2：在新对话开头使用，载入上下文</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">我正在延续上一轮对话，以下是之前的讨论总结：</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">【在此粘贴上面生成的总结】</span><br><span class="line">---</span><br><span class="line"></span><br><span class="line">请先确认你已理解以上背景，然后我们继续讨论。</span><br><span class="line">如果总结中有不清楚或可能遗漏的地方，请先向我确认。</span><br><span class="line"></span><br><span class="line">我接下来想要继续处理的是：[在此填写你的具体问题或任务]</span><br></pre></td></tr></table></figure><hr><h2 id="使用说明"><a href="#使用说明" class="headerlink" title="使用说明"></a>使用说明</h2><ol><li>当你感觉对话变长、AI 回答质量下降时，复制<strong>提示词 1</strong> 发送</li><li>AI 会输出一份结构化总结</li><li>快速检查总结是否有遗漏，有的话让 AI 补充</li><li>开一个新对话，复制<strong>提示词 2</strong>，把总结粘贴进去即可</li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;提示词-1：在当前对话末尾使用，生成总结&quot;&gt;&lt;a href=&quot;#提示词-1：在当前对话末尾使用，生成总结&quot; class=&quot;headerlink&quot; title=&quot;提示词 1：在当前对话末尾使用，生成总结&quot;&gt;&lt;/a&gt;提示词 1：在当前对话末尾使用，生成总结&lt;/h2&gt;&lt;</summary>
      
    
    
    
    <category term="AI探索" scheme="https://cq230.github.io/categories/AI%E6%8E%A2%E7%B4%A2/"/>
    
    
    <category term="AI" scheme="https://cq230.github.io/tags/AI/"/>
    
    <category term="Claude" scheme="https://cq230.github.io/tags/Claude/"/>
    
    <category term="AI提示词" scheme="https://cq230.github.io/tags/AI%E6%8F%90%E7%A4%BA%E8%AF%8D/"/>
    
  </entry>
  
  <entry>
    <title>2025.4.13-随笔</title>
    <link href="https://cq230.github.io/posts/35ad0ca1.html"/>
    <id>https://cq230.github.io/posts/35ad0ca1.html</id>
    <published>2025-04-12T17:04:34.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>最近工作比较忙，忘记给工位上的盆栽浇水，前天浇水的时候看到它们缺水的样子忽然想起小时候语文课上学过的一篇课文，记得讲的是一个种树的人故意不按规律给树苗浇水，模仿随机下雨的情况。今天趁着有空重温了这篇文章，是人教版《语文・六年级・下册》教科书第 3 课课文，林清玄的《桃花心木》，如果不是借助浇水的契机或许我再也不会回忆起这篇文章，有些时候突然想起一些尘封的记忆碎片就忍不住感慨原来已经是那么久之前的事了，时隔十几年再读这篇文章我已经记不起第一次看到这些文字的感受了。虽然这些都是一些小事，但是我觉得这种回顾过去的心情有被记录下来的必要，最近一两年愈发体会到记录生活的重要性了，偶尔看到相册里过去随手拍的照片都能立刻回想起相关联的事情，现在翻相册的乐趣除了看保存的梗图就是回顾这些被记录下的瞬间，我希望在未来的时光里不要遗忘这些美好的瞬间，这就是我记录的动力。</p><hr><p>附上原文及插图</p><blockquote><h2 id="桃花心木"><a href="#桃花心木" class="headerlink" title="桃花心木"></a>桃花心木</h2><p>林清玄</p><p>乡下老家屋旁，有一块非常大的空地，租给人家种桃花心木的树苗。</p><p>桃花心木是一种特别的树，树形优美，高大而笔直，从前老家林场种了许多，已长成几丈高的一片树林。所以当我看到桃花心木仅及膝盖的树苗，有点儿难以相信自己的眼睛。</p><p>种桃花心木苗的是一个个子很高的人，他弯腰种树的时候，感觉就像插秧一样。</p><p>树苗种下以后，他常来浇水。奇怪的是，他来得并没有规律，有时隔三天，有时隔五天，有时十几天才来一次；浇水的量也不一定，有时浇得多，有时浇得少。</p><p>我住在乡下时，天天都会在桃花心木苗旁的小路上散步，种树苗的人偶尔会来家里喝茶。他有时早上来，有时下午来，时间也不一定。</p><p>我越来越感到奇怪。</p><p>更奇怪的是，桃花心木苗有时莫名其妙地枯萎了。所以， 他来的时候总会带几株树苗来补种。</p><p>我起先以为他太懒，有时隔那么久才给树浇水。</p><p>但是，懒人怎么知道有几棵树会枯萎呢？</p><p>后来我以为他太忙，才会做什么事都不按规律。但是，忙人怎么可能做事那么从从容容？</p><p>我忍不住问他：到底应该什么时间来？多久浇一次水？桃花心木为什么无缘无故会枯萎？如果你每天来浇水，桃花心木苗应该不会枯萎吧？</p><p>种树的人笑了，他说：“种树不是种菜或种稻子，种树是百年的基业，不像青菜几个星期就可以收成。所以，树木自己要学会在土里找水源。我浇水只是模仿老天下雨，老天下雨是算不准的，它几天下一次？上午或下午？一次下多少？如果无法在这种不确定中汲水生长，树苗自然就枯萎了。但是，在不确定中找到水源、拼命扎根的树，长成百年的大树就不成问题了。”</p><p>种树人语重心长地说：“如果我每天都来浇水，每天定时浇一定的量，树苗就会养成依赖的心，根就会浮在地表上，无法深入地下，一旦我停止浇水，树苗会枯萎得更多。幸而存活的树苗，遇到狂风暴雨，也会一吹就倒。”</p><p>种树人的一番话，使我非常感动。不只是树，人也是一样，在不确定中生活的人，能比较经得起生活的考验，会锻炼出一颗独立自主的心。在不确定中，就能学会把很少的养分转化为巨大的能量，努力生长。</p><p>现在，窗前的桃花心木苗已经长得与屋顶一般高，是那么优雅自在，显示出勃勃生机。</p><p>种树的人不再来了，桃花心木也不会枯萎了。</p></blockquote><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1744480950512_R-C.jfif" alt="R-C.jfif"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近工作比较忙，忘记给工位上的盆栽浇水，前天浇水的时候看到它们缺水的样子忽然想起小时候语文课上学过的一篇课文，记得讲的是一个种树的人故意不按规律给树苗浇水，模仿随机下雨的情况。今天趁着有空重温了这篇文章，是人教版《语文・六年级・下册》教科书第 3 课课文，林清玄的《桃花心木</summary>
      
    
    
    
    <category term="心情随笔" scheme="https://cq230.github.io/categories/%E5%BF%83%E6%83%85%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="随笔" scheme="https://cq230.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>本站更新日志页面配置</title>
    <link href="https://cq230.github.io/posts/e6f9968d.html"/>
    <id>https://cq230.github.io/posts/e6f9968d.html</id>
    <published>2025-01-07T21:39:15.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>内容转载自<a href="https://blog.cent1pedee.top/posts/bda0f2db.html#%E6%A0%B7%E5%BC%8F%E9%A2%84%E8%A7%88">煮雪话河山</a></p><p>最近在迁移博客时，发现当时对博客的很多配置都记不清了，于是萌生了记录折腾网站的日志，本篇主要记录本站更新日志页面的创建及配置。</p><h2 id="1-配置文件"><a href="#1-配置文件" class="headerlink" title="1.配置文件"></a>1.配置文件</h2><p>在 <code>_config.anzhiyu.yml</code> 下的 <code>menu:</code> 添加 <code>日志: /update/ || xxx</code> , xxx 为你所想添加的图标，也可去掉。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">menu:</span></span><br><span class="line">  <span class="string">日志:</span> <span class="string">/update/</span> <span class="string">||</span> <span class="string">anzhiyu-icon-bolt</span></span><br></pre></td></tr></table></figure><hr><h2 id="2-Pug-文件修改"><a href="#2-Pug-文件修改" class="headerlink" title="2.Pug 文件修改"></a>2.Pug 文件修改</h2><p>在 <code>themes/anzhiyu/layout/page.pug</code> 中添加以下内容</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">when &#x27;update&#x27;</span><br><span class="line">  include includes/page/update.pug</span><br></pre></td></tr></table></figure><blockquote><p>注意，Pug 文件的缩进十分严格，在 Pug 中，缩进用于表示层级结构。</p></blockquote><hr><h2 id="3-添加-update-pug-文件"><a href="#3-添加-update-pug-文件" class="headerlink" title="3.添加 update.pug 文件"></a>3.添加 update.pug 文件</h2><p>在 <code>themes/anzhiyu/layout/includes/page</code> 下新建 <code>update.pug</code> 文件，并添加以下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">#update</span><br><span class="line">  if site.data.update</span><br><span class="line">    each i in site.data.update</span><br><span class="line">      .author-content.author-content-item.UpdatePage.single(style = `background: url($&#123;i.top_background&#125;) left 37% / cover no-repeat !important;`)</span><br><span class="line">        .card-content</span><br><span class="line">          .author-content-item-tips=i.class_name</span><br><span class="line">          span.author-content-item-title=i.description</span><br><span class="line">          .content-bottom</span><br><span class="line">            .tips=i.tip</span><br><span class="line">          .banner-button-group</span><br><span class="line">            a.banner-button(href=i.buttonLink)</span><br><span class="line">              i.anzhiyufont.anzhiyu-icon-arrow-circle-right(style=&#x27;font-size: 1.3rem&#x27;)</span><br><span class="line">              span.banner-button-text=i.buttonText</span><br><span class="line"></span><br><span class="line">  #article-container</span><br><span class="line">    != page.content</span><br></pre></td></tr></table></figure><hr><h2 id="4-添加-update-yml-文件"><a href="#4-添加-update-yml-文件" class="headerlink" title="4.添加 update.yml 文件"></a>4.添加 update.yml 文件</h2><p>在 <code>source/_data</code> 下新建 <code>update.yml</code> 文件，并添加以下内容：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="attr">top_background:</span> <span class="string">&quot;https://xxxxxx.png&quot;</span></span><br><span class="line">  <span class="attr">class_name:</span> <span class="string">&quot;Update Class 1&quot;</span></span><br><span class="line">  <span class="attr">description:</span> <span class="string">&quot;Description of the first update&quot;</span></span><br><span class="line">  <span class="attr">tip:</span> <span class="string">&quot;Tip for the first update&quot;</span></span><br><span class="line">  <span class="attr">buttonLink:</span> <span class="string">&quot;http://link_to_update1.com&quot;</span></span><br><span class="line">  <span class="attr">buttonText:</span> <span class="string">&quot;More&quot;</span></span><br></pre></td></tr></table></figure><blockquote><p>如果不想要页面上方图片，将此文件中的内容全部注释即可</p></blockquote><hr><h2 id="5-新建-Update-页面"><a href="#5-新建-Update-页面" class="headerlink" title="5.新建 Update 页面"></a>5.新建 Update 页面</h2><p>使用 <code>Hexo new page update</code> 命令新建页面，并在 <code>Front-matter</code> 中添加 <code>type: update</code>。</p><hr><h2 id="6-添加日志信息"><a href="#6-添加日志信息" class="headerlink" title="6.添加日志信息"></a>6.添加日志信息</h2><p>此时已经完成了 <strong>本站更新日志页面</strong> 的创建，<strong>如果需要添加内容，只需在 update 文件目录下的 index.md</strong> 中添加即可，页面支持 Markdown 格式，同时也支持 <code>anzhiyu</code> 主题下的外挂标签。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 本站更新日志</span><br><span class="line">date: 2025-01-08 00:00:00</span><br><span class="line">type: update</span><br><span class="line">comments: false</span><br><span class="line">aside: false</span><br><span class="line"><span class="section">top<span class="emphasis">_img: false</span></span></span><br><span class="line"><span class="emphasis"><span class="section">---</span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section">&#123;% tip home %&#125;本站日记是后面建的，前面有些时间对不上可能&#123;% endtip %&#125;</span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section">&#123;% timeline 2025 %&#125;</span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section">&lt;!-- timeline 01-08 --&gt;</span></span></span><br><span class="line"><span class="emphasis"><span class="section">添加本站更新日志页面</span></span></span><br><span class="line"><span class="emphasis"><span class="section">&lt;!-- endtimeline --&gt;</span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section">&lt;!-- timeline 01-07 --&gt;</span></span></span><br><span class="line"><span class="emphasis"><span class="section">将本站由 GitHub Pages 迁移至 Cloudflare Pages</span></span></span><br><span class="line"><span class="emphasis"><span class="section">&lt;!-- endtimeline --&gt;</span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section">&#123;% endtimeline %&#125;</span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section">&#123;% timeline 2024 %&#125;</span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section">&lt;!-- timeline 12-17 --&gt;</span></span></span><br><span class="line"><span class="emphasis"><span class="section">使用 Hexo 及安知鱼主题，在 GitHub Pages 搭建本站</span></span></span><br><span class="line"><span class="emphasis"><span class="section">&lt;!-- endtimeline --&gt;</span></span></span><br><span class="line"><span class="emphasis"><span class="section"></span></span></span><br><span class="line"><span class="emphasis"><span class="section">&#123;% endtimeline %&#125;</span></span></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;内容转载自&lt;a href=&quot;https://blog.cent1pedee.top/posts/bda0f2db.html#%E6%A0%B7%E5%BC%8F%E9%A2%84%E8%A7%88&quot;&gt;煮雪话河山&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;最近在迁移博客时，发现当时对博客的很多配</summary>
      
    
    
    
    <category term="博客建站" scheme="https://cq230.github.io/categories/%E5%8D%9A%E5%AE%A2%E5%BB%BA%E7%AB%99/"/>
    
    
    <category term="建站日志" scheme="https://cq230.github.io/tags/%E5%BB%BA%E7%AB%99%E6%97%A5%E5%BF%97/"/>
    
    <category term="Hexo" scheme="https://cq230.github.io/tags/Hexo/"/>
    
    <category term="安知鱼主题" scheme="https://cq230.github.io/tags/%E5%AE%89%E7%9F%A5%E9%B1%BC%E4%B8%BB%E9%A2%98/"/>
    
  </entry>
  
  <entry>
    <title>API整理</title>
    <link href="https://cq230.github.io/posts/3989239e.html"/>
    <id>https://cq230.github.io/posts/3989239e.html</id>
    <published>2024-12-05T12:00:44.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h3 id="图片类"><a href="#图片类" class="headerlink" title="图片类"></a>图片类</h3><h4 id="必应今日一图API"><a href="#必应今日一图API" class="headerlink" title="必应今日一图API:"></a>必应今日一图API:</h4><p><a href="https://bing.ee123.net/img/4k">https://bing.ee123.net/img/4k</a></p><h4 id="必应随机图API"><a href="#必应随机图API" class="headerlink" title="必应随机图API:"></a>必应随机图API:</h4><p><a href="https://bing.ee123.net/img/rand">https://bing.ee123.net/img/rand</a></p><h3 id="来源："><a href="#来源：" class="headerlink" title="来源："></a>来源：</h3><p><a href="https://bing.ee123.net/">必应壁纸 - 每日一图，带您领略世界之美</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;图片类&quot;&gt;&lt;a href=&quot;#图片类&quot; class=&quot;headerlink&quot; title=&quot;图片类&quot;&gt;&lt;/a&gt;图片类&lt;/h3&gt;&lt;h4 id=&quot;必应今日一图API&quot;&gt;&lt;a href=&quot;#必应今日一图API&quot; class=&quot;headerlink&quot; title=&quot;必应今</summary>
      
    
    
    
    <category term="资源整理" scheme="https://cq230.github.io/categories/%E8%B5%84%E6%BA%90%E6%95%B4%E7%90%86/"/>
    
    
    <category term="工具" scheme="https://cq230.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
    
    <category term="API" scheme="https://cq230.github.io/tags/API/"/>
    
    <category term="资源整理" scheme="https://cq230.github.io/tags/%E8%B5%84%E6%BA%90%E6%95%B4%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>爬取B站视频弹幕并分析</title>
    <link href="https://cq230.github.io/posts/6451bf19.html"/>
    <id>https://cq230.github.io/posts/6451bf19.html</id>
    <published>2024-11-18T11:54:34.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>最近看到李子柒回归的消息，借此机会练习从B站爬取弹幕并进行数据分析，本篇第一部分介绍数据爬取部分，第二部分介绍使用 python 对爬取的数据进行可视化分析。</p><h1 id="一、数据爬取"><a href="#一、数据爬取" class="headerlink" title="一、数据爬取"></a>一、数据爬取</h1><p>首先准备好要爬取的<a href="https://www.bilibili.com/video/BV14UUAYmExC/?spm_id_from=333.999.0.0&vd_source=e199bda1dc31de08316e4d8dd14e605e">视频链接</a>，浏览器打开链接进 F12 观察请求接口</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731934091240_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731934091240_image.png"></p><p>并没有发现有明文返回弹幕信息的请求接口，继续点开视频右上角的弹幕列表，观察发送的请求接口</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731936184646_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731936184646_image.png"></p><p>这里发现一共发送了三次请求，观察到三次请求的 &amp;segment_index 参数分别为 1，2，3，&amp;w_rid 也全部都不相同</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731936990359_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731936990359_image.png"></p><p>将这三个接口分别复制到 Apifox，分别测试三个接口都返回了弹幕文件，但是返回文件显示格式似乎出现错误，同时在对比观察时发现第一个接口比后两个接口多了三个参数</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731938063068_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731938063068_image.png"></p><p>尝试在第二个接口中加上这三个参数并再次发送请求发现返回 403 状态码，应该是对接口做了反爬处理</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731938284094_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731938284094_image.png"></p><p>对接口有了大致了解，接下来就是编写 python 代码的时间，因为三个接口的参数有区别，这里直接把三次请求的参数放入列表中进行循环请求，同时打印响应内容发现还是乱码，尝试把响应数据写入文件并设置为 utf-8 编码格式，用 VSCode 打开发现依旧是乱码，发送请求的部分代码如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">cookies = &#123;...&#125;</span><br><span class="line">headers = &#123;...&#125;</span><br><span class="line">params = [&#123;...&#125;,&#123;...&#125;,&#123;...&#125;]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(params)):</span><br><span class="line">        resp = requests.get(<span class="string">&#x27;https://api.bilibili.com/x/v2/dm/wbi/web/seg.so&#x27;</span>, </span><br><span class="line">                            params=params[i], </span><br><span class="line">                            cookies=cookies,</span><br><span class="line">                            headers=headers)                     </span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;HTTP 状态码: <span class="subst">&#123;resp.status_code&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;响应内容: <span class="subst">&#123;resp.text&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p>带着疑问我在 GitHub 找到了 <a href="https://github.com/SocialSisterYi/bilibili-API-collect">哔哩哔哩-API收集整理</a> 这个项目，了解到原来返回的文件乱码是因为B站弹幕接口数据传输格式改为了 <a href="https://protobuf.dev/">protobuf</a> ，这个格式为二进制编码传输，需要额外的编译器进行解析，相关教程在这里可以找到 <a href="https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/danmaku_proto.md">链接1</a>，<a href="https://blog.csdn.net/lyshark_lyshark/article/details/125848570">链接2</a> ，部分代码如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">my_seg = dm_pb2.DmSegMobileReply()</span><br><span class="line">my_seg.ParseFromString(resp.content)</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&#x27;danmu.csv&#x27;</span>, <span class="string">&#x27;a&#x27;</span>, newline=<span class="string">&#x27;&#x27;</span>, encoding=<span class="string">&#x27;utf-8-sig&#x27;</span>, ) <span class="keyword">as</span> f:</span><br><span class="line">        w = csv.writer(f)</span><br><span class="line">        w.writerow(</span><br><span class="line">            [<span class="string">&#x27;弹幕id&#x27;</span>, <span class="string">&#x27;视频内弹幕出现时间&#x27;</span>, <span class="string">&#x27;弹幕类型&#x27;</span>, <span class="string">&#x27;弹幕字号&#x27;</span>, <span class="string">&#x27;弹幕颜色&#x27;</span>, </span><br><span class="line">            <span class="string">&#x27;发送者mid的HASH&#x27;</span>, <span class="string">&#x27;弹幕内容&#x27;</span>, <span class="string">&#x27;弹幕发送时间&#x27;</span>, <span class="string">&#x27;弹幕dmid&#x27;</span>, <span class="string">&#x27;弹幕属性位&#x27;</span>])</span><br><span class="line">        <span class="keyword">for</span> elem <span class="keyword">in</span> my_seg.elems:</span><br><span class="line">            danmu_id = elem.<span class="built_in">id</span></span><br><span class="line">            progress = elem.progress</span><br><span class="line">            mode = elem.mode</span><br><span class="line">            fontsize = elem.fontsize</span><br><span class="line">            color = elem.color</span><br><span class="line">            mid_hash = elem.midHash</span><br><span class="line">            content = elem.content</span><br><span class="line">            ctime = elem.ctime</span><br><span class="line">            id_str = elem.idStr</span><br><span class="line">            attr = elem.attr</span><br><span class="line"></span><br><span class="line">            w.writerow(</span><br><span class="line">                [<span class="string">f&#x27;&quot;<span class="subst">&#123;danmu_id&#125;</span>&quot;&#x27;</span>, progress, mode, fontsize, color, mid_hash, </span><br><span class="line">                content, ctime, <span class="string">f&#x27;&quot;<span class="subst">&#123;id_str&#125;</span>&quot;&#x27;</span>,attr])</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;第<span class="subst">&#123;i + <span class="number">1</span>&#125;</span>份--弹幕数据已写入到 danmu.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 休眠</span></span><br><span class="line">    time.sleep(<span class="number">3</span>)</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;Exception-&#x27;</span> + <span class="built_in">str</span>(e))</span><br></pre></td></tr></table></figure><p>ps:这里有个小坑，在写入 csv 文件时把编码格式设置为 utf-8 用 Excel 打开会乱码，需要设置为 utf-8-sig</p><p>返回数据字段信息如下所示</p><table><thead><tr><th align="center">名称</th><th align="center">类型</th><th align="center">含义</th><th align="center">备注</th></tr></thead><tbody><tr><td align="center">id</td><td align="center">int64</td><td align="center">弹幕 dmid</td><td align="center">唯一 可用于操作参数</td></tr><tr><td align="center">progress</td><td align="center">int32</td><td align="center">视频内弹幕出现时间</td><td align="center">毫秒</td></tr><tr><td align="center">mode</td><td align="center">int32</td><td align="center">弹幕类型</td><td align="center">123：普通弹幕</td></tr><tr><td align="center">4：底部弹幕</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">5：顶部弹幕</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">6：逆向弹幕</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">7：高级弹幕</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">8：代码弹幕</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">9：BAS 弹幕（仅限于特殊弹幕专包）</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">fontsize</td><td align="center">int32</td><td align="center">弹幕字号</td><td align="center">18：小</td></tr><tr><td align="center">25：标准</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">36：大</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">color</td><td align="center">uint32</td><td align="center">弹幕颜色</td><td align="center">十进制 RGB888 值</td></tr><tr><td align="center">midHash</td><td align="center">string</td><td align="center">发送者 mid 的 HASH</td><td align="center">用于屏蔽用户和查看用户发送的所有弹幕，也可反查用户id</td></tr><tr><td align="center">content</td><td align="center">string</td><td align="center">弹幕内容</td><td align="center">utf-8编码</td></tr><tr><td align="center">ctime</td><td align="center">int64</td><td align="center">弹幕发送时间</td><td align="center">时间戳</td></tr><tr><td align="center">weight</td><td align="center">int32</td><td align="center">权重</td><td align="center">用于智能屏蔽，根据弹幕语义及长度通过AI识别得出范围：[0-10]值越大权重越高</td></tr><tr><td align="center">action</td><td align="center">string</td><td align="center">动作？</td><td align="center"></td></tr><tr><td align="center">pool</td><td align="center">int32</td><td align="center">弹幕池</td><td align="center">0：普通池</td></tr><tr><td align="center">1：字幕池</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">2：特殊池（代码&#x2F;BAS弹幕）</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">idStr</td><td align="center">string</td><td align="center">弹幕 dmid</td><td align="center">字串形式唯一 可用于操作参数</td></tr><tr><td align="center">attr</td><td align="center">int32</td><td align="center">弹幕属性位</td><td align="center">bit0：保护</td></tr><tr><td align="center">bit1：直播</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">bit2：高赞</td><td align="center"></td><td align="center"></td><td align="center"></td></tr><tr><td align="center">animation</td><td align="center">string</td><td align="center">动画？</td><td align="center"></td></tr></tbody></table><p>最终成功将爬取到的数据写入 csv 文件，共计27602条数据</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731944589874_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731944589874_image.png"></p><h1 id="二、数据分析-可视化"><a href="#二、数据分析-可视化" class="headerlink" title="二、数据分析&amp;可视化"></a>二、数据分析&amp;可视化</h1><h2 id="2-1数据预处理"><a href="#2-1数据预处理" class="headerlink" title="2.1数据预处理"></a>2.1数据预处理</h2><p>在拿到弹幕数据后，首先导入到 pandas 用 info 看一下这份数据的大致情况，可以看到一共有10项数据，每一项都没有缺失值</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line">df = pd.read_csv(<span class="string">&#x27;danmu.csv&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(df.info())</span><br></pre></td></tr></table></figure><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731957934097_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731957934097_image.png"></p><p>再用 describe 看一下弹幕内容的大概情况，可以看到一共有 27601 项不为空的弹幕内容，其中有 20076 项不重复的弹幕内容，“哇”这个词出现了 646 次，是频率最高的弹幕内容</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">print</span>(df[<span class="string">&#x27;弹幕内容&#x27;</span>].describe())</span><br></pre></td></tr></table></figure><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731958053331_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731958053331_image.png"></p><h2 id="2-2-生成弹幕词云图"><a href="#2-2-生成弹幕词云图" class="headerlink" title="2.2 生成弹幕词云图"></a>2.2 生成弹幕词云图</h2><p>生成词云图除了要用到 pandas 还有以下几个必要的库：</p><ul><li>wordcloud：用于生成词云图</li><li>matplotlib：用于展示词云图</li><li>jieba：中文分词库，用于处理中文文本</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">get_wordcloud</span>():</span><br><span class="line">    <span class="comment"># 提取弹幕内容</span></span><br><span class="line">    text_data = df[<span class="string">&#x27;弹幕内容&#x27;</span>].dropna()  <span class="comment"># 去除缺失值</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 对弹幕内容进行分词</span></span><br><span class="line">    text = <span class="string">&#x27; &#x27;</span>.join(text_data.apply(<span class="keyword">lambda</span> x: <span class="string">&#x27; &#x27;</span>.join(jieba.cut(x))))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建词云</span></span><br><span class="line">    wordcloud = WordCloud(</span><br><span class="line">        font_path=<span class="string">&#x27;msyh.ttc&#x27;</span>,  <span class="comment"># 指定字体路径，确保能显示中文，msyh.ttc 是微软雅黑字体的路径</span></span><br><span class="line">        width=<span class="number">800</span>, height=<span class="number">600</span>,  <span class="comment"># 设置词云图的大小</span></span><br><span class="line">        background_color=<span class="string">&#x27;white&#x27;</span>,  <span class="comment"># 设置背景色</span></span><br><span class="line">        max_words=<span class="number">1000</span>,  <span class="comment"># 最大显示的词数</span></span><br><span class="line">        max_font_size=<span class="number">100</span>,  <span class="comment"># 最大字体大小</span></span><br><span class="line">    ).generate(text)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 显示词云图</span></span><br><span class="line">    plt.figure(figsize=(<span class="number">10</span>, <span class="number">8</span>))</span><br><span class="line">    plt.imshow(wordcloud)</span><br><span class="line">    plt.show()</span><br></pre></td></tr></table></figure><p>执行上述代码，得到以下词云图：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731959595150_PixPin_2024-11-19_03-52-05.png" alt="弹幕词云图"></p><p>分析：从该词云图中可以看出大量弹幕出现了“欢迎回来”，“生日快乐”，“回来了”，“太美了”等词眼，说明许多发送弹幕的观众都表达了对李子柒回归的欢迎和祝福，“马面裙”，“蜀锦”等词眼可能是在讨论视频中出现的传统文化相关的内容。</p><h2 id="2-3-分析弹幕-TOP10-内容"><a href="#2-3-分析弹幕-TOP10-内容" class="headerlink" title="2.3 分析弹幕 TOP10 内容"></a>2.3 分析弹幕 TOP10 内容</h2><p>在看到词云图后，我开始对发送数量前10的弹幕是什么感到好奇，于是准备开始通过代码生成弹幕数量排行前10的统计图。</p><p>首先统计弹幕出现次数需要用到 collections 中的 Counter，将弹幕内容转换为列表再传进Counter()就得到了所以弹幕出现次数的统计数据，截取前10的数据并作图就得到了弹幕数量排行前10的统计图。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> Counter</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_top_dm</span>():</span><br><span class="line">    df = pd.read_csv(<span class="string">&#x27;danmu.csv&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    danmaku_list = df[<span class="string">&#x27;弹幕内容&#x27;</span>].dropna().tolist()  <span class="comment"># 去除空值并转换为列表</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 统计弹幕出现次数</span></span><br><span class="line">    counter = Counter(danmaku_list)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 获取出现次数最多的前 10 条弹幕</span></span><br><span class="line">    top_10 = counter.most_common(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 绘制图表</span></span><br><span class="line">    labels, values = <span class="built_in">zip</span>(*top_10)  <span class="comment"># 解压为两个列表</span></span><br><span class="line">    plt.figure(figsize=(<span class="number">10</span>, <span class="number">6</span>))</span><br><span class="line">    bars = plt.barh(labels, values, color=<span class="string">&#x27;skyblue&#x27;</span>)</span><br><span class="line">    <span class="comment"># 在每个柱状条上标注具体数据值</span></span><br><span class="line">    <span class="keyword">for</span> bar, value <span class="keyword">in</span> <span class="built_in">zip</span>(bars, values):</span><br><span class="line">        plt.text(value + <span class="number">1</span>, bar.get_y() + bar.get_height() / <span class="number">2</span>, <span class="built_in">str</span>(value), va=<span class="string">&#x27;center&#x27;</span>, fontsize=<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">    plt.xlabel(<span class="string">&#x27;出现次数&#x27;</span>)</span><br><span class="line">    plt.ylabel(<span class="string">&#x27;弹幕内容&#x27;</span>)</span><br><span class="line">    plt.title(<span class="string">&#x27;弹幕数量排行前10&#x27;</span>)</span><br><span class="line">    plt.gca().invert_yaxis()  <span class="comment"># 翻转 y 轴，最多的弹幕排在最上面</span></span><br><span class="line">    plt.tight_layout()</span><br><span class="line">    plt.show()</span><br></pre></td></tr></table></figure><p>执行以上代码，得到以下统计图：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1732189963117_myplot-top10-dm.png" alt="弹幕数量 TOP10"></p><p>分析：可以看到发送数量最多的弹幕内容是“哇”,共发送了 646 次，”欢迎回来”和“欢迎回来！”共发送了 805 次，“生日快乐”、“生日快乐！！！”、“生日快乐！”共发送了 604 次，剩下的热度比较高的词也是以夸赞性的词为主，和词云图看到的信息大致一致，弹幕主要表达了观众对李子柒回归的欢迎和生日的祝福。</p><h2 id="2-4-弹幕内容情感分析"><a href="#2-4-弹幕内容情感分析" class="headerlink" title="2.4 弹幕内容情感分析"></a>2.4 弹幕内容情感分析</h2><p>在看到发送的弹幕数量前10都是对李子柒这期视频的正面评价，我有了一个新的问题，绝大部分弹幕都是对李子柒的夸赞吗？为此，我决定对弹幕内容进行情感分析。</p><p>首先选择 Snownlp 这个库进行情感分析，因为它专门针对中文文本优化，尤其在处理分词和情感分析时表现较好，所以更适合用来进行中文情感分析。使用 SnowNLP 的 sentiment 方法对文本进行情感分析，该方法返回一个 0-1 之间的情感极性值，值越接近 <strong>1</strong> 表示情感越<strong>积极</strong>，值越接近 <strong>0</strong> 表示情感越<strong>消极</strong>。这里我自定义了阈值大于 0.6 为正面情绪，小于 0.4 为负面情绪，介于之间的为中性情绪。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> snownlp <span class="keyword">import</span> SnowNLP</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sentiment_analysis</span>():</span><br><span class="line">    df = pd.read_csv(<span class="string">&#x27;danmu.csv&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    danmaku_list = df[<span class="string">&#x27;弹幕内容&#x27;</span>].dropna().tolist()  <span class="comment"># 去除空值并转换为列表</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 1. 定义情感分析函数</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">analyze_sentiment</span>(<span class="params">text</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        使用 SnowNLP 进行情感分析</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        s = SnowNLP(text)</span><br><span class="line">        score = s.sentiments  <span class="comment"># 得分范围 0 ~ 1，接近 1 为正面，接近 0 为负面</span></span><br><span class="line">        <span class="keyword">if</span> score &gt; <span class="number">0.6</span>:  <span class="comment"># 自定义正面情感阈值</span></span><br><span class="line">            <span class="keyword">return</span> <span class="string">&#x27;积极&#x27;</span></span><br><span class="line">        <span class="keyword">elif</span> score &lt; <span class="number">0.4</span>:  <span class="comment"># 自定义负面情感阈值</span></span><br><span class="line">            <span class="keyword">return</span> <span class="string">&#x27;消极&#x27;</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&#x27;中立&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 2. 对弹幕内容进行情感分类</span></span><br><span class="line">    sentiment_results = [analyze_sentiment(danmaku) <span class="keyword">for</span> danmaku <span class="keyword">in</span> danmaku_list]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 将结果添加到原始数据</span></span><br><span class="line">    df[<span class="string">&#x27;Sentiment&#x27;</span>] = sentiment_results</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 3. 统计情感类别的比例</span></span><br><span class="line">    sentiment_counts = df[<span class="string">&#x27;Sentiment&#x27;</span>].value_counts()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 4. 绘制饼图展示情感分布</span></span><br><span class="line">    plt.figure(figsize=(<span class="number">8</span>, <span class="number">6</span>))</span><br><span class="line">    colors = [<span class="string">&#x27;#57A0D3&#x27;</span>, <span class="string">&#x27;#A0D357&#x27;</span>, <span class="string">&#x27;#D35757&#x27;</span>]  <span class="comment"># 为正面、中性、负面情感设置颜色</span></span><br><span class="line">    sentiment_counts.plot.pie(autopct=<span class="string">&#x27;%1.1f%%&#x27;</span>, colors=colors, startangle=<span class="number">140</span>, textprops=&#123;<span class="string">&#x27;fontsize&#x27;</span>: <span class="number">12</span>&#125;)</span><br><span class="line">    plt.title(<span class="string">&#x27;弹幕情感分析结果&#x27;</span>)</span><br><span class="line">    plt.ylabel(<span class="string">&#x27;&#x27;</span>)  <span class="comment"># 隐藏 y 轴标签</span></span><br><span class="line">    plt.tight_layout()</span><br><span class="line">    plt.show()</span><br></pre></td></tr></table></figure><p>执行以上代码，得到以下情感分析图：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1732192639745_%E5%BC%B9%E5%B9%95%E6%83%85%E6%84%9F%E5%88%86%E6%9E%90.png" alt="弹幕情感分析图"></p><p>分析：可以看到发送的弹幕中最多的是正面情绪的弹幕，占比达到了 55.9%，中立弹幕占到了 26.9%，最少的是负面情绪的弹幕，占到了17.2%，由此可见大部分弹幕都是积极的情绪，只有较少占比的弹幕是比较消极的。</p><h2 id="2-5-分析发送弹幕数量最多的用户"><a href="#2-5-分析发送弹幕数量最多的用户" class="headerlink" title="2.5 分析发送弹幕数量最多的用户"></a>2.5 分析发送弹幕数量最多的用户</h2><p>在做完以上分析，我突发奇想，观看量这么高的视频应该有不少人发了不止一条弹幕，那么在这个视频里发送弹幕最多的用户到底发了多少条弹幕呢？好在从B站弹幕接口里拿到的数据中的“发送者mid的HASH”是对用户id进行了hash计算后的结果，保证了用户信息的安全，所以在这里可以直接用“发送者mid的HASH”这个字段进行统计展示。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">send_dm_user_top</span>():</span><br><span class="line">    df = pd.read_csv(<span class="string">&#x27;danmu.csv&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 1. 统计每个用户发送弹幕的数量</span></span><br><span class="line">    user_danmaku_count = df[<span class="string">&#x27;发送者mid的HASH&#x27;</span>].value_counts()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 2. 获取弹幕数量排名前 10 的用户</span></span><br><span class="line">    top_users = user_danmaku_count.head(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 3. 绘制柱状图展示用户弹幕数量排行</span></span><br><span class="line">    plt.figure(figsize=(<span class="number">10</span>, <span class="number">6</span>))</span><br><span class="line">    bars = top_users.plot(kind=<span class="string">&#x27;bar&#x27;</span>, color=<span class="string">&#x27;#57A0D3&#x27;</span>, edgecolor=<span class="string">&#x27;black&#x27;</span>)</span><br><span class="line">    plt.title(<span class="string">&#x27;弹幕数量最多的用户排行榜&#x27;</span>, fontsize=<span class="number">16</span>)</span><br><span class="line">    plt.xlabel(<span class="string">&#x27;用户 ID（已加密）&#x27;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line">    plt.ylabel(<span class="string">&#x27;弹幕数量&#x27;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line">    plt.xticks(rotation=<span class="number">45</span>, fontsize=<span class="number">12</span>)</span><br><span class="line">    <span class="comment"># 4. 在每个柱状图上显示具体的弹幕数量</span></span><br><span class="line">    <span class="keyword">for</span> bar <span class="keyword">in</span> bars.patches:  <span class="comment"># 遍历所有柱子</span></span><br><span class="line">        height = bar.get_height()  <span class="comment"># 获取柱子的高度（弹幕数量）</span></span><br><span class="line">        plt.text(bar.get_x() + bar.get_width() / <span class="number">2</span>, height,  <span class="comment"># 设置文本位置</span></span><br><span class="line">                 <span class="string">f&#x27;<span class="subst">&#123;<span class="built_in">int</span>(height)&#125;</span>&#x27;</span>,  <span class="comment"># 显示数据</span></span><br><span class="line">                 ha=<span class="string">&#x27;center&#x27;</span>, va=<span class="string">&#x27;bottom&#x27;</span>, fontsize=<span class="number">12</span>)  <span class="comment"># 字体样式</span></span><br><span class="line">    plt.tight_layout()</span><br><span class="line">    plt.show()</span><br></pre></td></tr></table></figure><p>执行以上代码，得到以下 TOP10 分析图：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1732194988164_dm-user_top.png" alt="发送弹幕最多的用户TOP10"></p><p>分析：可以看到发送弹幕数量最多的用户发送了27条弹幕，第二名紧跟其后发了26条弹幕，第8、9、10名都发送了13条弹幕，看来还是有一部分观众对这个视频发言很积极的，同一个视频发送了10条以上的弹幕。</p><h2 id="2-6-分析用户弹幕数量分布情况"><a href="#2-6-分析用户弹幕数量分布情况" class="headerlink" title="2.6 分析用户弹幕数量分布情况"></a>2.6 分析用户弹幕数量分布情况</h2><p>在看完了发送弹幕数量最多的前10名用户，那么整体用户发送的弹幕数量情况是怎样的，大部分用户都发送了几条弹幕呢？为了得到答案，我决定用直方图来分析用户发弹幕的数量分布情况。这里使用“发送者mid的HASH”来统计每个用户发送了多少条弹幕，将取到的值传入到<code>plt.hist()</code>生成直方图。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">dm_distribution</span>():</span><br><span class="line">    df = pd.read_csv(<span class="string">&#x27;danmu.csv&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 获取用户的弹幕数量统计</span></span><br><span class="line">    user_danmaku_count = df[<span class="string">&#x27;发送者mid的HASH&#x27;</span>].value_counts()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 绘制直方图</span></span><br><span class="line">    plt.figure(figsize=(<span class="number">12</span>, <span class="number">6</span>))</span><br><span class="line">    counts, bins, patches = plt.hist(user_danmaku_count, bins=<span class="number">30</span>, color=<span class="string">&#x27;#57A0D3&#x27;</span>, edgecolor=<span class="string">&#x27;black&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 添加标题和标签</span></span><br><span class="line">    plt.title(<span class="string">&#x27;用户发送弹幕数量分布情况&#x27;</span>, fontsize=<span class="number">16</span>)</span><br><span class="line">    plt.xlabel(<span class="string">&#x27;每个用户的弹幕数量&#x27;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line">    plt.ylabel(<span class="string">&#x27;用户数量&#x27;</span>, fontsize=<span class="number">14</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 在每个柱子上显示数据</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(patches)):</span><br><span class="line">        height = counts[i]  <span class="comment"># 每个柱子的高度</span></span><br><span class="line">        <span class="keyword">if</span> height &gt; <span class="number">0</span>:  <span class="comment"># 仅显示大于0的数据</span></span><br><span class="line">            plt.text(patches[i].get_x() + patches[i].get_width() / <span class="number">2</span>, height, <span class="built_in">int</span>(height),</span><br><span class="line">                     ha=<span class="string">&#x27;center&#x27;</span>, va=<span class="string">&#x27;bottom&#x27;</span>, fontsize=<span class="number">10</span>, color=<span class="string">&#x27;black&#x27;</span>)</span><br><span class="line">    plt.tight_layout()</span><br><span class="line">    plt.show()</span><br></pre></td></tr></table></figure><p>执行以上代码，得到以下分析图：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1732276812238_dm-distribution.png" alt="用户发送弹幕数量分布图"></p><p>分析：可以看到绝大多数的观众在观看此视频时都只发了1条弹幕，小部分观众发了2条以上，其中有极少一部分观众发送了超过5条弹幕，发送弹幕最多的观众一共发了27条。这份分布图还是比较符合我的预期的，果然能坚持发多条弹幕的真爱粉还是少数。</p><h2 id="2-7-分析弹幕数量随日期变化情况"><a href="#2-7-分析弹幕数量随日期变化情况" class="headerlink" title="2.7 分析弹幕数量随日期变化情况"></a>2.7 分析弹幕数量随日期变化情况</h2><p>“弹幕发送时间”这个字段是字符串类型，需要先转换为 int 类型，再转换为 datetime 类型。</p><p>数据格式准备好后，接下来就是画图的部分，这里需要用到 matplotlib 这个库，统计每天有多少条弹幕需要用到 pandas 库中的 resample()，这里<code>df.resample(&#39;1d&#39;, on=&#39;弹幕发送时间&#39;).size()</code>将 DataFrame 按<strong>天</strong>（<code>&#39;1d&#39;</code>）重采样，<code>on=&#39;弹幕发送时间&#39;</code>指定了用’弹幕发送时间’这一列进行时间的分组，<code>size()</code>会返回每个分组的大小，即每一天的弹幕数量。因此，这行代码会返回一个按天索引的 Series，其中每个索引（日期）对应的值就是那一天的弹幕数量。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">get_dm_num_by_date_time</span>():</span><br><span class="line">    df = pd.read_csv(<span class="string">&#x27;danmu.csv&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 清理列名和数据</span></span><br><span class="line">    df.columns = df.columns.<span class="built_in">str</span>.strip()  <span class="comment"># 清理列名中的多余空格</span></span><br><span class="line">    df[<span class="string">&#x27;弹幕发送时间&#x27;</span>] = df[<span class="string">&#x27;弹幕发送时间&#x27;</span>].<span class="built_in">str</span>.strip(<span class="string">&#x27;&quot;&#x27;</span>)  <span class="comment"># 移除时间字段中的引号</span></span><br><span class="line">    df[<span class="string">&#x27;弹幕发送时间&#x27;</span>] = pd.to_numeric(df[<span class="string">&#x27;弹幕发送时间&#x27;</span>], errors=<span class="string">&#x27;coerce&#x27;</span>)  <span class="comment"># 转换为数值</span></span><br><span class="line">    df = df.dropna(subset=[<span class="string">&#x27;弹幕发送时间&#x27;</span>])  <span class="comment"># 删除无效时间值的行</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 转换时间字段为datetime格式</span></span><br><span class="line">    df[<span class="string">&#x27;弹幕发送时间&#x27;</span>] = pd.to_datetime(df[<span class="string">&#x27;弹幕发送时间&#x27;</span>], unit=<span class="string">&#x27;s&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 按天分组统计弹幕数量</span></span><br><span class="line">    danmu_per_minute = df.resample(<span class="string">&#x27;1d&#x27;</span>, on=<span class="string">&#x27;弹幕发送时间&#x27;</span>).size()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 绘制折线图</span></span><br><span class="line">    plt.figure(figsize=(<span class="number">12</span>, <span class="number">6</span>))</span><br><span class="line">    plt.plot(danmu_per_minute.index, danmu_per_minute.values, marker=<span class="string">&#x27;o&#x27;</span>, linestyle=<span class="string">&#x27;-&#x27;</span>, alpha=<span class="number">0.7</span>)</span><br><span class="line">    plt.title(<span class="string">&#x27;弹幕数量随日期变化折线图&#x27;</span>)</span><br><span class="line">    plt.xlabel(<span class="string">&#x27;时间&#x27;</span>)</span><br><span class="line">    plt.ylabel(<span class="string">&#x27;弹幕数量&#x27;</span>)</span><br><span class="line">    plt.grid(<span class="literal">True</span>)</span><br><span class="line">    plt.tight_layout()</span><br><span class="line">    plt.show()</span><br></pre></td></tr></table></figure><p>执行代码，得到以下折线图：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1732182712565_myplot-dm-num-by-date.png" alt="弹幕数量随日期变化图"></p><p>分析：从图中可以看出发送弹幕数量最多的一天是2024年11月13日，也就是视频发布当天，弹幕数量最高超过 14000 条，随后每天弹幕数量都在持续下降，到2024年11月18日这天发送弹幕数量已经在 1000 条左右。看来观众对李子柒回归后这期视频的围观和讨论主要集中在发布视频当天，随后开始持续下降。</p><h1 id="参考链接："><a href="#参考链接：" class="headerlink" title="参考链接："></a>参考链接：</h1><p><a href="https://github.com/SocialSisterYi/bilibili-API-collect/tree/master">GitHub - SocialSisterYi&#x2F;bilibili-API-collect: 哔哩哔哩-API收集整理【不断更新中….】</a></p><p><a href="https://blog.csdn.net/lyshark_lyshark/article/details/125848570">b站 实时弹幕和历史弹幕 Protobuf 格式解析-CSDN博客</a></p><p><a href="https://protobuf.dev/">Protocol Buffers</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近看到李子柒回归的消息，借此机会练习从B站爬取弹幕并进行数据分析，本篇第一部分介绍数据爬取部分，第二部分介绍使用 python 对爬取的数据进行可视化分析。&lt;/p&gt;
&lt;h1 id=&quot;一、数据爬取&quot;&gt;&lt;a href=&quot;#一、数据爬取&quot; class=&quot;headerlink&quot; </summary>
      
    
    
    
    <category term="实战开发" scheme="https://cq230.github.io/categories/%E5%AE%9E%E6%88%98%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Python" scheme="https://cq230.github.io/tags/Python/"/>
    
    <category term="数据分析" scheme="https://cq230.github.io/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/"/>
    
    <category term="爬虫" scheme="https://cq230.github.io/tags/%E7%88%AC%E8%99%AB/"/>
    
  </entry>
  
  <entry>
    <title>Tableau使用技巧</title>
    <link href="https://cq230.github.io/posts/201c4457.html"/>
    <id>https://cq230.github.io/posts/201c4457.html</id>
    <published>2024-11-16T11:51:00.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>在 Tableau 中数据有两种类型：</p><h2 id="维度"><a href="#维度" class="headerlink" title="维度"></a>维度</h2><ul><li><strong>维度</strong> 是用于描述数据的分类或属性的字段，通常是 <strong>定性</strong> 或 <strong>离散的</strong> 信息。它们帮助你 <strong>分组</strong> 和 <strong>切片</strong> 数据。</li><li>维度字段通常包含文字、日期、地理信息等。</li></ul><h2 id="度量"><a href="#度量" class="headerlink" title="度量"></a>度量</h2><ul><li><strong>度量</strong> 是表示 <strong>数值</strong> 的字段，通常是 <strong>定量</strong> 或 <strong>连续的</strong> 数据。度量字段通常是用来度量事物的 <strong>数量</strong> 或 <strong>大小</strong>，比如销售额、利润、数量等。</li><li>度量字段的值通常可以进行 <strong>计算</strong>（加总、平均、最大、最小等），因此它们通常表现为数值型数据。</li></ul><h3 id="维度与度量的关键区别"><a href="#维度与度量的关键区别" class="headerlink" title="维度与度量的关键区别"></a><strong>维度与度量的关键区别</strong></h3><table><thead><tr><th align="center"><strong>特性</strong></th><th align="center"><strong>维度</strong></th><th align="center"><strong>度量</strong></th></tr></thead><tbody><tr><td align="center"><strong>数据类型</strong></td><td align="center">通常是<strong>离散的</strong>，如文本、日期、地理信息等。</td><td align="center">通常是<strong>连续的</strong>，如数值型数据（例如销售额、数量）。</td></tr><tr><td align="center"><strong>示例</strong></td><td align="center">国家、产品类别、城市、日期、顾客 ID</td><td align="center">销售额、利润、数量、成本、收入等数值型数据。</td></tr><tr><td align="center"><strong>作用</strong></td><td align="center">用于<strong>分组</strong>、<strong>筛选</strong>、<strong>划分维度</strong>，如 X 轴和 Y 轴上的标签。</td><td align="center">用于<strong>量化</strong>、<strong>汇总</strong>数据（例如求和、平均等），如柱状图的高度。</td></tr><tr><td align="center"><strong>聚合操作</strong></td><td align="center">不会被聚合，直接表现为分类信息。</td><td align="center">可被聚合（如求和、求平均值、计数等），展示为数值。</td></tr></tbody></table><h2 id="Tableau-可视化原理"><a href="#Tableau-可视化原理" class="headerlink" title="Tableau 可视化原理"></a>Tableau 可视化原理</h2><ol><li><p>Tableau 第一概念：对 <strong>度量</strong> 和 <strong>维度</strong> 进行拖拽操作，从而完成可视化图表的制作</p><p><strong>行和列的基本映射</strong></p><ul><li><strong>列（Columns）</strong>：控制图表的 <strong>X 轴</strong>，即横向的方向。拖拽到“列”区域的字段通常会形成图表中的 <strong>X 轴</strong>，决定了数据的横向分布。</li><li><strong>行（Rows）</strong>：控制图表的 <strong>Y 轴</strong>，即纵向的方向。拖拽到“行”区域的字段通常会形成图表中的 <strong>Y 轴</strong>，决定了数据的纵向分布。</li></ul></li><li><p>Tableau 第二概念：维度会对度量值进行区分，增加度量值的信息密度（单个图表传达信息的多少)</p></li><li><p>Tableau 第三概念：图表分为有轴图表和无轴图表 (极坐标图表)</p></li><li><p>Tableau 第四概念：离散形成标签，连续形成数轴</p></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在 Tableau 中数据有两种类型：&lt;/p&gt;
&lt;h2 id=&quot;维度&quot;&gt;&lt;a href=&quot;#维度&quot; class=&quot;headerlink&quot; title=&quot;维度&quot;&gt;&lt;/a&gt;维度&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;维度&lt;/strong&gt; 是用于描述数据的分类或属性的字段，</summary>
      
    
    
    
    <category term="实用技巧" scheme="https://cq230.github.io/categories/%E5%AE%9E%E7%94%A8%E6%8A%80%E5%B7%A7/"/>
    
    
    <category term="工具" scheme="https://cq230.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
    
    <category term="Tableau" scheme="https://cq230.github.io/tags/Tableau/"/>
    
  </entry>
  
  <entry>
    <title>MySQL</title>
    <link href="https://cq230.github.io/posts/c24675b4.html"/>
    <id>https://cq230.github.io/posts/c24675b4.html</id>
    <published>2024-11-15T16:48:24.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h1 id="基础篇"><a href="#基础篇" class="headerlink" title="基础篇"></a>基础篇</h1><h2 id="通用语法及分类"><a href="#通用语法及分类" class="headerlink" title="通用语法及分类"></a>通用语法及分类</h2><ul><li>DDL: 数据定义语言，用来定义数据库对象（数据库、表、字段）</li><li>DML: 数据操作语言，用来对数据库表中的数据进行增删改</li><li>DQL: 数据查询语言，用来查询数据库中表的记录</li><li>DCL: 数据控制语言，用来创建数据库用户、控制数据库的控制权限</li></ul><h3 id="DDL（数据定义语言）"><a href="#DDL（数据定义语言）" class="headerlink" title="DDL（数据定义语言）"></a>DDL（数据定义语言）</h3><p>数据定义语言</p><h4 id="数据库操作"><a href="#数据库操作" class="headerlink" title="数据库操作"></a>数据库操作</h4><p>查询所有数据库：<br><code>SHOW DATABASES;</code><br>查询当前数据库：<br><code>SELECT DATABASE();</code><br>创建数据库：<br><code>CREATE DATABASE [ IF NOT EXISTS ] 数据库名 [ DEFAULT CHARSET 字符集] [COLLATE 排序规则 ];</code><br>删除数据库：<br><code>DROP DATABASE [ IF EXISTS ] 数据库名;</code><br>使用数据库：<br><code>USE 数据库名;</code></p><h5 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>UTF8字符集长度为3字节，有些符号占4字节，所以推荐用utf8mb4字符集</li></ul><h4 id="表操作"><a href="#表操作" class="headerlink" title="表操作"></a>表操作</h4><p>查询当前数据库所有表：<br><code>SHOW TABLES;</code><br>查询表结构：<br><code>DESC 表名;</code><br>查询指定表的建表语句：<br><code>SHOW CREATE TABLE 表名;</code></p><p>创建表：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE 表名(</span><br><span class="line">字段1 字段1类型 [COMMENT 字段1注释],</span><br><span class="line">字段2 字段2类型 [COMMENT 字段2注释],</span><br><span class="line">字段3 字段3类型 [COMMENT 字段3注释],</span><br><span class="line">...</span><br><span class="line">字段n 字段n类型 [COMMENT 字段n注释]</span><br><span class="line">)[ COMMENT 表注释 ];</span><br></pre></td></tr></table></figure><p><strong>最后一个字段后面没有逗号</strong></p><p>添加字段：<br><code>ALTER TABLE 表名 ADD 字段名 类型(长度) [COMMENT 注释] [约束];</code><br>例：<code>ALTER TABLE emp ADD nickname varchar(20) COMMENT &#39;昵称&#39;;</code></p><p>修改数据类型：<br><code>ALTER TABLE 表名 MODIFY 字段名 新数据类型(长度);</code><br>修改字段名和字段类型：<br><code>ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型(长度) [COMMENT 注释] [约束];</code><br>例：将emp表的nickname字段修改为username，类型为varchar(30)<br><code>ALTER TABLE emp CHANGE nickname username varchar(30) COMMENT &#39;昵称&#39;;</code></p><p>删除字段：<br><code>ALTER TABLE 表名 DROP 字段名;</code></p><p>修改表名：<br><code>ALTER TABLE 表名 RENAME TO 新表名</code></p><p>删除表：<br><code>DROP TABLE [IF EXISTS] 表名;</code><br>删除表，并重新创建该表：<br><code>TRUNCATE TABLE 表名;</code></p><h3 id="DML（数据操作语言）"><a href="#DML（数据操作语言）" class="headerlink" title="DML（数据操作语言）"></a>DML（数据操作语言）</h3><h4 id="添加数据"><a href="#添加数据" class="headerlink" title="添加数据"></a>添加数据</h4><p>指定字段：<br><code>INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...);</code><br>全部字段：<br><code>INSERT INTO 表名 VALUES (值1, 值2, ...);</code></p><p>批量添加数据：<br><code>INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);</code><br><code>INSERT INTO 表名 VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);</code></p><h5 id="注意事项-1"><a href="#注意事项-1" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>字符串和日期类型数据应该包含在引号中</li><li>插入的数据大小应该在字段的规定范围内</li></ul><h4 id="更新和删除数据"><a href="#更新和删除数据" class="headerlink" title="更新和删除数据"></a>更新和删除数据</h4><p>修改数据：<br><code>UPDATE 表名 SET 字段名1 = 值1, 字段名2 = 值2, ... [ WHERE 条件 ];</code><br>例：<br><code>UPDATE emp SET name = &#39;Jack&#39; WHERE id = 1;</code></p><p>删除数据：<br><code>DELETE FROM 表名 [ WHERE 条件 ];</code></p><h3 id="DQL（数据查询语言）"><a href="#DQL（数据查询语言）" class="headerlink" title="DQL（数据查询语言）"></a>DQL（数据查询语言）</h3><p>语法：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">SELECT</span><br><span class="line">字段列表</span><br><span class="line">FROM</span><br><span class="line">表名字段</span><br><span class="line">WHERE</span><br><span class="line">条件列表</span><br><span class="line">GROUP BY</span><br><span class="line">分组字段列表</span><br><span class="line">HAVING</span><br><span class="line">分组后的条件列表</span><br><span class="line">ORDER BY</span><br><span class="line">排序字段列表</span><br><span class="line">LIMIT</span><br><span class="line">分页参数</span><br></pre></td></tr></table></figure><h4 id="基础查询"><a href="#基础查询" class="headerlink" title="基础查询"></a>基础查询</h4><p>查询多个字段：<br><code>SELECT 字段1, 字段2, 字段3, ... FROM 表名;</code><br><code>SELECT * FROM 表名;</code></p><p>设置别名：<br><code>SELECT 字段1 [ AS 别名1 ], 字段2 [ AS 别名2 ], 字段3 [ AS 别名3 ], ... FROM 表名;</code><br><code>SELECT 字段1 [ 别名1 ], 字段2 [ 别名2 ], 字段3 [ 别名3 ], ... FROM 表名;</code></p><p>去除重复记录：<br><code>SELECT DISTINCT 字段列表 FROM 表名;</code></p><p>转义：<br><code>SELECT * FROM 表名 WHERE name LIKE &#39;/_张三&#39; ESCAPE &#39;/&#39;</code><br>&#x2F; 之后的_不作为通配符</p><h4 id="条件查询"><a href="#条件查询" class="headerlink" title="条件查询"></a>条件查询</h4><p>语法：<br><code>SELECT 字段列表 FROM 表名 WHERE 条件列表;</code></p><p>条件：</p><table><thead><tr><th>比较运算符</th><th>功能</th></tr></thead><tbody><tr><td>&gt;</td><td>大于</td></tr><tr><td>&gt;&#x3D;</td><td>大于等于</td></tr><tr><td>&lt;</td><td>小于</td></tr><tr><td>&lt;&#x3D;</td><td>小于等于</td></tr><tr><td>&#x3D;</td><td>等于</td></tr><tr><td>&lt;&gt; 或 !&#x3D;</td><td>不等于</td></tr><tr><td>BETWEEN … AND …</td><td>在某个范围内（含最小、最大值）</td></tr><tr><td>IN(…)</td><td>在in之后的列表中的值，多选一</td></tr><tr><td>LIKE 占位符</td><td>模糊匹配（_匹配单个字符，%匹配任意个字符）</td></tr><tr><td>IS NULL</td><td>是NULL</td></tr></tbody></table><table><thead><tr><th>逻辑运算符</th><th>功能</th></tr></thead><tbody><tr><td>AND 或 &amp;&amp;</td><td>并且（多个条件同时成立）</td></tr><tr><td>OR 或 &#124;&#124;</td><td>或者（多个条件任意一个成立）</td></tr><tr><td>NOT 或 !</td><td>非，不是</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">-- 年龄等于30</span><br><span class="line">select * from employee where age = 30;</span><br><span class="line">-- 年龄小于30</span><br><span class="line">select * from employee where age &lt; 30;</span><br><span class="line">-- 小于等于</span><br><span class="line">select * from employee where age &lt;= 30;</span><br><span class="line">-- 没有身份证</span><br><span class="line">select * from employee where idcard is null or idcard = &#x27;&#x27;;</span><br><span class="line">-- 有身份证</span><br><span class="line">select * from employee where idcard;</span><br><span class="line">select * from employee where idcard is not null;</span><br><span class="line">-- 不等于</span><br><span class="line">select * from employee where age != 30;</span><br><span class="line">-- 年龄在20到30之间</span><br><span class="line">select * from employee where age between 20 and 30;</span><br><span class="line">select * from employee where age &gt;= 20 and age &lt;= 30;</span><br><span class="line">-- 下面语句不报错，但查不到任何信息</span><br><span class="line">select * from employee where age between 30 and 20;</span><br><span class="line">-- 性别为女且年龄小于30</span><br><span class="line">select * from employee where age &lt; 30 and gender = &#x27;女&#x27;;</span><br><span class="line">-- 年龄等于25或30或35</span><br><span class="line">select * from employee where age = 25 or age = 30 or age = 35;</span><br><span class="line">select * from employee where age in (25, 30, 35);</span><br><span class="line">-- 姓名为两个字</span><br><span class="line">select * from employee where name like &#x27;__&#x27;;</span><br><span class="line">-- 身份证最后为X</span><br><span class="line">select * from employee where idcard like &#x27;%X&#x27;;</span><br></pre></td></tr></table></figure><h4 id="聚合查询（聚合函数）"><a href="#聚合查询（聚合函数）" class="headerlink" title="聚合查询（聚合函数）"></a>聚合查询（聚合函数）</h4><p>常见聚合函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>count</td><td>统计数量</td></tr><tr><td>max</td><td>最大值</td></tr><tr><td>min</td><td>最小值</td></tr><tr><td>avg</td><td>平均值</td></tr><tr><td>sum</td><td>求和</td></tr></tbody></table><p>语法：<br><code>SELECT 聚合函数(字段列表) FROM 表名;</code><br>例：<br><code>SELECT count(id) from employee where workaddress = &quot;广东省&quot;;</code></p><h4 id="分组查询"><a href="#分组查询" class="headerlink" title="分组查询"></a>分组查询</h4><p>语法：<br><code>SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后的过滤条件 ];</code></p><p>where 和 having 的区别：</p><ul><li>执行时机不同：where是分组之前进行过滤，不满足where条件不参与分组；having是分组后对结果进行过滤。</li><li>判断条件不同：where不能对聚合函数进行判断，而having可以。</li></ul><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">-- 根据性别分组，统计男性和女性数量（只显示分组数量，不显示哪个是男哪个是女）</span><br><span class="line">select count(*) from employee group by gender;</span><br><span class="line">-- 根据性别分组，统计男性和女性数量</span><br><span class="line">select gender, count(*) from employee group by gender;</span><br><span class="line">-- 根据性别分组，统计男性和女性的平均年龄</span><br><span class="line">select gender, avg(age) from employee group by gender;</span><br><span class="line">-- 年龄小于45，并根据工作地址分组</span><br><span class="line">select workaddress, count(*) from employee where age &lt; 45 group by workaddress;</span><br><span class="line">-- 年龄小于45，并根据工作地址分组，获取员工数量大于等于3的工作地址</span><br><span class="line">select workaddress, count(*) address_count from employee where age &lt; 45 group by workaddress having address_count &gt;= 3;</span><br></pre></td></tr></table></figure><h5 id="注意事项-2"><a href="#注意事项-2" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>执行顺序：where &gt; 聚合函数 &gt; having</li><li>分组之后，查询的字段一般为聚合函数和分组字段，查询其他字段无任何意义</li></ul><h4 id="排序查询"><a href="#排序查询" class="headerlink" title="排序查询"></a>排序查询</h4><p>语法：<br><code>SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1, 字段2 排序方式2;</code></p><p>排序方式：</p><ul><li>ASC: 升序（默认）</li><li>DESC: 降序</li></ul><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">-- 根据年龄升序排序</span><br><span class="line">SELECT * FROM employee ORDER BY age ASC;</span><br><span class="line">SELECT * FROM employee ORDER BY age;</span><br><span class="line">-- 两字段排序，根据年龄升序排序，入职时间降序排序</span><br><span class="line">SELECT * FROM employee ORDER BY age ASC, entrydate DESC;</span><br></pre></td></tr></table></figure><h5 id="注意事项-3"><a href="#注意事项-3" class="headerlink" title="注意事项"></a>注意事项</h5><p>如果是多字段排序，当第一个字段值相同时，才会根据第二个字段进行排序</p><h4 id="分页查询"><a href="#分页查询" class="headerlink" title="分页查询"></a>分页查询</h4><p>语法：<br><code>SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数;</code></p><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">-- 查询第一页数据，展示10条</span><br><span class="line">SELECT * FROM employee LIMIT 0, 10;</span><br><span class="line">-- 查询第二页</span><br><span class="line">SELECT * FROM employee LIMIT 10, 10;</span><br></pre></td></tr></table></figure><h5 id="注意事项-4"><a href="#注意事项-4" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>起始索引从0开始，起始索引 &#x3D; （查询页码 - 1） * 每页显示记录数</li><li>分页查询是数据库的方言，不同数据库有不同实现，MySQL是LIMIT</li><li>如果查询的是第一页数据，起始索引可以省略，直接简写 LIMIT 10</li></ul><h4 id="DQL执行顺序"><a href="#DQL执行顺序" class="headerlink" title="DQL执行顺序"></a>DQL执行顺序</h4><p>FROM -&gt; WHERE -&gt; GROUP BY -&gt; SELECT -&gt; ORDER BY -&gt; LIMIT</p><h3 id="DCL"><a href="#DCL" class="headerlink" title="DCL"></a>DCL</h3><h4 id="管理用户"><a href="#管理用户" class="headerlink" title="管理用户"></a>管理用户</h4><p>查询用户：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">USER mysql;</span><br><span class="line">SELECT * FROM user;</span><br></pre></td></tr></table></figure><p>创建用户:<br><code>CREATE USER &#39;用户名&#39;@&#39;主机名&#39; IDENTIFIED BY &#39;密码&#39;;</code></p><p>修改用户密码：<br><code>ALTER USER &#39;用户名&#39;@&#39;主机名&#39; IDENTIFIED WITH mysql_native_password BY &#39;新密码&#39;;</code></p><p>删除用户：<br><code>DROP USER &#39;用户名&#39;@&#39;主机名&#39;;</code></p><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">-- 创建用户test，只能在当前主机localhost访问</span><br><span class="line">create user &#x27;test&#x27;@&#x27;localhost&#x27; identified by &#x27;123456&#x27;;</span><br><span class="line">-- 创建用户test，能在任意主机访问</span><br><span class="line">create user &#x27;test&#x27;@&#x27;%&#x27; identified by &#x27;123456&#x27;;</span><br><span class="line">create user &#x27;test&#x27; identified by &#x27;123456&#x27;;</span><br><span class="line">-- 修改密码</span><br><span class="line">alter user &#x27;test&#x27;@&#x27;localhost&#x27; identified with mysql_native_password by &#x27;1234&#x27;;</span><br><span class="line">-- 删除用户</span><br><span class="line">drop user &#x27;test&#x27;@&#x27;localhost&#x27;;</span><br></pre></td></tr></table></figure><h5 id="注意事项-5"><a href="#注意事项-5" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>主机名可以使用 % 通配</li></ul><h4 id="权限控制"><a href="#权限控制" class="headerlink" title="权限控制"></a>权限控制</h4><p>常用权限：</p><table><thead><tr><th>权限</th><th>说明</th></tr></thead><tbody><tr><td>ALL, ALL PRIVILEGES</td><td>所有权限</td></tr><tr><td>SELECT</td><td>查询数据</td></tr><tr><td>INSERT</td><td>插入数据</td></tr><tr><td>UPDATE</td><td>修改数据</td></tr><tr><td>DELETE</td><td>删除数据</td></tr><tr><td>ALTER</td><td>修改表</td></tr><tr><td>DROP</td><td>删除数据库&#x2F;表&#x2F;视图</td></tr><tr><td>CREATE</td><td>创建数据库&#x2F;表</td></tr></tbody></table><p>更多权限请看<a href="#%E6%9D%83%E9%99%90%E4%B8%80%E8%A7%88%E8%A1%A8" title="权限一览表">权限一览表</a></p><p>查询权限：<br><code>SHOW GRANTS FOR &#39;用户名&#39;@&#39;主机名&#39;;</code></p><p>授予权限：<br><code>GRANT 权限列表 ON 数据库名.表名 TO &#39;用户名&#39;@&#39;主机名&#39;;</code></p><p>撤销权限：<br><code>REVOKE 权限列表 ON 数据库名.表名 FROM &#39;用户名&#39;@&#39;主机名&#39;;</code></p><h5 id="注意事项-6"><a href="#注意事项-6" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>多个权限用逗号分隔</li><li>授权时，数据库名和表名可以用 * 进行通配，代表所有</li></ul><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><ul><li>字符串函数</li><li>数值函数</li><li>日期函数</li><li>流程函数</li></ul><h3 id="字符串函数"><a href="#字符串函数" class="headerlink" title="字符串函数"></a>字符串函数</h3><p>常用函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>CONCAT(s1, s2, …, sn)</td><td>字符串拼接，将s1, s2, …, sn拼接成一个字符串</td></tr><tr><td>LOWER(str)</td><td>将字符串全部转为小写</td></tr><tr><td>UPPER(str)</td><td>将字符串全部转为大写</td></tr><tr><td>LPAD(str, n, pad)</td><td>左填充，用字符串pad对str的左边进行填充，达到n个字符串长度</td></tr><tr><td>RPAD(str, n, pad)</td><td>右填充，用字符串pad对str的右边进行填充，达到n个字符串长度</td></tr><tr><td>TRIM(str)</td><td>去掉字符串头部和尾部的空格</td></tr><tr><td>SUBSTRING(str, start, len)</td><td>返回从字符串str从start位置起的len个长度的字符串</td></tr></tbody></table><p>使用示例：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">-- 拼接</span><br><span class="line">SELECT CONCAT(&#x27;Hello&#x27;, &#x27;World&#x27;);</span><br><span class="line">-- 小写</span><br><span class="line">SELECT LOWER(&#x27;Hello&#x27;);</span><br><span class="line">-- 大写</span><br><span class="line">SELECT UPPER(&#x27;Hello&#x27;);</span><br><span class="line">-- 左填充</span><br><span class="line">SELECT LPAD(&#x27;01&#x27;, 5, &#x27;-&#x27;);</span><br><span class="line">-- 右填充</span><br><span class="line">SELECT RPAD(&#x27;01&#x27;, 5, &#x27;-&#x27;);</span><br><span class="line">-- 去除空格</span><br><span class="line">SELECT TRIM(&#x27; Hello World &#x27;);</span><br><span class="line">-- 切片（起始索引为1）</span><br><span class="line">SELECT SUBSTRING(&#x27;Hello World&#x27;, 1, 5);</span><br></pre></td></tr></table></figure><h3 id="数值函数"><a href="#数值函数" class="headerlink" title="数值函数"></a>数值函数</h3><p>常见函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>CEIL(x)</td><td>向上取整</td></tr><tr><td>FLOOR(x)</td><td>向下取整</td></tr><tr><td>MOD(x, y)</td><td>返回x&#x2F;y的模</td></tr><tr><td>RAND()</td><td>返回0~1内的随机数</td></tr><tr><td>ROUND(x, y)</td><td>求参数x的四舍五入值，保留y位小数</td></tr></tbody></table><h3 id="日期函数"><a href="#日期函数" class="headerlink" title="日期函数"></a>日期函数</h3><p>常用函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>CURDATE()</td><td>返回当前日期</td></tr><tr><td>CURTIME()</td><td>返回当前时间</td></tr><tr><td>NOW()</td><td>返回当前日期和时间</td></tr><tr><td>YEAR(date)</td><td>获取指定date的年份</td></tr><tr><td>MONTH(date)</td><td>获取指定date的月份</td></tr><tr><td>DAY(date)</td><td>获取指定date的日期</td></tr><tr><td>DATE_ADD(date, INTERVAL expr type)</td><td>返回一个日期&#x2F;时间值加上一个时间间隔expr后的时间值</td></tr><tr><td>DATEDIFF(date1, date2)</td><td>返回起始时间date1和结束时间date2之间的天数</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">-- DATE_ADD</span><br><span class="line">SELECT DATE_ADD(NOW(), INTERVAL 70 YEAR);</span><br></pre></td></tr></table></figure><h3 id="流程函数"><a href="#流程函数" class="headerlink" title="流程函数"></a>流程函数</h3><p>常用函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>IF(value, t, f)</td><td>如果value为true，则返回t，否则返回f</td></tr><tr><td>IFNULL(value1, value2)</td><td>如果value1不为空，返回value1，否则返回value2</td></tr><tr><td>CASE WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END</td><td>如果val1为true，返回res1，… 否则返回default默认值</td></tr><tr><td>CASE [ expr ] WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END</td><td>如果expr的值等于val1，返回res1，… 否则返回default默认值</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">select</span><br><span class="line">name,</span><br><span class="line">(case when age &gt; 30 then &#x27;中年&#x27; else &#x27;青年&#x27; end)</span><br><span class="line">from employee;</span><br><span class="line">select</span><br><span class="line">name,</span><br><span class="line">(case workaddress when &#x27;北京市&#x27; then &#x27;一线城市&#x27; when &#x27;上海市&#x27; then &#x27;一线城市&#x27; else &#x27;二线城市&#x27; end) as &#x27;工作地址&#x27;</span><br><span class="line">from employee;</span><br></pre></td></tr></table></figure><h2 id="约束"><a href="#约束" class="headerlink" title="约束"></a>约束</h2><p>分类：</p><table><thead><tr><th>约束</th><th>描述</th><th>关键字</th></tr></thead><tbody><tr><td>非空约束</td><td>限制该字段的数据不能为null</td><td>NOT NULL</td></tr><tr><td>唯一约束</td><td>保证该字段的所有数据都是唯一、不重复的</td><td>UNIQUE</td></tr><tr><td>主键约束</td><td>主键是一行数据的唯一标识，要求非空且唯一</td><td>PRIMARY KEY</td></tr><tr><td>默认约束</td><td>保存数据时，如果未指定该字段的值，则采用默认值</td><td>DEFAULT</td></tr><tr><td>检查约束（8.0.1版本后）</td><td>保证字段值满足某一个条件</td><td>CHECK</td></tr><tr><td>外键约束</td><td>用来让两张图的数据之间建立连接，保证数据的一致性和完整性</td><td>FOREIGN KEY</td></tr></tbody></table><p>约束是作用于表中字段上的，可以再创建表&#x2F;修改表的时候添加约束。</p><h3 id="常用约束"><a href="#常用约束" class="headerlink" title="常用约束"></a>常用约束</h3><table><thead><tr><th>约束条件</th><th>关键字</th></tr></thead><tbody><tr><td>主键</td><td>PRIMARY KEY</td></tr><tr><td>自动增长</td><td>AUTO_INCREMENT</td></tr><tr><td>不为空</td><td>NOT NULL</td></tr><tr><td>唯一</td><td>UNIQUE</td></tr><tr><td>逻辑条件</td><td>CHECK</td></tr><tr><td>默认值</td><td>DEFAULT</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">create table user(</span><br><span class="line">id int primary key auto_increment,</span><br><span class="line">name varchar(10) not null unique,</span><br><span class="line">age int check(age &gt; 0 and age &lt; 120),</span><br><span class="line">status char(1) default &#x27;1&#x27;,</span><br><span class="line">gender char(1)</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h3 id="外键约束"><a href="#外键约束" class="headerlink" title="外键约束"></a>外键约束</h3><p>添加外键：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE 表名(</span><br><span class="line">字段名 字段类型,</span><br><span class="line">...</span><br><span class="line">[CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)</span><br><span class="line">);</span><br><span class="line">ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名);</span><br><span class="line"></span><br><span class="line">-- 例子</span><br><span class="line">alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id);</span><br></pre></td></tr></table></figure><p>删除外键：<br><code>ALTER TABLE 表名 DROP FOREIGN KEY 外键名;</code></p><h4 id="删除-更新行为"><a href="#删除-更新行为" class="headerlink" title="删除&#x2F;更新行为"></a>删除&#x2F;更新行为</h4><table><thead><tr><th>行为</th><th>说明</th></tr></thead><tbody><tr><td>NO ACTION</td><td>当在父表中删除&#x2F;更新对应记录时，首先检查该记录是否有对应外键，如果有则不允许删除&#x2F;更新（与RESTRICT一致）</td></tr><tr><td>RESTRICT</td><td>当在父表中删除&#x2F;更新对应记录时，首先检查该记录是否有对应外键，如果有则不允许删除&#x2F;更新（与NO ACTION一致）</td></tr><tr><td>CASCADE</td><td>当在父表中删除&#x2F;更新对应记录时，首先检查该记录是否有对应外键，如果有则也删除&#x2F;更新外键在子表中的记录</td></tr><tr><td>SET NULL</td><td>当在父表中删除&#x2F;更新对应记录时，首先检查该记录是否有对应外键，如果有则设置子表中该外键值为null（要求该外键允许为null）</td></tr><tr><td>SET DEFAULT</td><td>父表有变更时，子表将外键设为一个默认值（Innodb不支持）</td></tr></tbody></table><p>更改删除&#x2F;更新行为：<br><code>ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE 行为 ON DELETE 行为;</code></p><h2 id="多表查询"><a href="#多表查询" class="headerlink" title="多表查询"></a>多表查询</h2><h3 id="多表关系"><a href="#多表关系" class="headerlink" title="多表关系"></a>多表关系</h3><ul><li>一对多（多对一）</li><li>多对多</li><li>一对一</li></ul><h4 id="一对多"><a href="#一对多" class="headerlink" title="一对多"></a>一对多</h4><p>案例：部门与员工<br>关系：一个部门对应多个员工，一个员工对应一个部门<br>实现：在多的一方建立外键，指向一的一方的主键</p><h4 id="多对多"><a href="#多对多" class="headerlink" title="多对多"></a>多对多</h4><p>案例：学生与课程<br>关系：一个学生可以选多门课程，一门课程也可以供多个学生选修<br>实现：建立第三张中间表，中间表至少包含两个外键，分别关联两方主键</p><h4 id="一对一"><a href="#一对一" class="headerlink" title="一对一"></a>一对一</h4><p>案例：用户与用户详情<br>关系：一对一关系，多用于单表拆分，将一张表的基础字段放在一张表中，其他详情字段放在另一张表中，以提升操作效率<br>实现：在任意一方加入外键，关联另外一方的主键，并且设置外键为唯一的（UNIQUE）</p><h3 id="查询"><a href="#查询" class="headerlink" title="查询"></a>查询</h3><p>合并查询（笛卡尔积，会展示所有组合结果）：<br><code>select * from employee, dept;</code></p><blockquote><p>笛卡尔积：两个集合A集合和B集合的所有组合情况（在多表查询时，需要消除无效的笛卡尔积）</p></blockquote><p>消除无效笛卡尔积：<br><code>select * from employee, dept where employee.dept = dept.id;</code></p><h3 id="内连接查询"><a href="#内连接查询" class="headerlink" title="内连接查询"></a>内连接查询</h3><p>内连接查询的是两张表交集的部分</p><p>隐式内连接：<br><code>SELECT 字段列表 FROM 表1, 表2 WHERE 条件 ...;</code></p><p>显式内连接：<br><code>SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ...;</code></p><p>显式性能比隐式高</p><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">-- 查询员工姓名，及关联的部门的名称</span><br><span class="line">-- 隐式</span><br><span class="line">select e.name, d.name from employee as e, dept as d where e.dept = d.id;</span><br><span class="line">-- 显式</span><br><span class="line">select e.name, d.name from employee as e inner join dept as d on e.dept = d.id;</span><br></pre></td></tr></table></figure><h3 id="外连接查询"><a href="#外连接查询" class="headerlink" title="外连接查询"></a>外连接查询</h3><p>左外连接：<br>查询左表所有数据，以及两张表交集部分数据<br><code>SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ...;</code><br>相当于查询表1的所有数据，包含表1和表2交集部分数据</p><p>右外连接：<br>查询右表所有数据，以及两张表交集部分数据<br><code>SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ...;</code></p><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">-- 左</span><br><span class="line">select e.*, d.name from employee as e left outer join dept as d on e.dept = d.id;</span><br><span class="line">select d.name, e.* from dept d left outer join emp e on e.dept = d.id;  -- 这条语句与下面的语句效果一样</span><br><span class="line">-- 右</span><br><span class="line">select d.name, e.* from employee as e right outer join dept as d on e.dept = d.id;</span><br></pre></td></tr></table></figure><p>左连接可以查询到没有dept的employee，右连接可以查询到没有employee的dept</p><h3 id="自连接查询"><a href="#自连接查询" class="headerlink" title="自连接查询"></a>自连接查询</h3><p>当前表与自身的连接查询，自连接必须使用表别名</p><p>语法：<br><code>SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ...;</code></p><p>自连接查询，可以是内连接查询，也可以是外连接查询</p><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">-- 查询员工及其所属领导的名字</span><br><span class="line">select a.name, b.name from employee a, employee b where a.manager = b.id;</span><br><span class="line">-- 没有领导的也查询出来</span><br><span class="line">select a.name, b.name from employee a left join employee b on a.manager = b.id;</span><br></pre></td></tr></table></figure><h3 id="联合查询-union-union-all"><a href="#联合查询-union-union-all" class="headerlink" title="联合查询 union, union all"></a>联合查询 union, union all</h3><p>把多次查询的结果合并，形成一个新的查询集</p><p>语法：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">SELECT 字段列表 FROM 表A ...</span><br><span class="line">UNION [ALL]</span><br><span class="line">SELECT 字段列表 FROM 表B ...</span><br></pre></td></tr></table></figure><h4 id="注意事项-7"><a href="#注意事项-7" class="headerlink" title="注意事项"></a>注意事项</h4><ul><li>UNION ALL 会有重复结果，UNION 不会</li><li>联合查询比使用or效率高，不会使索引失效</li></ul><h3 id="子查询"><a href="#子查询" class="headerlink" title="子查询"></a>子查询</h3><p>SQL语句中嵌套SELECT语句，称谓嵌套查询，又称子查询。<br><code>SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2);</code><br><strong>子查询外部的语句可以是 INSERT &#x2F; UPDATE &#x2F; DELETE &#x2F; SELECT 的任何一个</strong></p><p>根据子查询结果可以分为：</p><ul><li>标量子查询（子查询结果为单个值）</li><li>列子查询（子查询结果为一列）</li><li>行子查询（子查询结果为一行）</li><li>表子查询（子查询结果为多行多列）</li></ul><p>根据子查询位置可分为：</p><ul><li>WHERE 之后</li><li>FROM 之后</li><li>SELECT 之后</li></ul><h4 id="标量子查询"><a href="#标量子查询" class="headerlink" title="标量子查询"></a>标量子查询</h4><p>子查询返回的结果是单个值（数字、字符串、日期等）。<br>常用操作符：- &lt; &gt; &gt; &gt;&#x3D; &lt; &lt;&#x3D;</p><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">-- 查询销售部所有员工</span><br><span class="line">select id from dept where name = &#x27;销售部&#x27;;</span><br><span class="line">-- 根据销售部部门ID，查询员工信息</span><br><span class="line">select * from employee where dept = 4;</span><br><span class="line">-- 合并（子查询）</span><br><span class="line">select * from employee where dept = (select id from dept where name = &#x27;销售部&#x27;);</span><br><span class="line"></span><br><span class="line">-- 查询xxx入职之后的员工信息</span><br><span class="line">select * from employee where entrydate &gt; (select entrydate from employee where name = &#x27;xxx&#x27;);</span><br></pre></td></tr></table></figure><h4 id="列子查询"><a href="#列子查询" class="headerlink" title="列子查询"></a>列子查询</h4><p>返回的结果是一列（可以是多行）。</p><p>常用操作符：</p><table><thead><tr><th>操作符</th><th>描述</th></tr></thead><tbody><tr><td>IN</td><td>在指定的集合范围内，多选一</td></tr><tr><td>NOT IN</td><td>不在指定的集合范围内</td></tr><tr><td>ANY</td><td>子查询返回列表中，有任意一个满足即可</td></tr><tr><td>SOME</td><td>与ANY等同，使用SOME的地方都可以使用ANY</td></tr><tr><td>ALL</td><td>子查询返回列表的所有值都必须满足</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">-- 查询销售部和市场部的所有员工信息</span><br><span class="line">select * from employee where dept in (select id from dept where name = &#x27;销售部&#x27; or name = &#x27;市场部&#x27;);</span><br><span class="line">-- 查询比财务部所有人工资都高的员工信息</span><br><span class="line">select * from employee where salary &gt; all(select salary from employee where dept = (select id from dept where name = &#x27;财务部&#x27;));</span><br><span class="line">-- 查询比研发部任意一人工资高的员工信息</span><br><span class="line">select * from employee where salary &gt; any (select salary from employee where dept = (select id from dept where name = &#x27;研发部&#x27;));</span><br></pre></td></tr></table></figure><h4 id="行子查询"><a href="#行子查询" class="headerlink" title="行子查询"></a>行子查询</h4><p>返回的结果是一行（可以是多列）。<br>常用操作符：&#x3D;, &lt;, &gt;, IN, NOT IN</p><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-- 查询与xxx的薪资及直属领导相同的员工信息</span><br><span class="line">select * from employee where (salary, manager) = (12500, 1);</span><br><span class="line">select * from employee where (salary, manager) = (select salary, manager from employee where name = &#x27;xxx&#x27;);</span><br></pre></td></tr></table></figure><h4 id="表子查询"><a href="#表子查询" class="headerlink" title="表子查询"></a>表子查询</h4><p>返回的结果是多行多列<br>常用操作符：IN</p><p>例子：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">-- 查询与xxx1，xxx2的职位和薪资相同的员工</span><br><span class="line">select * from employee where (job, salary) in (select job, salary from employee where name = &#x27;xxx1&#x27; or name = &#x27;xxx2&#x27;);</span><br><span class="line">-- 查询入职日期是2006-01-01之后的员工，及其部门信息</span><br><span class="line">select e.*, d.* from (select * from employee where entrydate &gt; &#x27;2006-01-01&#x27;) as e left join dept as d on e.dept = d.id;</span><br></pre></td></tr></table></figure><h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><p>事务是一组操作的集合，事务会把所有操作作为一个整体一起向系统提交或撤销操作请求，即这些操作要么同时成功，要么同时失败。</p><p>基本操作：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">-- 1. 查询张三账户余额</span><br><span class="line">select * from account where name = &#x27;张三&#x27;;</span><br><span class="line">-- 2. 将张三账户余额-1000</span><br><span class="line">update account set money = money - 1000 where name = &#x27;张三&#x27;;</span><br><span class="line">-- 此语句出错后张三钱减少但是李四钱没有增加</span><br><span class="line">模拟sql语句错误</span><br><span class="line">-- 3. 将李四账户余额+1000</span><br><span class="line">update account set money = money + 1000 where name = &#x27;李四&#x27;;</span><br><span class="line"></span><br><span class="line">-- 查看事务提交方式</span><br><span class="line">SELECT @@AUTOCOMMIT;</span><br><span class="line">-- 设置事务提交方式，1为自动提交，0为手动提交，该设置只对当前会话有效</span><br><span class="line">SET @@AUTOCOMMIT = 0;</span><br><span class="line">-- 提交事务</span><br><span class="line">COMMIT;</span><br><span class="line">-- 回滚事务</span><br><span class="line">ROLLBACK;</span><br><span class="line"></span><br><span class="line">-- 设置手动提交后上面代码改为：</span><br><span class="line">select * from account where name = &#x27;张三&#x27;;</span><br><span class="line">update account set money = money - 1000 where name = &#x27;张三&#x27;;</span><br><span class="line">update account set money = money + 1000 where name = &#x27;李四&#x27;;</span><br><span class="line">commit;</span><br></pre></td></tr></table></figure><p>操作方式二：</p><p>开启事务：<br><code>START TRANSACTION 或 BEGIN TRANSACTION;</code><br>提交事务：<br><code>COMMIT;</code><br>回滚事务：<br><code>ROLLBACK;</code></p><p>操作实例：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">start transaction;</span><br><span class="line">select * from account where name = &#x27;张三&#x27;;</span><br><span class="line">update account set money = money - 1000 where name = &#x27;张三&#x27;;</span><br><span class="line">update account set money = money + 1000 where name = &#x27;李四&#x27;;</span><br><span class="line">commit;</span><br></pre></td></tr></table></figure><h3 id="四大特性ACID"><a href="#四大特性ACID" class="headerlink" title="四大特性ACID"></a>四大特性ACID</h3><ul><li>原子性(Atomicity)：事务是不可分割的最小操作但愿，要么全部成功，要么全部失败</li><li>一致性(Consistency)：事务完成时，必须使所有数据都保持一致状态</li><li>隔离性(Isolation)：数据库系统提供的隔离机制，保证事务在不受外部并发操作影响的独立环境下运行</li><li>持久性(Durability)：事务一旦提交或回滚，它对数据库中的数据的改变就是永久的</li></ul><h3 id="并发事务"><a href="#并发事务" class="headerlink" title="并发事务"></a>并发事务</h3><table><thead><tr><th>问题</th><th>描述</th></tr></thead><tbody><tr><td>脏读</td><td>一个事务读到另一个事务还没提交的数据</td></tr><tr><td>不可重复读</td><td>一个事务先后读取同一条记录，但两次读取的数据不同</td></tr><tr><td>幻读</td><td>一个事务按照条件查询数据时，没有对应的数据行，但是再插入数据时，又发现这行数据已经存在</td></tr></tbody></table><blockquote><p>这三个问题的详细演示：<a href="https://www.bilibili.com/video/BV1Kr4y1i7ru?p=55cd">https://www.bilibili.com/video/BV1Kr4y1i7ru?p=55cd</a> </p></blockquote><p>并发事务隔离级别：</p><table><thead><tr><th>隔离级别</th><th>脏读</th><th>不可重复读</th><th>幻读</th></tr></thead><tbody><tr><td>Read uncommitted</td><td>√</td><td>√</td><td>√</td></tr><tr><td>Read committed</td><td>×</td><td>√</td><td>√</td></tr><tr><td>Repeatable Read(默认)</td><td>×</td><td>×</td><td>√</td></tr><tr><td>Serializable</td><td>×</td><td>×</td><td>×</td></tr></tbody></table><ul><li>√表示在当前隔离级别下该问题会出现</li><li>Serializable 性能最低；Read uncommitted 性能最高，数据安全性最差</li></ul><p>查看事务隔离级别：<br><code>SELECT @@TRANSACTION_ISOLATION;</code><br>设置事务隔离级别：<br><code>SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL &#123;READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE &#125;;</code><br>SESSION 是会话级别，表示只针对当前会话有效，GLOBAL 表示对所有会话有效</p><h1 id="进阶篇"><a href="#进阶篇" class="headerlink" title="进阶篇"></a>进阶篇</h1><h2 id="存储引擎"><a href="#存储引擎" class="headerlink" title="存储引擎"></a>存储引擎</h2><p>MySQL体系结构：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734713652482_MySQL%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84_20220315034329549927.png" alt="MySQL体系结构_20220315034329549927.png"></p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734713659791_MySQL%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84%E5%B1%82%E7%BA%A7%E5%90%AB%E4%B9%89_20220315034359342837.png" alt="MySQL体系结构层级含义_20220315034359342837.png"></p><p>存储引擎就是存储数据、建立索引、更新&#x2F;查询数据等技术的实现方式。存储引擎是基于表而不是基于库的，所以存储引擎也可以被称为表引擎。<br>默认存储引擎是InnoDB。</p><p>相关操作：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">-- 查询建表语句</span><br><span class="line">show create table account;</span><br><span class="line">-- 建表时指定存储引擎</span><br><span class="line">CREATE TABLE 表名(</span><br><span class="line">...</span><br><span class="line">) ENGINE=INNODB;</span><br><span class="line">-- 查看当前数据库支持的存储引擎</span><br><span class="line">show engines;</span><br></pre></td></tr></table></figure><h3 id="InnoDB"><a href="#InnoDB" class="headerlink" title="InnoDB"></a>InnoDB</h3><p>InnoDB 是一种兼顾高可靠性和高性能的通用存储引擎，在 MySQL 5.5 之后，InnoDB 是默认的 MySQL 引擎。</p><p>特点：</p><ul><li>DML 操作遵循 ACID 模型，支持<strong>事务</strong></li><li><strong>行级锁</strong>，提高并发访问性能</li><li>支持<strong>外键</strong>约束，保证数据的完整性和正确性</li></ul><p>文件：</p><ul><li>xxx.ibd: xxx代表表名，InnoDB 引擎的每张表都会对应这样一个表空间文件，存储该表的表结构（frm、sdi）、数据和索引。</li></ul><p>参数：innodb_file_per_table，决定多张表共享一个表空间还是每张表对应一个表空间</p><p>知识点：</p><p>查看 Mysql 变量：<br><code>show variables like &#39;innodb_file_per_table&#39;;</code></p><p>从idb文件提取表结构数据：<br>（在cmd运行）<br><code>ibd2sdi xxx.ibd</code></p><p>InnoDB 逻辑存储结构：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734713482073_%E9%80%BB%E8%BE%91%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84_20220316030616590001.png" alt="逻辑存储结构_20220316030616590001.png"></p><h3 id="MyISAM"><a href="#MyISAM" class="headerlink" title="MyISAM"></a>MyISAM</h3><p>MyISAM 是 MySQL 早期的默认存储引擎。</p><p>特点：</p><ul><li>不支持事务，不支持外键</li><li>支持表锁，不支持行锁</li><li>访问速度快</li></ul><p>文件：</p><ul><li>xxx.sdi: 存储表结构信息</li><li>xxx.MYD: 存储数据</li><li>xxx.MYI: 存储索引</li></ul><h3 id="Memory"><a href="#Memory" class="headerlink" title="Memory"></a>Memory</h3><p>Memory 引擎的表数据是存储在内存中的，受硬件问题、断电问题的影响，只能将这些表作为临时表或缓存使用。</p><p>特点：</p><ul><li>存放在内存中，速度快</li><li>hash索引（默认）</li></ul><p>文件：</p><ul><li>xxx.sdi: 存储表结构信息</li></ul><h3 id="存储引擎特点"><a href="#存储引擎特点" class="headerlink" title="存储引擎特点"></a>存储引擎特点</h3><table><thead><tr><th>特点</th><th>InnoDB</th><th>MyISAM</th><th>Memory</th></tr></thead><tbody><tr><td>存储限制</td><td>64TB</td><td>有</td><td>有</td></tr><tr><td>事务安全</td><td>支持</td><td>-</td><td>-</td></tr><tr><td>锁机制</td><td>行锁</td><td>表锁</td><td>表锁</td></tr><tr><td>B+tree索引</td><td>支持</td><td>支持</td><td>支持</td></tr><tr><td>Hash索引</td><td>-</td><td>-</td><td>支持</td></tr><tr><td>全文索引</td><td>支持（5.6版本之后）</td><td>支持</td><td>-</td></tr><tr><td>空间使用</td><td>高</td><td>低</td><td>N&#x2F;A</td></tr><tr><td>内存使用</td><td>高</td><td>低</td><td>中等</td></tr><tr><td>批量插入速度</td><td>低</td><td>高</td><td>高</td></tr><tr><td>支持外键</td><td>支持</td><td>-</td><td>-</td></tr></tbody></table><h3 id="存储引擎的选择"><a href="#存储引擎的选择" class="headerlink" title="存储引擎的选择"></a>存储引擎的选择</h3><p>在选择存储引擎时，应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统，还可以根据实际情况选择多种存储引擎进行组合。</p><ul><li>InnoDB: 如果应用对事物的完整性有比较高的要求，在并发条件下要求数据的一致性，数据操作除了插入和查询之外，还包含很多的更新、删除操作，则 InnoDB 是比较合适的选择</li><li>MyISAM: 如果应用是以读操作和插入操作为主，只有很少的更新和删除操作，并且对事务的完整性、并发性要求不高，那这个存储引擎是非常合适的。</li><li>Memory: 将所有数据保存在内存中，访问速度快，通常用于临时表及缓存。Memory 的缺陷是对表的大小有限制，太大的表无法缓存在内存中，而且无法保障数据的安全性</li></ul><p>电商中的足迹和评论适合使用 MyISAM 引擎，缓存适合使用 Memory 引擎。</p><h2 id="性能分析"><a href="#性能分析" class="headerlink" title="性能分析"></a>性能分析</h2><h3 id="查看执行频次"><a href="#查看执行频次" class="headerlink" title="查看执行频次"></a>查看执行频次</h3><p>查看当前数据库的 INSERT, UPDATE, DELETE, SELECT 访问频次：<br><code>SHOW GLOBAL STATUS LIKE &#39;Com_______&#39;;</code> 或者 <code>SHOW SESSION STATUS LIKE &#39;Com_______&#39;;</code><br>例：<code>show global status like &#39;Com_______&#39;</code></p><h3 id="慢查询日志"><a href="#慢查询日志" class="headerlink" title="慢查询日志"></a>慢查询日志</h3><p>慢查询日志记录了所有执行时间超过指定参数（long_query_time，单位：秒，默认10秒）的所有SQL语句的日志。<br>MySQL的慢查询日志默认没有开启，需要在MySQL的配置文件（&#x2F;etc&#x2F;my.cnf）中配置如下信息：<br>    # 开启慢查询日志开关<br>    slow_query_log&#x3D;1<br>    # 设置慢查询日志的时间为2秒，SQL语句执行时间超过2秒，就会视为慢查询，记录慢查询日志<br>    long_query_time&#x3D;2<br>更改后记得重启MySQL服务，日志文件位置：&#x2F;var&#x2F;lib&#x2F;mysql&#x2F;localhost-slow.log</p><p>查看慢查询日志开关状态：<br><code>show variables like &#39;slow_query_log&#39;;</code></p><h3 id="profile"><a href="#profile" class="headerlink" title="profile"></a>profile</h3><p>show profile 能在做SQL优化时帮我们了解时间都耗费在哪里。通过 have_profiling 参数，能看到当前 MySQL 是否支持 profile 操作：<br><code>SELECT @@have_profiling;</code><br>profiling 默认关闭，可以通过set语句在session&#x2F;global级别开启 profiling：<br><code>SET profiling = 1;</code><br>查看所有语句的耗时：<br><code>show profiles;</code><br>查看指定query_id的SQL语句各个阶段的耗时：<br><code>show profile for query query_id;</code><br>查看指定query_id的SQL语句CPU的使用情况<br><code>show profile cpu for query query_id;</code></p><h3 id="explain"><a href="#explain" class="headerlink" title="explain"></a>explain</h3><p>EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息，包括在 SELECT 语句执行过程中表如何连接和连接的顺序。<br>语法：<br>    # 直接在select语句之前加上关键字 explain &#x2F; desc<br>    EXPLAIN SELECT 字段列表 FROM 表名 HWERE 条件;</p><p>EXPLAIN 各字段含义：</p><ul><li>id：select 查询的序列号，表示查询中执行 select 子句或者操作表的顺序（id相同，执行顺序从上到下；id不同，值越大越先执行）</li><li>select_type：表示 SELECT 的类型，常见取值有 SIMPLE（简单表，即不适用表连接或者子查询）、PRIMARY（主查询，即外层的查询）、UNION（UNION中的第二个或者后面的查询语句）、SUBQUERY（SELECT&#x2F;WHERE之后包含了子查询）等</li><li>type：表示连接类型，性能由好到差的连接类型为 NULL、system、const、eq_ref、ref、range、index、all</li><li>possible_key：可能应用在这张表上的索引，一个或多个</li><li>Key：实际使用的索引，如果为 NULL，则没有使用索引</li><li>Key_len：表示索引中使用的字节数，该值为索引字段最大可能长度，并非实际使用长度，在不损失精确性的前提下，长度越短越好</li><li>rows：MySQL认为必须要执行的行数，在InnoDB引擎的表中，是一个估计值，可能并不总是准确的</li><li>filtered：表示返回结果的行数占需读取行数的百分比，filtered的值越大越好</li></ul><h2 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h2><p>索引是帮助 MySQL <strong>高效获取数据</strong>的<strong>数据结构（有序）</strong>。在数据之外，数据库系统还维护着满足特定查找算法的数据结构，这些数据结构以某种方式引用（指向）数据，这样就可以在这些数据结构上实现高级查询算法，这种数据结构就是索引。</p><p>优缺点：</p><p>优点：</p><ul><li>提高数据检索效率，降低数据库的IO成本</li><li>通过索引列对数据进行排序，降低数据排序的成本，降低CPU的消耗</li></ul><p>缺点：</p><ul><li>索引列也是要占用空间的</li><li>索引大大提高了查询效率，但降低了更新的速度，比如 INSERT、UPDATE、DELETE</li></ul><h3 id="索引结构"><a href="#索引结构" class="headerlink" title="索引结构"></a>索引结构</h3><table><thead><tr><th>索引结构</th><th>描述</th></tr></thead><tbody><tr><td>B+Tree</td><td>最常见的索引类型，大部分引擎都支持B+树索引</td></tr><tr><td>Hash</td><td>底层数据结构是用哈希表实现，只有精确匹配索引列的查询才有效，不支持范围查询</td></tr><tr><td>R-Tree(空间索引)</td><td>空间索引是 MyISAM 引擎的一个特殊索引类型，主要用于地理空间数据类型，通常使用较少</td></tr><tr><td>Full-Text(全文索引)</td><td>是一种通过建立倒排索引，快速匹配文档的方式，类似于 Lucene, Solr, ES</td></tr></tbody></table><table><thead><tr><th>索引</th><th>InnoDB</th><th>MyISAM</th><th>Memory</th></tr></thead><tbody><tr><td>B+Tree索引</td><td>支持</td><td>支持</td><td>支持</td></tr><tr><td>Hash索引</td><td>不支持</td><td>不支持</td><td>支持</td></tr><tr><td>R-Tree索引</td><td>不支持</td><td>支持</td><td>不支持</td></tr><tr><td>Full-text</td><td>5.6版本后支持</td><td>支持</td><td>不支持</td></tr></tbody></table><h4 id="B-Tree"><a href="#B-Tree" class="headerlink" title="B-Tree"></a>B-Tree</h4><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734714375595_%E4%BA%8C%E5%8F%89%E6%A0%91_20220316153214227108.png" alt="二叉树_20220316153214227108.png"></p><p>二叉树的缺点可以用红黑树来解决：<br><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734714388157_%E7%BA%A2%E9%BB%91%E6%A0%91_20220316163142686602.png" alt="红黑树_20220316163142686602.png"><br>红黑树也存在大数据量情况下，层级较深，检索速度慢的问题。</p><p>为了解决上述问题，可以使用 B-Tree 结构。<br>B-Tree (多路平衡查找树) 以一棵最大度数（max-degree，指一个节点的子节点个数）为5（5阶）的 b-tree 为例（每个节点最多存储4个key，5个指针）</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734714437322_B-Tree%E7%BB%93%E6%9E%84_20220316163813441163.png" alt="B-Tree结构_20220316163813441163.png"></p><blockquote><p>B-Tree 的数据插入过程动画参照：<a href="https://www.bilibili.com/video/BV1Kr4y1i7ru?p=68">https://www.bilibili.com/video/BV1Kr4y1i7ru?p=68</a><br>演示地址：<a href="https://www.cs.usfca.edu/~galles/visualization/BTree.html">https://www.cs.usfca.edu/~galles/visualization/BTree.html</a></p></blockquote><h4 id="B-Tree-1"><a href="#B-Tree-1" class="headerlink" title="B+Tree"></a>B+Tree</h4><p>结构图：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734714446827_B+Tree%E7%BB%93%E6%9E%84%E5%9B%BE_20220316170700591277.png" alt="B+Tree结构图_20220316170700591277.png"></p><blockquote><p>演示地址：<a href="https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html">https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html</a></p></blockquote><p>与 B-Tree 的区别：</p><ul><li>所有的数据都会出现在叶子节点</li><li>叶子节点形成一个单向链表</li></ul><p>MySQL 索引数据结构对经典的 B+Tree 进行了优化。在原 B+Tree 的基础上，增加一个指向相邻叶子节点的链表指针，就形成了带有顺序指针的 B+Tree，提高区间访问的性能。</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734714491947_%E7%BB%93%E6%9E%84%E5%9B%BE_20220316171730865611.png" alt="结构图_20220316171730865611.png"></p><h4 id="Hash"><a href="#Hash" class="headerlink" title="Hash"></a>Hash</h4><p>哈希索引就是采用一定的hash算法，将键值换算成新的hash值，映射到对应的槽位上，然后存储在hash表中。<br>如果两个（或多个）键值，映射到一个相同的槽位上，他们就产生了hash冲突（也称为hash碰撞），可以通过链表来解决。</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734714513778_Hash%E7%B4%A2%E5%BC%95%E5%8E%9F%E7%90%86%E5%9B%BE_20220317143226150679.png" alt="Hash索引原理图_20220317143226150679.png"></p><p>特点：</p><ul><li>Hash索引只能用于对等比较（&#x3D;、in），不支持范围查询（betwwn、&gt;、&lt;、…）</li><li>无法利用索引完成排序操作</li><li>查询效率高，通常只需要一次检索就可以了，效率通常要高于 B+Tree 索引</li></ul><p>存储引擎支持：</p><ul><li>Memory</li><li>InnoDB: 具有自适应hash功能，hash索引是存储引擎根据 B+Tree 索引在指定条件下自动构建的</li></ul><h4 id="面试题"><a href="#面试题" class="headerlink" title="面试题"></a>面试题</h4><ol><li>为什么 InnoDB 存储引擎选择使用 B+Tree 索引结构？</li></ol><ul><li>相对于二叉树，层级更少，搜索效率高</li><li>对于 B-Tree，无论是叶子节点还是非叶子节点，都会保存数据，这样导致一页中存储的键值减少，指针也跟着减少，要同样保存大量数据，只能增加树的高度，导致性能降低</li><li>相对于 Hash 索引，B+Tree 支持范围匹配及排序操作</li></ul><h3 id="索引分类"><a href="#索引分类" class="headerlink" title="索引分类"></a>索引分类</h3><table><thead><tr><th>分类</th><th>含义</th><th>特点</th><th>关键字</th></tr></thead><tbody><tr><td>主键索引</td><td>针对于表中主键创建的索引</td><td>默认自动创建，只能有一个</td><td>PRIMARY</td></tr><tr><td>唯一索引</td><td>避免同一个表中某数据列中的值重复</td><td>可以有多个</td><td>UNIQUE</td></tr><tr><td>常规索引</td><td>快速定位特定数据</td><td>可以有多个</td><td></td></tr><tr><td>全文索引</td><td>全文索引查找的是文本中的关键词，而不是比较索引中的值</td><td>可以有多个</td><td>FULLTEXT</td></tr></tbody></table><p>在 InnoDB 存储引擎中，根据索引的存储形式，又可以分为以下两种：</p><table><thead><tr><th>分类</th><th>含义</th><th>特点</th></tr></thead><tbody><tr><td>聚集索引(Clustered Index)</td><td>将数据存储与索引放一块，索引结构的叶子节点保存了行数据</td><td>必须有，而且只有一个</td></tr><tr><td>二级索引(Secondary Index)</td><td>将数据与索引分开存储，索引结构的叶子节点关联的是对应的主键</td><td>可以存在多个</td></tr></tbody></table><p>演示图：</p><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734714549937_%E5%8E%9F%E7%90%86%E5%9B%BE_20220318194454880073.png" alt="原理图_20220318194454880073.png"><br><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1734714554084_%E6%BC%94%E7%A4%BA%E5%9B%BE_20220319215403721066.png" alt="演示图_20220319215403721066.png"></p><p>聚集索引选取规则：</p><ul><li>如果存在主键，主键索引就是聚集索引</li><li>如果不存在主键，将使用第一个唯一(UNIQUE)索引作为聚集索引</li><li>如果表没有主键或没有合适的唯一索引，则 InnoDB 会自动生成一个 rowid 作为隐藏的聚集索引</li></ul><h4 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h4><p>1. 以下 SQL 语句，哪个执行效率高？为什么？</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">select * from user where id = 10;</span><br><span class="line">select * from user where name = &#x27;Arm&#x27;;</span><br><span class="line">-- 备注：id为主键，name字段创建的有索引</span><br></pre></td></tr></table></figure><p>答：第一条语句，因为第二条需要回表查询，相当于两个步骤。</p><p>2. InnoDB 主键索引的 B+Tree 高度为多少？</p><p>答：假设一行数据大小为1k，一页中可以存储16行这样的数据。InnoDB 的指针占用6个字节的空间，主键假设为bigint，占用字节数为8.<br>可得公式：<code>n * 8 + (n + 1) * 6 = 16 * 1024</code>，其中 8 表示 bigint 占用的字节数，n 表示当前节点存储的key的数量，(n + 1) 表示指针数量（比key多一个）。算出n约为1170。</p><p>如果树的高度为2，那么他能存储的数据量大概为：<code>1171 * 16 = 18736</code>；<br>如果树的高度为3，那么他能存储的数据量大概为：<code>1171 * 1171 * 16 = 21939856</code>。</p><p>另外，如果有成千上万的数据，那么就要考虑分表，涉及运维篇知识。</p><h3 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h3><p>创建索引：<br><code>CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name, ...);</code><br>如果不加 CREATE 后面不加索引类型参数，则创建的是常规索引</p><p>查看索引：<br><code>SHOW INDEX FROM table_name;</code></p><p>删除索引：<br><code>DROP INDEX index_name ON table_name;</code></p><p>案例：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">-- name字段为姓名字段，该字段的值可能会重复，为该字段创建索引</span><br><span class="line">create index idx_user_name on tb_user(name);</span><br><span class="line">-- phone手机号字段的值非空，且唯一，为该字段创建唯一索引</span><br><span class="line">create unique index idx_user_phone on tb_user (phone);</span><br><span class="line">-- 为profession, age, status创建联合索引</span><br><span class="line">create index idx_user_pro_age_stat on tb_user(profession, age, status);</span><br><span class="line">-- 为email建立合适的索引来提升查询效率</span><br><span class="line">create index idx_user_email on tb_user(email);</span><br><span class="line"></span><br><span class="line">-- 删除索引</span><br><span class="line">drop index idx_user_email on tb_user;</span><br></pre></td></tr></table></figure><h3 id="使用规则"><a href="#使用规则" class="headerlink" title="使用规则"></a>使用规则</h3><h4 id="最左前缀法则"><a href="#最左前缀法则" class="headerlink" title="最左前缀法则"></a>最左前缀法则</h4><p>如果索引关联了多列（联合索引），要遵守最左前缀法则，最左前缀法则指的是查询从索引的最左列开始，并且不跳过索引中的列。<br>如果跳跃某一列，索引将部分失效（后面的字段索引失效）。</p><p>联合索引中，出现范围查询（&lt;, &gt;），范围查询右侧的列索引失效。可以用&gt;&#x3D;或者&lt;&#x3D;来规避索引失效问题。</p><h4 id="索引失效情况"><a href="#索引失效情况" class="headerlink" title="索引失效情况"></a>索引失效情况</h4><ol><li>在索引列上进行运算操作，索引将失效。如：<code>explain select * from tb_user where substring(phone, 10, 2) = &#39;15&#39;;</code></li><li>字符串类型字段使用时，不加引号，索引将失效。如：<code>explain select * from tb_user where phone = 17799990015;</code>，此处phone的值没有加引号</li><li>模糊查询中，如果仅仅是尾部模糊匹配，索引不会是失效；如果是头部模糊匹配，索引失效。如：<code>explain select * from tb_user where profession like &#39;%工程&#39;;</code>，前后都有 % 也会失效。</li><li>用 or 分割开的条件，如果 or 其中一个条件的列没有索引，那么涉及的索引都不会被用到。</li><li>如果 MySQL 评估使用索引比全表更慢，则不使用索引。</li></ol><h4 id="SQL-提示"><a href="#SQL-提示" class="headerlink" title="SQL 提示"></a>SQL 提示</h4><p>是优化数据库的一个重要手段，简单来说，就是在SQL语句中加入一些人为的提示来达到优化操作的目的。</p><p>例如，使用索引：<br><code>explain select * from tb_user use index(idx_user_pro) where profession=&quot;软件工程&quot;;</code><br>不使用哪个索引：<br><code>explain select * from tb_user ignore index(idx_user_pro) where profession=&quot;软件工程&quot;;</code><br>必须使用哪个索引：<br><code>explain select * from tb_user force index(idx_user_pro) where profession=&quot;软件工程&quot;;</code></p><p>use 是建议，实际使用哪个索引 MySQL 还会自己权衡运行速度去更改，force就是无论如何都强制使用该索引。</p><h4 id="覆盖索引-回表查询"><a href="#覆盖索引-回表查询" class="headerlink" title="覆盖索引&amp;回表查询"></a>覆盖索引&amp;回表查询</h4><p>尽量使用覆盖索引（查询使用了索引，并且需要返回的列，在该索引中已经全部能找到），减少 select *。</p><p>explain 中 extra 字段含义：<br><code>using index condition</code>：查找使用了索引，但是需要回表查询数据<br><code>using where; using index;</code>：查找使用了索引，但是需要的数据都在索引列中能找到，所以不需要回表查询</p><p>如果在聚集索引中直接能找到对应的行，则直接返回行数据，只需要一次查询，哪怕是select *；如果在辅助索引中找聚集索引，如<code>select id, name from xxx where name=&#39;xxx&#39;;</code>，也只需要通过辅助索引(name)查找到对应的id，返回name和name索引对应的id即可，只需要一次查询；如果是通过辅助索引查找其他字段，则需要回表查询，如<code>select id, name, gender from xxx where name=&#39;xxx&#39;;</code></p><p>所以尽量不要用<code>select *</code>，容易出现回表查询，降低效率，除非有联合索引包含了所有字段</p><p>面试题：一张表，有四个字段（id, username, password, status），由于数据量大，需要对以下SQL语句进行优化，该如何进行才是最优方案：<br><code>select id, username, password from tb_user where username=&#39;itcast&#39;;</code></p><p>解：给username和password字段建立联合索引，则不需要回表查询，直接覆盖索引</p><h4 id="前缀索引"><a href="#前缀索引" class="headerlink" title="前缀索引"></a>前缀索引</h4><p>当字段类型为字符串（varchar, text等）时，有时候需要索引很长的字符串，这会让索引变得很大，查询时，浪费大量的磁盘IO，影响查询效率，此时可以只降字符串的一部分前缀，建立索引，这样可以大大节约索引空间，从而提高索引效率。</p><p>语法：<code>create index idx_xxxx on table_name(columnn(n));</code><br>前缀长度：可以根据索引的选择性来决定，而选择性是指不重复的索引值（基数）和数据表的记录总数的比值，索引选择性越高则查询效率越高，唯一索引的选择性是1，这是最好的索引选择性，性能也是最好的。<br>求选择性公式：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">select count(distinct email) / count(*) from tb_user;</span><br><span class="line">select count(distinct substring(email, 1, 5)) / count(*) from tb_user;</span><br></pre></td></tr></table></figure><p>show index 里面的sub_part可以看到接取的长度</p><h4 id="单列索引-联合索引"><a href="#单列索引-联合索引" class="headerlink" title="单列索引&amp;联合索引"></a>单列索引&amp;联合索引</h4><p>单列索引：即一个索引只包含单个列<br>联合索引：即一个索引包含了多个列<br>在业务场景中，如果存在多个查询条件，考虑针对于查询字段建立索引时，建议建立联合索引，而非单列索引。</p><p>单列索引情况：<br><code>explain select id, phone, name from tb_user where phone = &#39;17799990010&#39; and name = &#39;韩信&#39;;</code><br>这句只会用到phone索引字段</p><h5 id="注意事项-8"><a href="#注意事项-8" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>多条件联合查询时，MySQL优化器会评估哪个字段的索引效率更高，会选择该索引完成本次查询</li></ul><h3 id="设计原则"><a href="#设计原则" class="headerlink" title="设计原则"></a>设计原则</h3><ol><li>针对于数据量较大，且查询比较频繁的表建立索引</li><li>针对于常作为查询条件（where）、排序（order by）、分组（group by）操作的字段建立索引</li><li>尽量选择区分度高的列作为索引，尽量建立唯一索引，区分度越高，使用索引的效率越高</li><li>如果是字符串类型的字段，字段长度较长，可以针对于字段的特点，建立前缀索引</li><li>尽量使用联合索引，减少单列索引，查询时，联合索引很多时候可以覆盖索引，节省存储空间，避免回表，提高查询效率</li><li>要控制索引的数量，索引并不是多多益善，索引越多，维护索引结构的代价就越大，会影响增删改的效率</li><li>如果索引列不能存储NULL值，请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时，它可以更好地确定哪个索引最有效地用于查询</li></ol><h2 id="SQL-优化"><a href="#SQL-优化" class="headerlink" title="SQL 优化"></a>SQL 优化</h2><h3 id="插入数据"><a href="#插入数据" class="headerlink" title="插入数据"></a>插入数据</h3><p>普通插入：</p><ol><li>采用批量插入（一次插入的数据不建议超过1000条）</li><li>手动提交事务</li><li>主键顺序插入</li></ol><p>大批量插入：<br>如果一次性需要插入大批量数据，使用insert语句插入性能较低，此时可以使用MySQL数据库提供的load指令插入。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># 客户端连接服务端时，加上参数 --local-infile（这一行在bash/cmd界面输入）</span><br><span class="line">mysql --local-infile -u root -p</span><br><span class="line"># 设置全局参数local_infile为1，开启从本地加载文件导入数据的开关</span><br><span class="line">set global local_infile = 1;</span><br><span class="line">select @@local_infile;</span><br><span class="line"># 执行load指令将准备好的数据，加载到表结构中</span><br><span class="line">load data local infile &#x27;/root/sql1.log&#x27; into table &#x27;tb_user&#x27; fields terminated by &#x27;,&#x27; lines terminated by &#x27;\n&#x27;;</span><br></pre></td></tr></table></figure><h3 id="主键优化"><a href="#主键优化" class="headerlink" title="主键优化"></a>主键优化</h3><p>数据组织方式：在InnoDB存储引擎中，表数据都是根据主键顺序组织存放的，这种存储方式的表称为索引组织表（Index organized table, IOT）</p><p>页分裂：页可以为空，也可以填充一般，也可以填充100%，每个页包含了2-N行数据（如果一行数据过大，会行溢出），根据主键排列。<br>页合并：当删除一行记录时，实际上记录并没有被物理删除，只是记录被标记（flaged）为删除并且它的空间变得允许被其他记录声明使用。当页中删除的记录到达 MERGE_THRESHOLD（默认为页的50%），InnoDB会开始寻找最靠近的页（前后）看看是否可以将这两个页合并以优化空间使用。</p><p>MERGE_THRESHOLD：合并页的阈值，可以自己设置，在创建表或创建索引时指定</p><blockquote><p>文字说明不够清晰明了，具体可以看视频里的PPT演示过程：<a href="https://www.bilibili.com/video/BV1Kr4y1i7ru?p=90">https://www.bilibili.com/video/BV1Kr4y1i7ru?p=90</a></p></blockquote><p>主键设计原则：</p><ul><li>满足业务需求的情况下，尽量降低主键的长度</li><li>插入数据时，尽量选择顺序插入，选择使用 AUTO_INCREMENT 自增主键</li><li>尽量不要使用 UUID 做主键或者是其他的自然主键，如身份证号</li><li>业务操作时，避免对主键的修改</li></ul><h3 id="order-by优化"><a href="#order-by优化" class="headerlink" title="order by优化"></a>order by优化</h3><ol><li>Using filesort：通过表的索引或全表扫描，读取满足条件的数据行，然后在排序缓冲区 sort buffer 中完成排序操作，所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序</li><li>Using index：通过有序索引顺序扫描直接返回有序数据，这种情况即为 using index，不需要额外排序，操作效率高</li></ol><p>如果order by字段全部使用升序排序或者降序排序，则都会走索引，但是如果一个字段升序排序，另一个字段降序排序，则不会走索引，explain的extra信息显示的是<code>Using index, Using filesort</code>，如果要优化掉Using filesort，则需要另外再创建一个索引，如：<code>create index idx_user_age_phone_ad on tb_user(age asc, phone desc);</code>，此时使用<code>select id, age, phone from tb_user order by age asc, phone desc;</code>会全部走索引</p><p>总结：</p><ul><li>根据排序字段建立合适的索引，多字段排序时，也遵循最左前缀法则</li><li>尽量使用覆盖索引</li><li>多字段排序，一个升序一个降序，此时需要注意联合索引在创建时的规则（ASC&#x2F;DESC）</li><li>如果不可避免出现filesort，大数据量排序时，可以适当增大排序缓冲区大小 sort_buffer_size（默认256k）</li></ul><h3 id="group-by优化"><a href="#group-by优化" class="headerlink" title="group by优化"></a>group by优化</h3><ul><li>在分组操作时，可以通过索引来提高效率</li><li>分组操作时，索引的使用也是满足最左前缀法则的</li></ul><p>如索引为<code>idx_user_pro_age_stat</code>，则句式可以是<code>select ... where profession order by age</code>，这样也符合最左前缀法则</p><h3 id="limit优化"><a href="#limit优化" class="headerlink" title="limit优化"></a>limit优化</h3><p>常见的问题如<code>limit 2000000, 10</code>，此时需要 MySQL 排序前2000000条记录，但仅仅返回2000000 - 2000010的记录，其他记录丢弃，查询排序的代价非常大。<br>优化方案：一般分页查询时，通过创建覆盖索引能够比较好地提高性能，可以通过覆盖索引加子查询形式进行优化</p><p>例如：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">-- 此语句耗时很长</span><br><span class="line">select * from tb_sku limit 9000000, 10;</span><br><span class="line">-- 通过覆盖索引加快速度，直接通过主键索引进行排序及查询</span><br><span class="line">select id from tb_sku order by id limit 9000000, 10;</span><br><span class="line">-- 下面的语句是错误的，因为 MySQL 不支持 in 里面使用 limit</span><br><span class="line">-- select * from tb_sku where id in (select id from tb_sku order by id limit 9000000, 10);</span><br><span class="line">-- 通过连表查询即可实现第一句的效果，并且能达到第二句的速度</span><br><span class="line">select * from tb_sku as s, (select id from tb_sku order by id limit 9000000, 10) as a where s.id = a.id;</span><br></pre></td></tr></table></figure><h3 id="count优化"><a href="#count优化" class="headerlink" title="count优化"></a>count优化</h3><p>MyISAM 引擎把一个表的总行数存在了磁盘上，因此执行 count(*) 的时候会直接返回这个数，效率很高（前提是不适用where）；<br>InnoDB 在执行 count(*) 时，需要把数据一行一行地从引擎里面读出来，然后累计计数。<br>优化方案：自己计数，如创建key-value表存储在内存或硬盘，或者是用redis</p><p>count的几种用法：</p><ul><li>如果count函数的参数（count里面写的那个字段）不是NULL（字段值不为NULL），累计值就加一，最后返回累计值</li><li>用法：count(*)、count(主键)、count(字段)、count(1)</li><li>count(主键)跟count(*)一样，因为主键不能为空；count(字段)只计算字段值不为NULL的行；count(1)引擎会为每行添加一个1，然后就count这个1，返回结果也跟count(*)一样；count(null)返回0</li></ul><p>各种用法的性能：</p><ul><li>count(主键)：InnoDB引擎会遍历整张表，把每行的主键id值都取出来，返回给服务层，服务层拿到主键后，直接按行进行累加（主键不可能为空）</li><li>count(字段)：没有not null约束的话，InnoDB引擎会遍历整张表把每一行的字段值都取出来，返回给服务层，服务层判断是否为null，不为null，计数累加；有not null约束的话，InnoDB引擎会遍历整张表把每一行的字段值都取出来，返回给服务层，直接按行进行累加</li><li>count(1)：InnoDB 引擎遍历整张表，但不取值。服务层对于返回的每一层，放一个数字 1 进去，直接按行进行累加</li><li>count(*)：InnoDB 引擎并不会把全部字段取出来，而是专门做了优化，不取值，服务层直接按行进行累加</li></ul><p>按效率排序：count(字段) &lt; count(主键) &lt; count(1) &lt; count(*)，所以尽量使用 count(*)</p><h3 id="update优化（避免行锁升级为表锁）"><a href="#update优化（避免行锁升级为表锁）" class="headerlink" title="update优化（避免行锁升级为表锁）"></a>update优化（避免行锁升级为表锁）</h3><p>InnoDB 的行锁是针对索引加的锁，不是针对记录加的锁，并且该索引不能失效，否则会从行锁升级为表锁。</p><p>如以下两条语句：<br><code>update student set no = &#39;123&#39; where id = 1;</code>，这句由于id有主键索引，所以只会锁这一行；<br><code>update student set no = &#39;123&#39; where name = &#39;test&#39;;</code>，这句由于name没有索引，所以会把整张表都锁住进行数据更新，解决方法是给name字段添加索引</p><h1 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h1><h2 id="整型"><a href="#整型" class="headerlink" title="整型"></a>整型</h2><table><thead><tr><th>类型名称</th><th>取值范围</th><th>大小</th></tr></thead><tbody><tr><td>TINYINT</td><td>-128〜127</td><td>1个字节</td></tr><tr><td>SMALLINT</td><td>-32768〜32767</td><td>2个宇节</td></tr><tr><td>MEDIUMINT</td><td>-8388608〜8388607</td><td>3个字节</td></tr><tr><td>INT (INTEGHR)</td><td>-2147483648〜2147483647</td><td>4个字节</td></tr><tr><td>BIGINT</td><td>-9223372036854775808〜9223372036854775807</td><td>8个字节</td></tr></tbody></table><p>无符号在数据类型后加 unsigned 关键字。</p><h2 id="浮点型"><a href="#浮点型" class="headerlink" title="浮点型"></a>浮点型</h2><table><thead><tr><th>类型名称</th><th>说明</th><th>存储需求</th></tr></thead><tbody><tr><td>FLOAT</td><td>单精度浮点数</td><td>4 个字节</td></tr><tr><td>DOUBLE</td><td>双精度浮点数</td><td>8 个字节</td></tr><tr><td>DECIMAL (M, D)，DEC</td><td>压缩的“严格”定点数</td><td>M+2 个字节</td></tr></tbody></table><h2 id="日期和时间"><a href="#日期和时间" class="headerlink" title="日期和时间"></a>日期和时间</h2><table><thead><tr><th>类型名称</th><th>日期格式</th><th>日期范围</th><th>存储需求</th></tr></thead><tbody><tr><td>YEAR</td><td>YYYY</td><td>1901 ~ 2155</td><td>1 个字节</td></tr><tr><td>TIME</td><td>HH:MM:SS</td><td>-838:59:59 ~ 838:59:59</td><td>3 个字节</td></tr><tr><td>DATE</td><td>YYYY-MM-DD</td><td>1000-01-01 ~ 9999-12-3</td><td>3 个字节</td></tr><tr><td>DATETIME</td><td>YYYY-MM-DD HH:MM:SS</td><td>1000-01-01 00:00:00 ~ 9999-12-31 23:59:59</td><td>8 个字节</td></tr><tr><td>TIMESTAMP</td><td>YYYY-MM-DD HH:MM:SS</td><td>1980-01-01 00:00:01 UTC ~ 2040-01-19 03:14:07 UTC</td><td>4 个字节</td></tr></tbody></table><h2 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h2><table><thead><tr><th>类型名称</th><th>说明</th><th>存储需求</th></tr></thead><tbody><tr><td>CHAR(M)</td><td>固定长度非二进制字符串</td><td>M 字节，1&lt;&#x3D;M&lt;&#x3D;255</td></tr><tr><td>VARCHAR(M)</td><td>变长非二进制字符串</td><td>L+1字节，在此，L&lt; &#x3D; M和 1&lt;&#x3D;M&lt;&#x3D;255</td></tr><tr><td>TINYTEXT</td><td>非常小的非二进制字符串</td><td>L+1字节，在此，L&lt;2^8</td></tr><tr><td>TEXT</td><td>小的非二进制字符串</td><td>L+2字节，在此，L&lt;2^16</td></tr><tr><td>MEDIUMTEXT</td><td>中等大小的非二进制字符串</td><td>L+3字节，在此，L&lt;2^24</td></tr><tr><td>LONGTEXT</td><td>大的非二进制字符串</td><td>L+4字节，在此，L&lt;2^32</td></tr><tr><td>ENUM</td><td>枚举类型，只能有一个枚举字符串值</td><td>1或2个字节，取决于枚举值的数目 (最大值为65535)</td></tr><tr><td>SET</td><td>一个设置，字符串对象可以有零个或 多个SET成员</td><td>1、2、3、4或8个字节，取决于集合 成员的数量（最多64个成员）</td></tr></tbody></table><h2 id="二进制类型"><a href="#二进制类型" class="headerlink" title="二进制类型"></a>二进制类型</h2><table><thead><tr><th>类型名称</th><th>说明</th><th>存储需求</th></tr></thead><tbody><tr><td>BIT(M)</td><td>位字段类型</td><td>大约 (M+7)&#x2F;8 字节</td></tr><tr><td>BINARY(M)</td><td>固定长度二进制字符串</td><td>M 字节</td></tr><tr><td>VARBINARY (M)</td><td>可变长度二进制字符串</td><td>M+1 字节</td></tr><tr><td>TINYBLOB (M)</td><td>非常小的BLOB</td><td>L+1 字节，在此，L&lt;2^8</td></tr><tr><td>BLOB (M)</td><td>小 BLOB</td><td>L+2 字节，在此，L&lt;2^16</td></tr><tr><td>MEDIUMBLOB (M)</td><td>中等大小的BLOB</td><td>L+3 字节，在此，L&lt;2^24</td></tr><tr><td>LONGBLOB (M)</td><td>非常大的BLOB</td><td>L+4 字节，在此，L&lt;2^32</td></tr></tbody></table><h1 id="权限一览表"><a href="#权限一览表" class="headerlink" title="权限一览表"></a>权限一览表</h1><blockquote><p>具体权限的作用详见<a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html" title="官方文档">官方文档</a></p></blockquote><p>GRANT 和 REVOKE 允许的静态权限</p><table><thead><tr><th align="left">Privilege</th><th align="left">Grant Table Column</th><th align="left">Context</th></tr></thead><tbody><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_all"><code>ALL [PRIVILEGES]</code></a></td><td align="left">Synonym for “all privileges”</td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_alter"><code>ALTER</code></a></td><td align="left"><code>Alter_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_alter-routine"><code>ALTER ROUTINE</code></a></td><td align="left"><code>Alter_routine_priv</code></td><td align="left">Stored routines</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create"><code>CREATE</code></a></td><td align="left"><code>Create_priv</code></td><td align="left">Databases, tables, or indexes</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-role"><code>CREATE ROLE</code></a></td><td align="left"><code>Create_role_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-routine"><code>CREATE ROUTINE</code></a></td><td align="left"><code>Create_routine_priv</code></td><td align="left">Stored routines</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-tablespace"><code>CREATE TABLESPACE</code></a></td><td align="left"><code>Create_tablespace_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-temporary-tables"><code>CREATE TEMPORARY TABLES</code></a></td><td align="left"><code>Create_tmp_table_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-user"><code>CREATE USER</code></a></td><td align="left"><code>Create_user_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-view"><code>CREATE VIEW</code></a></td><td align="left"><code>Create_view_priv</code></td><td align="left">Views</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_delete"><code>DELETE</code></a></td><td align="left"><code>Delete_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_drop"><code>DROP</code></a></td><td align="left"><code>Drop_priv</code></td><td align="left">Databases, tables, or views</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_drop-role"><code>DROP ROLE</code></a></td><td align="left"><code>Drop_role_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_event"><code>EVENT</code></a></td><td align="left"><code>Event_priv</code></td><td align="left">Databases</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_execute"><code>EXECUTE</code></a></td><td align="left"><code>Execute_priv</code></td><td align="left">Stored routines</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_file"><code>FILE</code></a></td><td align="left"><code>File_priv</code></td><td align="left">File access on server host</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_grant-option"><code>GRANT OPTION</code></a></td><td align="left"><code>Grant_priv</code></td><td align="left">Databases, tables, or stored routines</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_index"><code>INDEX</code></a></td><td align="left"><code>Index_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_insert"><code>INSERT</code></a></td><td align="left"><code>Insert_priv</code></td><td align="left">Tables or columns</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_lock-tables"><code>LOCK TABLES</code></a></td><td align="left"><code>Lock_tables_priv</code></td><td align="left">Databases</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_process"><code>PROCESS</code></a></td><td align="left"><code>Process_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_proxy"><code>PROXY</code></a></td><td align="left">See <code>proxies_priv</code> table</td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_references"><code>REFERENCES</code></a></td><td align="left"><code>References_priv</code></td><td align="left">Databases or tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_reload"><code>RELOAD</code></a></td><td align="left"><code>Reload_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_replication-client"><code>REPLICATION CLIENT</code></a></td><td align="left"><code>Repl_client_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_replication-slave"><code>REPLICATION SLAVE</code></a></td><td align="left"><code>Repl_slave_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_select"><code>SELECT</code></a></td><td align="left"><code>Select_priv</code></td><td align="left">Tables or columns</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_show-databases"><code>SHOW DATABASES</code></a></td><td align="left"><code>Show_db_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_show-view"><code>SHOW VIEW</code></a></td><td align="left"><code>Show_view_priv</code></td><td align="left">Views</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_shutdown"><code>SHUTDOWN</code></a></td><td align="left"><code>Shutdown_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_super"><code>SUPER</code></a></td><td align="left"><code>Super_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_trigger"><code>TRIGGER</code></a></td><td align="left"><code>Trigger_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_update"><code>UPDATE</code></a></td><td align="left"><code>Update_priv</code></td><td align="left">Tables or columns</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_usage"><code>USAGE</code></a></td><td align="left">Synonym for “no privileges”</td><td align="left">Server administration</td></tr></tbody></table><p>GRANT 和 REVOKE 允许的动态权限</p><table><thead><tr><th align="left">Privilege</th><th align="left">Context</th></tr></thead><tbody><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_application-password-admin"><code>APPLICATION_PASSWORD_ADMIN</code></a></td><td align="left">Dual password administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_audit-abort-exempt"><code>AUDIT_ABORT_EXEMPT</code></a></td><td align="left">Allow queries blocked by audit log filter</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_audit-admin"><code>AUDIT_ADMIN</code></a></td><td align="left">Audit log administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_authentication-policy-admin"><code>AUTHENTICATION_POLICY_ADMIN</code></a></td><td align="left">Authentication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_backup-admin"><code>BACKUP_ADMIN</code></a></td><td align="left">Backup administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_binlog-admin"><code>BINLOG_ADMIN</code></a></td><td align="left">Backup and Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_binlog-encryption-admin"><code>BINLOG_ENCRYPTION_ADMIN</code></a></td><td align="left">Backup and Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_clone-admin"><code>CLONE_ADMIN</code></a></td><td align="left">Clone administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_connection-admin"><code>CONNECTION_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_encryption-key-admin"><code>ENCRYPTION_KEY_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_firewall-admin"><code>FIREWALL_ADMIN</code></a></td><td align="left">Firewall administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_firewall-exempt"><code>FIREWALL_EXEMPT</code></a></td><td align="left">Firewall administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_firewall-user"><code>FIREWALL_USER</code></a></td><td align="left">Firewall administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_flush-optimizer-costs"><code>FLUSH_OPTIMIZER_COSTS</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_flush-status"><code>FLUSH_STATUS</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_flush-tables"><code>FLUSH_TABLES</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_flush-user-resources"><code>FLUSH_USER_RESOURCES</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_group-replication-admin"><code>GROUP_REPLICATION_ADMIN</code></a></td><td align="left">Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_group-replication-stream"><code>GROUP_REPLICATION_STREAM</code></a></td><td align="left">Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_innodb-redo-log-archive"><code>INNODB_REDO_LOG_ARCHIVE</code></a></td><td align="left">Redo log archiving administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_ndb-stored-user"><code>NDB_STORED_USER</code></a></td><td align="left">NDB Cluster</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_passwordless-user-admin"><code>PASSWORDLESS_USER_ADMIN</code></a></td><td align="left">Authentication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_persist-ro-variables-admin"><code>PERSIST_RO_VARIABLES_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_replication-applier"><code>REPLICATION_APPLIER</code></a></td><td align="left"><code>PRIVILEGE_CHECKS_USER</code> for a replication channel</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_replication-slave-admin"><code>REPLICATION_SLAVE_ADMIN</code></a></td><td align="left">Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_resource-group-admin"><code>RESOURCE_GROUP_ADMIN</code></a></td><td align="left">Resource group administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_resource-group-user"><code>RESOURCE_GROUP_USER</code></a></td><td align="left">Resource group administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_role-admin"><code>ROLE_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_session-variables-admin"><code>SESSION_VARIABLES_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_set-user-id"><code>SET_USER_ID</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_show-routine"><code>SHOW_ROUTINE</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_system-user"><code>SYSTEM_USER</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_system-variables-admin"><code>SYSTEM_VARIABLES_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_table-encryption-admin"><code>TABLE_ENCRYPTION_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_version-token-admin"><code>VERSION_TOKEN_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_xa-recover-admin"><code>XA_RECOVER_ADMIN</code></a></td><td align="left">Server administration</td></tr></tbody></table><h1 id="图形化界面工具"><a href="#图形化界面工具" class="headerlink" title="图形化界面工具"></a>图形化界面工具</h1><ul><li>Workbench(免费): <a href="http://dev.mysql.com/downloads/workbench/">http://dev.mysql.com/downloads/workbench/</a></li><li>navicat(收费，试用版30天): <a href="https://www.navicat.com/en/download/navicat-for-mysql">https://www.navicat.com/en/download/navicat-for-mysql</a></li><li>Sequel Pro(开源免费，仅支持Mac OS): <a href="http://www.sequelpro.com/">http://www.sequelpro.com/</a></li><li>HeidiSQL(免费): <a href="http://www.heidisql.com/">http://www.heidisql.com/</a></li><li>phpMyAdmin(免费): <a href="https://www.phpmyadmin.net/">https://www.phpmyadmin.net/</a></li><li>SQLyog: <a href="https://sqlyog.en.softonic.com/">https://sqlyog.en.softonic.com/</a></li></ul><h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><h1 id="小技巧"><a href="#小技巧" class="headerlink" title="小技巧"></a>小技巧</h1><ol><li>在SQL语句之后加上<code>\G</code>会将结果的表格形式转换成行文本形式</li><li>查看Mysql数据库占用空间：</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">SELECT table_schema &quot;Database Name&quot;</span><br><span class="line">     , SUM(data_length + index_length) / (1024 * 1024) &quot;Database Size in MB&quot;</span><br><span class="line">FROM information_schema.TABLES</span><br><span class="line">GROUP BY table_schema;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;基础篇&quot;&gt;&lt;a href=&quot;#基础篇&quot; class=&quot;headerlink&quot; title=&quot;基础篇&quot;&gt;&lt;/a&gt;基础篇&lt;/h1&gt;&lt;h2 id=&quot;通用语法及分类&quot;&gt;&lt;a href=&quot;#通用语法及分类&quot; class=&quot;headerlink&quot; title=&quot;通用语法及分类</summary>
      
    
    
    
    <category term="编程学习" scheme="https://cq230.github.io/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="数据库" scheme="https://cq230.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    <category term="MySQL" scheme="https://cq230.github.io/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>Excel使用技巧</title>
    <link href="https://cq230.github.io/posts/b8a31387.html"/>
    <id>https://cq230.github.io/posts/b8a31387.html</id>
    <published>2024-11-14T11:43:09.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、Excel-常用操作"><a href="#一、Excel-常用操作" class="headerlink" title="一、Excel 常用操作"></a>一、Excel 常用操作</h1><table><thead><tr><th align="center">功能</th><th align="center">操作</th></tr></thead><tbody><tr><td align="center">启用或关闭筛选功能</td><td align="center">Ctrl + Shift + L</td></tr><tr><td align="center">移动列</td><td align="center">选中全列，按住 Shift 键，鼠标在列边缘拖动至目标列</td></tr><tr><td align="center">快速到最后一行</td><td align="center">选中一个单元格，鼠标双击单元格下边框，最上一行同理</td></tr><tr><td align="center">删除所有右边列</td><td align="center">选中目标列，Ctrl + Shift + → 选中并手动删除，删除全部下边行同理</td></tr><tr><td align="center">复制填充行</td><td align="center">选中目标单元格，Ctrl + R</td></tr><tr><td align="center">复制填充列</td><td align="center">选中目标单元格，Ctrl + D</td></tr><tr><td align="center">填充序号</td><td align="center">单元格填 1 ，选中单元格鼠标点住单元格右下角拖动</td></tr><tr><td align="center">复制填充合并单元格</td><td align="center">选中目标单元格，在上方填入值，Ctrl + Enter</td></tr><tr><td align="center">yyyy&#x2F;m&#x2F;d -&gt; 星期几</td><td align="center">选中单元格，设置单元格格式 - 数字 - 自定义 - 填 aaaa - 确定</td></tr><tr><td align="center">yyyy&#x2F;m&#x2F;d -&gt; 周几</td><td align="center">选中单元格，设置单元格格式 - 数字 - 自定义 - 填 周aaa - 确定</td></tr><tr><td align="center">文本转日期格式</td><td align="center">选中单元格，数据 - 分列 - 固定宽度 - 下一步 - 下一步 - 日期 - 完成</td></tr><tr><td align="center">数字加上单位（不影响计算）</td><td align="center">选中单元格，设置单元格格式 - 数字 - 自定义 - 在对应格式后面加单位 - 确定</td></tr></tbody></table><p><strong>Tips:</strong></p><p>尽量不要用 Excel 中的日期格式存储日期数据，用字符串格式。</p><h1 id="二、EDATE-DATE"><a href="#二、EDATE-DATE" class="headerlink" title="二、EDATE &amp; DATE"></a>二、EDATE &amp; DATE</h1><p>在 Excel 中，<code>EDATE</code> 和 <code>DATE</code> 是两个常用的日期函数，它们具有不同的功能。下面是对它们的详细解释：</p><h2 id="1-EDATE-函数"><a href="#1-EDATE-函数" class="headerlink" title="1. EDATE 函数"></a>1. <code>EDATE</code> 函数</h2><p><code>EDATE</code> 函数用于返回从给定日期起，按月数偏移的日期。</p><h3 id="语法："><a href="#语法：" class="headerlink" title="语法："></a>语法：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">EDATE</span>(start_date, months)</span><br></pre></td></tr></table></figure><ul><li><strong>start_date</strong>：起始日期，可以是一个日期值或一个包含日期的单元格。</li><li><strong>months</strong>：偏移的月份数。如果是正数，返回的是未来的日期；如果是负数，返回的是过去的日期。</li></ul><h3 id="示例："><a href="#示例：" class="headerlink" title="示例："></a>示例：</h3><p>假设：</p><ul><li>A1 单元格包含日期 <code>2024年1月1日</code>，你想要知道从这个日期起，<strong>3个月之后</strong>是何时。</li></ul><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">EDATE</span>(A1, <span class="number">3</span>)</span><br></pre></td></tr></table></figure><p>结果：<code>2024年4月1日</code>，因为 3 个月后是 2024年4月1日。</p><p>如果想要返回 <strong>3个月之前</strong>的日期，可以使用负数：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">EDATE</span>(A1, -<span class="number">3</span>)</span><br></pre></td></tr></table></figure><p>结果：<code>2023年10月1日</code>。</p><h3 id="常见应用："><a href="#常见应用：" class="headerlink" title="常见应用："></a>常见应用：</h3><ul><li>用于计算发票到期日（例如，30天、60天、90天后的日期）。</li><li>用于按月份推算业务周期或贷款期限。</li></ul><hr><h2 id="2-DATE-函数"><a href="#2-DATE-函数" class="headerlink" title="2. DATE 函数"></a>2. <code>DATE</code> 函数</h2><p><code>DATE</code> 函数用于根据指定的年份、月份和日期来返回一个日期值。</p><h3 id="语法：-1"><a href="#语法：-1" class="headerlink" title="语法："></a>语法：</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">DATE</span>(<span class="keyword">year</span>, <span class="keyword">month</span>, <span class="keyword">day</span>)</span><br></pre></td></tr></table></figure><ul><li><strong>year</strong>：年份，通常是一个四位数的年份（例如，2024）。</li><li><strong>month</strong>：月份，可以是 1 到 12 的数字，或使用负数和大于 12 的数来跨年计算月份。</li><li><strong>day</strong>：日期，表示该月份中的具体日期，通常是 1 到 31。</li></ul><h3 id="示例：-1"><a href="#示例：-1" class="headerlink" title="示例："></a>示例：</h3><p>假设：</p><ul><li>你想根据年份 <code>2024</code>、月份 <code>7</code>、日期 <code>31</code> 来生成一个日期。</li></ul><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">DATE</span>(<span class="number">2024</span>, <span class="number">7</span>, <span class="number">31</span>)</span><br></pre></td></tr></table></figure><p>结果：<code>2024年7月31日</code>。</p><h3 id="特殊情况："><a href="#特殊情况：" class="headerlink" title="特殊情况："></a>特殊情况：</h3><ul><li>如果 <strong>month</strong> 超过 12 或为负数，Excel 会自动处理年份的溢出。例如：</li></ul><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">DATE</span>(<span class="number">2024</span>, <span class="number">14</span>, <span class="number">31</span>)</span><br></pre></td></tr></table></figure><p>结果：<code>2025年2月28日</code>，因为 <code>14</code> 月是 2025年的第2个月（2月）。</p><ul><li>如果 <strong>day</strong> 超过了该月的最大日期，Excel 会自动调整日期。例如：</li></ul><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">DATE</span>(<span class="number">2024</span>, <span class="number">2</span>, <span class="number">30</span>)</span><br></pre></td></tr></table></figure><p>结果：<code>2024年3月1日</code>，因为 2024年2月只有29天，30天会被自动调整为 3月1日。</p><h3 id="常见应用：-1"><a href="#常见应用：-1" class="headerlink" title="常见应用："></a>常见应用：</h3><ul><li>用于创建特定日期，或者从年、月、日等不同数据源构造日期。</li><li>用于日期的计算和比较，尤其是在涉及多个字段的日期时。</li></ul><hr><h2 id="3-总结对比："><a href="#3-总结对比：" class="headerlink" title="3.总结对比："></a>3.总结对比：</h2><table><thead><tr><th><strong>功能</strong></th><th><strong>EDATE</strong></th><th><strong>DATE</strong></th></tr></thead><tbody><tr><td><strong>功能描述</strong></td><td>按指定的月份偏移返回日期</td><td>根据年、月、日构造一个日期</td></tr><tr><td><strong>用法</strong></td><td>用于计算偏移的日期（加减月数）</td><td>用于根据年份、月份和日期创建日期</td></tr><tr><td><strong>参数</strong></td><td>起始日期、月份偏移（正负整数）</td><td>年份、月份、日期</td></tr><tr><td><strong>示例</strong></td><td><code>=EDATE(A1, 3)</code>（当前日期加3个月）</td><td><code>=DATE(2024, 7, 31)</code>（2024年7月31日）</td></tr><tr><td><strong>应用场景</strong></td><td>计算未来或过去的日期（按月）</td><td>根据年、月、日创建特定日期</td></tr></tbody></table><p>这两个函数都是处理日期的常用工具，选择哪一个取决于具体需求：</p><ul><li>如果你需要按月推算日期，使用 <code>EDATE</code>。</li><li>如果你需要根据年、月、日来生成特定日期，使用 <code>DATE</code>。</li><li><strong>按月推算日期</strong>：使用 <code>EDATE</code> 或 <code>EOMONTH</code>。</li><li><strong>按天数推算日期</strong>：使用 <code>WORKDAY</code> 或 <code>WORKDAY.INTL</code>。</li><li><strong>计算日期差异</strong>：使用 <code>DAYS</code> 或 <code>YEARFRAC</code></li></ul><h1 id="三、SUM-SUBTOTAL"><a href="#三、SUM-SUBTOTAL" class="headerlink" title="三、SUM &amp; SUBTOTAL"></a>三、SUM &amp; SUBTOTAL</h1><p>在 Excel 中，<code>SUM</code> 和 <code>SUBTOTAL</code> 都是常用的数学函数，用于对一组数字进行求和。它们的主要区别在于如何处理数据范围和隐藏行。以下是详细的比较和说明：</p><h2 id="1-SUM-函数"><a href="#1-SUM-函数" class="headerlink" title="1. SUM 函数"></a>1. <code>SUM</code> 函数</h2><p><code>SUM</code> 是 Excel 中最基本的求和函数，旨在对指定的数字范围进行简单的求和。</p><h3 id="语法：-2"><a href="#语法：-2" class="headerlink" title="语法："></a>语法：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">SUM</span>(number1, [number2], ...)</span><br></pre></td></tr></table></figure><ul><li>**number1, number2, …**：要相加的数字或单元格范围。</li></ul><h3 id="示例：-2"><a href="#示例：-2" class="headerlink" title="示例："></a>示例：</h3><p>假设你有一个数据范围 A1:A5，其中包含数字 10, 20, 30, 40, 和 50。</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">SUM</span>(A1:A5)</span><br></pre></td></tr></table></figure><p>结果：<code>150</code>（10 + 20 + 30 + 40 + 50）</p><h3 id="特点："><a href="#特点：" class="headerlink" title="特点："></a>特点：</h3><ul><li><code>SUM</code> 函数会 <strong>对所有可见和隐藏的单元格</strong> 进行求和，<strong>即使这些行被隐藏（如通过过滤或手动隐藏）</strong>，也会将其包括在求和中。</li><li>它适用于所有需要对一系列数值进行加总的情况。</li></ul><h2 id="2-SUBTOTAL-函数"><a href="#2-SUBTOTAL-函数" class="headerlink" title="2. SUBTOTAL 函数"></a>2. <code>SUBTOTAL</code> 函数</h2><p><code>SUBTOTAL</code> 函数更为灵活，除了可以执行求和外，还能进行其他统计计算。<code>SUBTOTAL</code> 的一个显著特性是：<strong>它可以忽略被隐藏的行</strong>，如果你使用自动筛选（Filter）或手动隐藏了行时，<code>SUBTOTAL</code> 只对可见的数据进行操作。</p><h3 id="语法：-3"><a href="#语法：-3" class="headerlink" title="语法："></a>语法：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">SUBTOTAL</span>(function_num, ref1, [ref2], ...)</span><br></pre></td></tr></table></figure><ul><li><strong>function_num</strong>：一个数字，指定要使用的汇总函数。对于求和，<code>function_num</code> 应设置为 <strong>9</strong> 或 <strong>109</strong>。<ul><li><strong>9</strong>：表示求和，<strong>包括隐藏的行</strong>。</li><li><strong>109</strong>：表示求和，<strong>忽略隐藏的行</strong>。</li></ul></li><li>**ref1, ref2, …**：要进行汇总的范围，可以是单元格范围或多个范围。</li></ul><h3 id="示例：-3"><a href="#示例：-3" class="headerlink" title="示例："></a>示例：</h3><p>假设你有一个数据范围 A1:A5，其中包含数字 10, 20, 30, 40, 和 50，并且 A3 行被隐藏。</p><p>如果你使用 <code>SUBTOTAL</code> 来计算求和并忽略隐藏的行（例如，使用过滤或手动隐藏行）：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">SUBTOTAL</span>(<span class="number">109</span>, A1:A5)</span><br></pre></td></tr></table></figure><ul><li>结果将是 <strong>120</strong>（10 + 20 + 40 + 50），因为第 3 行（数字 30）被隐藏，<code>SUBTOTAL</code> 忽略了这行。</li></ul><p>如果你使用 9（表示包括隐藏行的求和）：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">SUBTOTAL</span>(<span class="number">9</span>, A1:A5)</span><br></pre></td></tr></table></figure><ul><li>结果将是 <strong>150</strong>（10 + 20 + 30 + 40 + 50），即使第 3 行被隐藏，<code>SUM</code> 也会计算所有行。</li></ul><h3 id="常见的-function-num-参数："><a href="#常见的-function-num-参数：" class="headerlink" title="常见的 function_num 参数："></a>常见的 <code>function_num</code> 参数：</h3><ul><li><strong>1</strong>：<code>AVERAGE</code>（平均值）</li><li><strong>2</strong>：<code>COUNT</code>（计数）</li><li><strong>3</strong>：<code>COUNTA</code>（非空单元格计数）</li><li><strong>9</strong>：<code>SUM</code>（求和）</li><li><strong>10</strong>：<code>MAX</code>（最大值）</li><li><strong>11</strong>：<code>MIN</code>（最小值）</li></ul><h3 id="示例：不同功能的-function-num"><a href="#示例：不同功能的-function-num" class="headerlink" title="示例：不同功能的 function_num"></a>示例：不同功能的 <code>function_num</code></h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">SUBTOTAL</span>(<span class="number">9</span>, A1:A5)    &#x27; 求和，包括隐藏行</span><br><span class="line">=<span class="built_in">SUBTOTAL</span>(<span class="number">109</span>, A1:A5)   &#x27; 求和，忽略隐藏行</span><br><span class="line">=<span class="built_in">SUBTOTAL</span>(<span class="number">1</span>, A1:A5)     &#x27; 平均值，包括隐藏行</span><br><span class="line">=<span class="built_in">SUBTOTAL</span>(<span class="number">101</span>, A1:A5)   &#x27; 平均值，忽略隐藏行</span><br></pre></td></tr></table></figure><h2 id="3-SUM-与-SUBTOTAL-的区别"><a href="#3-SUM-与-SUBTOTAL-的区别" class="headerlink" title="3. SUM 与 SUBTOTAL 的区别"></a>3. <code>SUM</code> 与 <code>SUBTOTAL</code> 的区别</h2><table><thead><tr><th><strong>特性</strong></th><th><strong>SUM</strong></th><th><strong>SUBTOTAL</strong></th></tr></thead><tbody><tr><td><strong>功能</strong></td><td>求和指定范围内所有单元格的值</td><td>既可以求和，又可以进行多种统计（如平均、计数、最大值等）</td></tr><tr><td><strong>忽略隐藏行</strong></td><td>不会忽略隐藏的行</td><td>可以根据 <code>function_num</code> 的选择，选择忽略或不忽略隐藏行</td></tr><tr><td><strong>适用场景</strong></td><td>需要对所有数据进行求和（包括隐藏数据）</td><td>需要对可见数据进行求和，或其他统计操作，尤其是当使用自动筛选或手动隐藏行时</td></tr><tr><td><strong>常见用途</strong></td><td>对一组数值进行简单求和</td><td>在有过滤数据时，统计可见数据的求和或其他汇总操作</td></tr><tr><td><strong>函数的灵活性</strong></td><td>只有求和功能</td><td>可根据 <code>function_num</code> 实现多种统计功能（如平均值、计数、最大最小值等）</td></tr></tbody></table><h2 id="4-SUBTOTAL-更高效的使用场景："><a href="#4-SUBTOTAL-更高效的使用场景：" class="headerlink" title="4. SUBTOTAL 更高效的使用场景："></a>4. <code>SUBTOTAL</code> 更高效的使用场景：</h2><ul><li>当你有一个通过 <strong>筛选</strong> 或 <strong>手动隐藏</strong> 行的表格时，<code>SUBTOTAL</code> 会忽略隐藏的行，这对于避免错误的计算结果很有帮助。</li><li>例如，当你对销售数据进行筛选时，只希望对筛选出的（可见的）数据进行求和，而不包括被隐藏的数据行。这时，使用 <code>SUBTOTAL</code> 比使用 <code>SUM</code> 更为合适。</li></ul><h2 id="5-总结："><a href="#5-总结：" class="headerlink" title="5.总结："></a>5.总结：</h2><ul><li>使用 <code>SUM</code> 时，它会对所有数据（包括隐藏的行）进行求和，适用于需要对整个数据范围求和的场景。</li><li>使用 <code>SUBTOTAL</code> 时，你可以选择是否忽略隐藏的行，并且它支持更多的统计功能，特别适用于有筛选或隐藏行的情况。</li></ul><h1 id="四、SUMIF-SUMIFS"><a href="#四、SUMIF-SUMIFS" class="headerlink" title="四、SUMIF &amp; SUMIFS"></a>四、SUMIF &amp; SUMIFS</h1><p>在 Excel 中，<code>SUMIF</code> 和 <code>SUMIFS</code> 函数都用于条件求和，但它们在使用上有一些不同。下面是对这两个函数的详细解释：</p><h2 id="1-SUMIF-函数"><a href="#1-SUMIF-函数" class="headerlink" title="1. SUMIF 函数"></a>1. <code>SUMIF</code> 函数</h2><p><code>SUMIF</code> 用于在满足指定条件时对范围内的数值进行求和。它只接受一个条件进行判断，适用于简单的单一条件求和。</p><h3 id="语法：-4"><a href="#语法：-4" class="headerlink" title="语法："></a>语法：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">SUMIF</span>(range, criteria, [sum_range])</span><br></pre></td></tr></table></figure><ul><li><strong>range</strong>：要应用条件的单元格范围。</li><li><strong>criteria</strong>：条件，可以是一个数值、表达式、文本，甚至是一个单元格引用。</li><li>**[sum_range]**：可选。实际要求和的范围。如果省略，则默认使用 <strong>range</strong> 作为要求和的范围。</li></ul><h3 id="示例：-4"><a href="#示例：-4" class="headerlink" title="示例："></a>示例：</h3><p>假设在 A2:A6 中是销售员姓名，在 B2:B6 中是他们对应的销售额，你想要求和 <strong>销售员“张三”</strong> 的销售额。</p><table><thead><tr><th align="center">A列</th><th align="center">B列</th></tr></thead><tbody><tr><td align="center">销售员</td><td align="center">销售额</td></tr><tr><td align="center">张三</td><td align="center">5000</td></tr><tr><td align="center">李四</td><td align="center">3000</td></tr><tr><td align="center">张三</td><td align="center">4000</td></tr><tr><td align="center">王五</td><td align="center">3500</td></tr><tr><td align="center">张三</td><td align="center">4500</td></tr></tbody></table><p>公式：</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="selector-tag">SUMIF</span>(<span class="attribute">A2</span>:A6, <span class="string">&quot;张三&quot;</span>, <span class="attribute">B2</span>:B6)</span><br></pre></td></tr></table></figure><p>结果：<code>13500</code>，因为“张三”在 B2:B6 中对应的销售额是 5000 + 4000 + 4500。</p><h3 id="常见用法："><a href="#常见用法：" class="headerlink" title="常见用法："></a>常见用法：</h3><ul><li>使用 <code>SUMIF</code> 进行单一条件求和。例如，求和大于某个值、等于某个值、包含特定文本等。</li><li><code>SUMIF</code> 支持以下条件表达式：<ul><li>数字：例如 <code>&quot;&gt;5000&quot;</code>（大于5000）</li><li>文本：例如 <code>&quot;张三&quot;</code>（等于“张三”）</li><li>通配符：<code>&quot;?&quot;</code>（匹配一个字符）、<code>&quot;*&quot;</code> （匹配多个字符）</li></ul></li></ul><h2 id="2-SUMIFS-函数"><a href="#2-SUMIFS-函数" class="headerlink" title="2. SUMIFS 函数"></a>2. <code>SUMIFS</code> 函数</h2><p><code>SUMIFS</code> 是 <code>SUMIF</code> 的扩展，允许使用 <strong>多个条件</strong> 来进行求和。你可以对多个范围应用条件，只有当所有条件都满足时，才会进行求和。</p><h3 id="语法：-5"><a href="#语法：-5" class="headerlink" title="语法："></a>语法：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">SUMIFS</span>(sum_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)</span><br></pre></td></tr></table></figure><ul><li><strong>sum_range</strong>：实际要求和的单元格范围。</li><li><strong>criteria_range1</strong>：第一个条件范围。</li><li><strong>criteria1</strong>：第一个条件。</li><li>**[criteria_range2, criteria2]**：可选，第二个条件范围及条件。可以有多个条件范围和条件。</li></ul><h3 id="示例：-5"><a href="#示例：-5" class="headerlink" title="示例："></a>示例：</h3><p>假设在 A2:A6 中是销售员姓名，在 B2:B6 中是销售额，在 C2:C6 中是销售的月份，你想要求和 <strong>“张三”</strong> 在 <strong>“1月”</strong> 的销售额。</p><table><thead><tr><th align="center">A列</th><th align="center">B列</th><th align="center">C列</th></tr></thead><tbody><tr><td align="center">销售员</td><td align="center">销售额</td><td align="center">月份</td></tr><tr><td align="center">张三</td><td align="center">5000</td><td align="center">1月</td></tr><tr><td align="center">李四</td><td align="center">3000</td><td align="center">2月</td></tr><tr><td align="center">张三</td><td align="center">4000</td><td align="center">1月</td></tr><tr><td align="center">王五</td><td align="center">3500</td><td align="center">3月</td></tr><tr><td align="center">张三</td><td align="center">4500</td><td align="center">2月</td></tr></tbody></table><p>公式：</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="selector-tag">SUMIFS</span>(<span class="attribute">B2</span>:B6, <span class="attribute">A2</span>:A6, <span class="string">&quot;张三&quot;</span>, <span class="attribute">C2</span>:C6, <span class="string">&quot;1月&quot;</span>)</span><br></pre></td></tr></table></figure><p>结果：<code>9000</code>，因为只有在“张三”并且在“1月”时，销售额才会被加总（5000 + 4000）。</p><h3 id="常见用法：-1"><a href="#常见用法：-1" class="headerlink" title="常见用法："></a>常见用法：</h3><ul><li>使用 <code>SUMIFS</code> 进行多个条件的求和。例如，求和在特定日期、特定销售员、特定产品等情况下的销售额。</li><li><code>SUMIFS</code> 支持条件的比较运算符：<ul><li>数字：<code>&quot;&gt;1000&quot;</code>、<code>&quot;&lt;5000&quot;</code> 等</li><li>文本：例如 <code>&quot;张三&quot;</code></li><li>日期：例如 <code>&quot;&gt;2024-01-01&quot;</code> 等</li><li>通配符：<code>&quot;?&quot;</code> 和 <code>&quot;*&quot;</code></li></ul></li></ul><h3 id="SUMIF-与-SUMIFS-的区别"><a href="#SUMIF-与-SUMIFS-的区别" class="headerlink" title="SUMIF 与 SUMIFS 的区别"></a><code>SUMIF</code> 与 <code>SUMIFS</code> 的区别</h3><table><thead><tr><th align="center"><strong>特性</strong></th><th align="center"><strong>SUMIF</strong></th><th align="center"><strong>SUMIFS</strong></th></tr></thead><tbody><tr><td align="center"><strong>条件数量</strong></td><td align="center">只能处理 <strong>一个条件</strong>。</td><td align="center">可以处理 <strong>多个条件</strong>。</td></tr><tr><td align="center"><strong>条件范围</strong></td><td align="center">只有 <strong>一个条件范围</strong>。</td><td align="center">可以指定多个条件范围，每个条件范围对应一个条件。</td></tr><tr><td align="center"><strong>语法</strong></td><td align="center"><code>SUMIF(range, criteria, [sum_range])</code></td><td align="center"><code>SUMIFS(sum_range, criteria_range1, criteria1, ...)</code></td></tr><tr><td align="center"><strong>条件表达式</strong></td><td align="center">支持简单的单一条件（数字、文本、比较运算符等）</td><td align="center">支持多个条件，可以按多个标准进行过滤。</td></tr><tr><td align="center"><strong>适用场景</strong></td><td align="center">单一条件求和，如计算某个销售员的总销售额。</td><td align="center">多条件求和，如计算某销售员在某月的销售额。</td></tr></tbody></table><h3 id="示例比较："><a href="#示例比较：" class="headerlink" title="示例比较："></a>示例比较：</h3><p>假设你有一个销售表，包含销售员、销售额和月份，表格如下：</p><table><thead><tr><th align="center">销售员</th><th align="center">销售额</th><th align="center">月份</th></tr></thead><tbody><tr><td align="center">张三</td><td align="center">5000</td><td align="center">1月</td></tr><tr><td align="center">李四</td><td align="center">3000</td><td align="center">2月</td></tr><tr><td align="center">张三</td><td align="center">4000</td><td align="center">1月</td></tr><tr><td align="center">王五</td><td align="center">3500</td><td align="center">3月</td></tr><tr><td align="center">张三</td><td align="center">4500</td><td align="center">2月</td></tr></tbody></table><h3 id="使用-SUMIF："><a href="#使用-SUMIF：" class="headerlink" title="使用 SUMIF："></a>使用 <code>SUMIF</code>：</h3><p>假设你只想计算 <strong>张三</strong> 的总销售额：</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="selector-tag">SUMIF</span>(<span class="attribute">A2</span>:A6, <span class="string">&quot;张三&quot;</span>, <span class="attribute">B2</span>:B6)</span><br></pre></td></tr></table></figure><p>结果：<code>13500</code>。</p><h3 id="使用-SUMIFS："><a href="#使用-SUMIFS：" class="headerlink" title="使用 SUMIFS："></a>使用 <code>SUMIFS</code>：</h3><p>假设你想计算 <strong>张三</strong> 在 <strong>1月</strong> 的销售额：</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="selector-tag">SUMIFS</span>(<span class="attribute">B2</span>:B6, <span class="attribute">A2</span>:A6, <span class="string">&quot;张三&quot;</span>, <span class="attribute">C2</span>:C6, <span class="string">&quot;1月&quot;</span>)</span><br></pre></td></tr></table></figure><p>结果：<code>9000</code>（5000 + 4000）。</p><h2 id="3-总结："><a href="#3-总结：" class="headerlink" title="3.总结："></a>3.总结：</h2><ul><li>使用 <code>SUMIF</code> 时，只能处理 <strong>单一条件</strong> 的求和，适用于较简单的求和场景。</li><li>使用 <code>SUMIFS</code> 时，可以处理 <strong>多个条件</strong> 的求和，适用于需要根据多个条件进行筛选和求和的复杂场景。</li></ul><p>希望这能帮助你更好地理解 <code>SUMIF</code> 和 <code>SUMIFS</code> 的区别和使用场景！</p><h1 id="五、VLOOKUP"><a href="#五、VLOOKUP" class="headerlink" title="五、VLOOKUP"></a>五、VLOOKUP</h1><p><code>VLOOKUP</code> 是 Excel 中常用的查找函数之一，用于在一个数据表中根据某个条件查找值，并返回该值所在行的其他列的数据。它非常适合用于基于某个关键字从大型数据表中检索相关信息。</p><h2 id="1-VLOOKUP-函数简介"><a href="#1-VLOOKUP-函数简介" class="headerlink" title="1. VLOOKUP 函数简介"></a>1. <code>VLOOKUP</code> 函数简介</h2><p><code>VLOOKUP</code> 的全称是 <strong>Vertical Lookup</strong>，意思是 <strong>垂直查找</strong>。它在数据表的第一列中查找一个值，然后返回该值所在行其他列的内容。</p><h3 id="语法：-6"><a href="#语法：-6" class="headerlink" title="语法："></a>语法：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">VLOOKUP</span>(lookup_value, table_array, col_index_num, [range_lookup])</span><br></pre></td></tr></table></figure><ul><li><strong>lookup_value</strong>：你想要查找的值。可以是单元格引用或具体的数值或文本。</li><li><strong>table_array</strong>：查找的范围或表格。查找值将从这一范围的第一列开始查找。</li><li><strong>col_index_num</strong>：返回的值所在列的列号。列号是相对于 <code>table_array</code> 的第一列而言的。例如，<code>1</code> 表示 <code>table_array</code> 的第一列，<code>2</code> 表示第二列，依此类推。</li><li>**[range_lookup]**：可选参数，决定是否进行近似匹配。<ul><li><strong>TRUE</strong> 或省略：进行近似匹配（默认）。如果没有精确匹配，则返回小于 <code>lookup_value</code> 的最大值。</li><li><strong>FALSE</strong>：进行精确匹配。如果找不到精确匹配，返回 <code>#N/A</code> 错误。</li></ul></li></ul><h2 id="2-VLOOKUP-示例"><a href="#2-VLOOKUP-示例" class="headerlink" title="2. VLOOKUP 示例"></a>2. <code>VLOOKUP</code> 示例</h2><p>假设你有一个员工信息表，包含员工的 ID、姓名、部门等信息。你希望根据员工的 ID 查找对应的姓名。</p><table><thead><tr><th align="center">员工ID</th><th align="center">姓名</th><th align="center">部门</th></tr></thead><tbody><tr><td align="center">1001</td><td align="center">张三</td><td align="center">财务部</td></tr><tr><td align="center">1002</td><td align="center">李四</td><td align="center">市场部</td></tr><tr><td align="center">1003</td><td align="center">王五</td><td align="center">销售部</td></tr><tr><td align="center">1004</td><td align="center">赵六</td><td align="center">人事部</td></tr></tbody></table><h3 id="查找员工ID为-1003-的姓名"><a href="#查找员工ID为-1003-的姓名" class="headerlink" title="查找员工ID为 1003 的姓名"></a>查找员工ID为 <code>1003</code> 的姓名</h3><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">VLOOKUP</span>(<span class="number">1003</span>, A2:C5, <span class="number">2</span>, FALSE)</span><br></pre></td></tr></table></figure><p>解释：</p><ul><li><code>1003</code>：查找值是 <code>1003</code>。</li><li><code>A2:C5</code>：查找的范围是 A2 到 C5。</li><li><code>2</code>：返回查找值所在行的第2列，即“姓名”列。</li><li><code>FALSE</code>：精确匹配，只有找到员工ID为 <code>1003</code> 时才会返回姓名。</li></ul><p>结果：<code>王五</code></p><h3 id="查找员工ID为-1005-的姓名（不存在该ID）"><a href="#查找员工ID为-1005-的姓名（不存在该ID）" class="headerlink" title="查找员工ID为 1005 的姓名（不存在该ID）"></a>查找员工ID为 <code>1005</code> 的姓名（不存在该ID）</h3><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">VLOOKUP</span>(<span class="number">1005</span>, A2:C5, <span class="number">2</span>, FALSE)</span><br></pre></td></tr></table></figure><p>结果：<code>#N/A</code>，因为找不到 ID 为 <code>1005</code> 的员工。</p><hr><h2 id="3-使用-VLOOKUP-时的注意事项"><a href="#3-使用-VLOOKUP-时的注意事项" class="headerlink" title="3. 使用 VLOOKUP 时的注意事项"></a>3. 使用 <code>VLOOKUP</code> 时的注意事项</h2><h3 id="3-1-查找值必须位于-table-array-的第一列"><a href="#3-1-查找值必须位于-table-array-的第一列" class="headerlink" title="3.1. 查找值必须位于 table_array 的第一列"></a>3.1. <strong>查找值必须位于 <code>table_array</code> 的第一列</strong></h3><p><code>VLOOKUP</code> 总是从 <code>table_array</code> 的 <strong>第一列</strong> 开始查找，因此，查找值必须在 <code>table_array</code> 的第一列。否则，<code>VLOOKUP</code> 无法工作。</p><h3 id="3-2-近似匹配（TRUE-或省略）"><a href="#3-2-近似匹配（TRUE-或省略）" class="headerlink" title="3.2. 近似匹配（TRUE 或省略）"></a>3.2. <strong>近似匹配（<code>TRUE</code> 或省略）</strong></h3><p>如果 <code>[range_lookup]</code> 为 <code>TRUE</code> 或省略，<code>VLOOKUP</code> 会执行近似匹配。此时，查找值必须按升序排列，否则可能得到不准确的结果。</p><ul><li>如果你希望进行近似匹配（例如查找一个介于两个值之间的值），请确保查找列已按升序排列。</li><li>近似匹配的常见用途包括计算税率表、价格表等。</li></ul><h3 id="3-3-精确匹配（FALSE）"><a href="#3-3-精确匹配（FALSE）" class="headerlink" title="3.3. 精确匹配（FALSE）"></a>3.3. <strong>精确匹配（<code>FALSE</code>）</strong></h3><p>如果 <code>[range_lookup]</code> 为 <code>FALSE</code>，<code>VLOOKUP</code> 将执行 <strong>精确匹配</strong>。如果查找值没有找到，<code>VLOOKUP</code> 将返回 <code>#N/A</code> 错误。</p><h3 id="3-4-列号必须大于等于-2"><a href="#3-4-列号必须大于等于-2" class="headerlink" title="3.4. 列号必须大于等于 2"></a>3.4. <strong>列号必须大于等于 2</strong></h3><p><code>VLOOKUP</code> 只能返回查找值所在行的 <strong>后续列</strong> 的数据，即 <code>col_index_num</code> 必须大于等于 2。如果你想返回查找列的值，通常会使用 <code>INDEX</code> 和 <code>MATCH</code> 函数的组合。</p><h3 id="3-5-VLOOKUP-的限制"><a href="#3-5-VLOOKUP-的限制" class="headerlink" title="3.5. VLOOKUP 的限制"></a>3.5. <strong><code>VLOOKUP</code> 的限制</strong></h3><ul><li><code>VLOOKUP</code> 只能查找 <strong>左侧</strong> 的列，不能反向查找。例如，你不能在表格的最后一列查找某个值并返回第一列的结果。</li><li>它只能返回 <strong>一列数据</strong>，如果你需要从多个列中返回结果，可以使用多次 <code>VLOOKUP</code> 或者考虑其他函数组合，如 <code>INDEX</code> 和 <code>MATCH</code>。</li></ul><hr><h2 id="4-VLOOKUP-的常见应用场景"><a href="#4-VLOOKUP-的常见应用场景" class="headerlink" title="4. VLOOKUP 的常见应用场景"></a>4. <code>VLOOKUP</code> 的常见应用场景</h2><h3 id="4-1-查找价格"><a href="#4-1-查找价格" class="headerlink" title="4.1. 查找价格"></a>4.1. <strong>查找价格</strong></h3><p>假设你有一个商品价格表，想根据商品 ID 查找对应商品的价格：</p><table><thead><tr><th align="center">商品ID</th><th align="center">商品名称</th><th align="center">单价</th></tr></thead><tbody><tr><td align="center">A101</td><td align="center">商品A</td><td align="center">100元</td></tr><tr><td align="center">A102</td><td align="center">商品B</td><td align="center">150元</td></tr><tr><td align="center">A103</td><td align="center">商品C</td><td align="center">200元</td></tr></tbody></table><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">VLOOKUP</span>(&quot;A102&quot;, A2:C4, <span class="number">3</span>, FALSE)</span><br></pre></td></tr></table></figure><p>结果：<code>150元</code>（返回商品B的价格）。</p><h3 id="4-2-查找员工的部门"><a href="#4-2-查找员工的部门" class="headerlink" title="4.2. 查找员工的部门"></a>4.2. <strong>查找员工的部门</strong></h3><p>假设你有一个员工表，根据员工ID查找其所属的部门：</p><table><thead><tr><th align="center">员工ID</th><th align="center">姓名</th><th align="center">部门</th></tr></thead><tbody><tr><td align="center">1001</td><td align="center">张三</td><td align="center">财务部</td></tr><tr><td align="center">1002</td><td align="center">李四</td><td align="center">市场部</td></tr><tr><td align="center">1003</td><td align="center">王五</td><td align="center">销售部</td></tr><tr><td align="center">1004</td><td align="center">赵六</td><td align="center">人事部</td></tr></tbody></table><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">VLOOKUP</span>(<span class="number">1002</span>, A2:C5, <span class="number">3</span>, FALSE)</span><br></pre></td></tr></table></figure><p>结果：<code>市场部</code>（返回员工ID为1002的部门）。</p><h3 id="4-3-使用-VLOOKUP-进行评分查询"><a href="#4-3-使用-VLOOKUP-进行评分查询" class="headerlink" title="4.3. 使用 VLOOKUP 进行评分查询"></a>4.3. <strong>使用 <code>VLOOKUP</code> 进行评分查询</strong></h3><p>假设你有一个分数等级表，根据学生的分数返回相应的等级：</p><table><thead><tr><th align="center">分数范围</th><th align="center">等级</th></tr></thead><tbody><tr><td align="center">90-100</td><td align="center">A</td></tr><tr><td align="center">80-89</td><td align="center">B</td></tr><tr><td align="center">70-79</td><td align="center">C</td></tr><tr><td align="center">60-69</td><td align="center">D</td></tr><tr><td align="center">0-59</td><td align="center">F</td></tr></tbody></table><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">VLOOKUP</span>(<span class="number">85</span>, A2:B6, <span class="number">2</span>, TRUE)</span><br></pre></td></tr></table></figure><p>结果：<code>B</code>（因为85分落在“80-89”区间）。</p><hr><h2 id="5-总结"><a href="#5-总结" class="headerlink" title="5. 总结"></a>5. 总结</h2><ul><li><code>VLOOKUP</code> 是一个强大的查找工具，适用于从一个数据表中根据条件查找数据并返回相关信息。</li><li>它只能在查找值的 <strong>左侧列</strong> 进行查找，并且只能返回查找值所在行的 <strong>后续列</strong> 数据。</li><li>需要确保查找表格的排列和选择适当的匹配类型（精确或近似匹配）。</li></ul><h1 id="六、INDEX-MATCH"><a href="#六、INDEX-MATCH" class="headerlink" title="六、INDEX &amp; MATCH"></a>六、INDEX &amp; MATCH</h1><h3 id="INDEX-和-MATCH-函数介绍"><a href="#INDEX-和-MATCH-函数介绍" class="headerlink" title="INDEX 和 MATCH 函数介绍"></a><code>INDEX</code> 和 <code>MATCH</code> 函数介绍</h3><p><code>INDEX</code> 和 <code>MATCH</code> 函数是 Excel 中非常强大的函数，可以单独使用，也可以结合起来使用，完成更为复杂和灵活的查找操作。与传统的 <code>VLOOKUP</code> 或 <code>HLOOKUP</code> 不同，<code>INDEX</code> 和 <code>MATCH</code> 提供了更高的灵活性和效率，尤其是在处理大数据集时，能够解决 <code>VLOOKUP</code> 无法处理的一些问题。</p><p>下面，我将分别介绍这两个函数，并详细说明它们如何结合使用。</p><hr><h2 id="1-INDEX-函数"><a href="#1-INDEX-函数" class="headerlink" title="1. INDEX 函数"></a>1. <code>INDEX</code> 函数</h2><p><code>INDEX</code> 函数用于从一个数据范围中返回一个值，基于给定的行号和列号。它有两种使用方式：<strong>数组形式</strong> 和 <strong>引用形式</strong>。</p><h3 id="数组形式（最常用）"><a href="#数组形式（最常用）" class="headerlink" title="数组形式（最常用）"></a><strong>数组形式（最常用）</strong></h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">INDEX</span>(array, row_num, [column_num])</span><br></pre></td></tr></table></figure><ul><li><strong>array</strong>：包含数据的数组或数据范围。</li><li><strong>row_num</strong>：数据范围中的行号，用于指定要返回的值所在的行。</li><li>**[column_num]**：可选。数据范围中的列号，用于指定要返回的值所在的列。如果是单列或单行数据，则可以省略此项。</li></ul><h3 id="示例-1：从单列数组中提取一个值"><a href="#示例-1：从单列数组中提取一个值" class="headerlink" title="示例 1：从单列数组中提取一个值"></a>示例 1：从单列数组中提取一个值</h3><p>假设有如下商品列表：</p><table><thead><tr><th align="left">商品</th></tr></thead><tbody><tr><td align="left">商品A</td></tr><tr><td align="left">商品B</td></tr><tr><td align="left">商品C</td></tr><tr><td align="left">商品D</td></tr></tbody></table><p>你想返回第三行（即商品C）。</p><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">INDEX</span>(A1:A4, <span class="number">3</span>)</span><br></pre></td></tr></table></figure><ul><li><strong>A1:A4</strong> 是数据范围。</li><li><strong>3</strong> 是行号，表示返回第三行的值。</li></ul><p>结果：<code>商品C</code></p><h3 id="示例-2：从二维数组中提取一个值"><a href="#示例-2：从二维数组中提取一个值" class="headerlink" title="示例 2：从二维数组中提取一个值"></a>示例 2：从二维数组中提取一个值</h3><p>假设有如下员工信息表：</p><table><thead><tr><th align="center">员工ID</th><th align="center">姓名</th><th align="center">部门</th></tr></thead><tbody><tr><td align="center">1001</td><td align="center">张三</td><td align="center">财务部</td></tr><tr><td align="center">1002</td><td align="center">李四</td><td align="center">市场部</td></tr><tr><td align="center">1003</td><td align="center">王五</td><td align="center">销售部</td></tr><tr><td align="center">1004</td><td align="center">赵六</td><td align="center">人事部</td></tr></tbody></table><p>你希望返回第二行第三列的值（即第二个员工的部门）。</p><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">INDEX</span>(A1:C4, <span class="number">2</span>, <span class="number">3</span>)</span><br></pre></td></tr></table></figure><ul><li><strong>A1:C4</strong> 是数据范围。</li><li><strong>2</strong> 是行号，表示第二行。</li><li><strong>3</strong> 是列号，表示第三列（部门列）。</li></ul><p>结果：<code>市场部</code></p><hr><h2 id="2-MATCH-函数"><a href="#2-MATCH-函数" class="headerlink" title="2. MATCH 函数"></a>2. <code>MATCH</code> 函数</h2><p><code>MATCH</code> 函数用于在指定的范围中查找某个值，并返回该值的相对位置。与 <code>INDEX</code> 不同，<code>MATCH</code> 只返回 <strong>位置索引</strong>，不返回实际的值。</p><h3 id="语法：-7"><a href="#语法：-7" class="headerlink" title="语法："></a>语法：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">MATCH</span>(lookup_value, lookup_array, [match_type])</span><br></pre></td></tr></table></figure><ul><li><strong>lookup_value</strong>：你要查找的值，可以是一个具体的数值、文本、或者单元格引用。</li><li><strong>lookup_array</strong>：包含要查找值的数组或范围。</li><li>**[match_type]**：可选。指定匹配的类型：<ul><li><strong>1</strong>（默认）：查找小于或等于 <code>lookup_value</code> 的最大值。要求 <code>lookup_array</code> 按升序排列。</li><li><strong>0</strong>：查找精确匹配的值。</li><li><strong>1</strong>：查找大于或等于 <code>lookup_value</code> 的最小值。要求 <code>lookup_array</code> 按降序排列。</li></ul></li></ul><h3 id="示例：查找某个值的位置信息"><a href="#示例：查找某个值的位置信息" class="headerlink" title="示例：查找某个值的位置信息"></a>示例：查找某个值的位置信息</h3><p>假设有如下员工信息表：</p><table><thead><tr><th align="center">员工ID</th><th align="center">姓名</th><th align="center">部门</th></tr></thead><tbody><tr><td align="center">1001</td><td align="center">张三</td><td align="center">财务部</td></tr><tr><td align="center">1002</td><td align="center">李四</td><td align="center">市场部</td></tr><tr><td align="center">1003</td><td align="center">王五</td><td align="center">销售部</td></tr><tr><td align="center">1004</td><td align="center">赵六</td><td align="center">人事部</td></tr></tbody></table><p>你希望找出 “王五” 在表中的位置。</p><p>公式：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">MATCH</span>(&quot;王五&quot;, B1:B4, <span class="number">0</span>)</span><br></pre></td></tr></table></figure><ul><li><strong>“王五”</strong> 是查找值。</li><li><strong>B1:B4</strong> 是包含姓名的范围。</li><li><strong>0</strong> 表示进行精确匹配。</li></ul><p>结果：<code>3</code>，即王五在第3行。</p><hr><h2 id="3-INDEX-和-MATCH-组合使用"><a href="#3-INDEX-和-MATCH-组合使用" class="headerlink" title="3. INDEX 和 MATCH 组合使用"></a>3. <code>INDEX</code> 和 <code>MATCH</code> 组合使用</h2><p><code>INDEX</code> 和 <code>MATCH</code> 的组合使用，能实现更灵活和强大的查找功能。通过使用 <code>MATCH</code> 查找行号或列号，<code>INDEX</code> 可以根据这个位置返回对应的数据。结合 <code>INDEX</code> 和 <code>MATCH</code> 可以克服 <code>VLOOKUP</code> 的一些限制（比如只能在查找表的第一列查找）。</p><h3 id="例子：通过-INDEX-和-MATCH-查找员工的部门"><a href="#例子：通过-INDEX-和-MATCH-查找员工的部门" class="headerlink" title="例子：通过 INDEX 和 MATCH 查找员工的部门"></a>例子：通过 <code>INDEX</code> 和 <code>MATCH</code> 查找员工的部门</h3><p>假设你有一个员工信息表，如下所示：</p><table><thead><tr><th align="center">员工ID</th><th align="center">姓名</th><th align="center">部门</th></tr></thead><tbody><tr><td align="center">1001</td><td align="center">张三</td><td align="center">财务部</td></tr><tr><td align="center">1002</td><td align="center">李四</td><td align="center">市场部</td></tr><tr><td align="center">1003</td><td align="center">王五</td><td align="center">销售部</td></tr><tr><td align="center">1004</td><td align="center">赵六</td><td align="center">人事部</td></tr></tbody></table><p>你希望根据员工的姓名查找其所属的部门。</p><p><strong>步骤：</strong></p><ol><li>使用 <code>MATCH</code> 查找姓名所在行号。</li><li>使用 <code>INDEX</code> 根据行号返回部门。</li></ol><p><strong>公式：</strong></p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">INDEX</span>(C1:C4, MATCH(&quot;王五&quot;, B1:B4, <span class="number">0</span>))</span><br></pre></td></tr></table></figure><ul><li><code>MATCH(&quot;王五&quot;, B1:B4, 0)</code> 返回 <code>&quot;王五&quot;</code> 在姓名列中的位置（即第3行）。</li><li><code>INDEX(C1:C4, 3)</code> 根据行号 <code>3</code> 从部门列（C列）中返回对应的部门。</li></ul><p>结果：<code>销售部</code></p><h3 id="例子：通过-INDEX-和-MATCH-查找指定员工的姓名"><a href="#例子：通过-INDEX-和-MATCH-查找指定员工的姓名" class="headerlink" title="例子：通过 INDEX 和 MATCH 查找指定员工的姓名"></a>例子：通过 <code>INDEX</code> 和 <code>MATCH</code> 查找指定员工的姓名</h3><p>如果你要根据员工ID查找对应的姓名，可以使用类似的组合。</p><p>假设你有一个员工ID列（A列）和姓名列（B列）：</p><table><thead><tr><th align="center">员工ID</th><th align="center">姓名</th></tr></thead><tbody><tr><td align="center">1001</td><td align="center">张三</td></tr><tr><td align="center">1002</td><td align="center">李四</td></tr><tr><td align="center">1003</td><td align="center">王五</td></tr><tr><td align="center">1004</td><td align="center">赵六</td></tr></tbody></table><p>你希望根据员工ID查找姓名，假设要查找员工ID为 <code>1003</code> 的姓名。</p><p><strong>步骤：</strong></p><ol><li>使用 <code>MATCH</code> 查找员工ID所在的行号。</li><li>使用 <code>INDEX</code> 根据行号返回姓名。</li></ol><p><strong>公式：</strong></p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">=<span class="built_in">INDEX</span>(B1:B4, MATCH(<span class="number">1003</span>, A1:A4, <span class="number">0</span>))</span><br></pre></td></tr></table></figure><ul><li><code>MATCH(1003, A1:A4, 0)</code> 查找员工ID <code>1003</code> 在 A 列的位置（即第3行）。</li><li><code>INDEX(B1:B4, 3)</code> 返回该行对应的姓名，即第3行的姓名 <code>王五</code>。</li></ul><p>结果：<code>王五</code></p><hr><h2 id="4-INDEX-和-MATCH-的优点"><a href="#4-INDEX-和-MATCH-的优点" class="headerlink" title="4. INDEX 和 MATCH 的优点"></a>4. <code>INDEX</code> 和 <code>MATCH</code> 的优点</h2><ul><li><strong>灵活性</strong>：<code>INDEX</code> 和 <code>MATCH</code> 组合可以从任何位置（包括不在第一列的地方）查找数据，而 <code>VLOOKUP</code> 只能从数据范围的第一列查找。</li><li><strong>效率</strong>：在处理非常大的数据集时，<code>INDEX</code> 和 <code>MATCH</code> 的组合通常比 <code>VLOOKUP</code> 更高效，尤其是在数据列数很大时。</li><li><strong>支持横向查找</strong>：<code>VLOOKUP</code> 是纵向查找，<code>HLOOKUP</code> 是横向查找，而 <code>INDEX</code> 和 <code>MATCH</code> 可以在任何方向上查找数据，提供更多的灵活性。</li></ul><hr><h2 id="5-总结-1"><a href="#5-总结-1" class="headerlink" title="5. 总结"></a>5. 总结</h2><ul><li><strong><code>INDEX</code></strong> 用于返回数组中指定位置的值。</li><li><strong><code>MATCH</code></strong> 用于查找某个值在数组中的位置。</li><li><strong><code>INDEX</code> 和 <code>MATCH</code> 的组合</strong> 提供了比 <code>VLOOKUP</code> 更强大和灵活的查找能力，尤其是在处理复杂的数据结构时。</li></ul><p>通过将 <code>INDEX</code> 和 <code>MATCH</code> 组合使用，可以进行灵活的数据查找和返回操作，弥补了 <code>VLOOKUP</code> 和 <code>HLOOKUP</code> 函数的一些局限性。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、Excel-常用操作&quot;&gt;&lt;a href=&quot;#一、Excel-常用操作&quot; class=&quot;headerlink&quot; title=&quot;一、Excel 常用操作&quot;&gt;&lt;/a&gt;一、Excel 常用操作&lt;/h1&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;c</summary>
      
    
    
    
    <category term="实用技巧" scheme="https://cq230.github.io/categories/%E5%AE%9E%E7%94%A8%E6%8A%80%E5%B7%A7/"/>
    
    
    <category term="工具" scheme="https://cq230.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
    
    <category term="Excel" scheme="https://cq230.github.io/tags/Excel/"/>
    
  </entry>
  
  <entry>
    <title>同比和环比</title>
    <link href="https://cq230.github.io/posts/b2e071e.html"/>
    <id>https://cq230.github.io/posts/b2e071e.html</id>
    <published>2024-11-14T11:35:44.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>在数据分析中，<strong>同比</strong> 和 <strong>环比</strong> 是两种常用的统计比较方法，用于衡量数据随时间变化的趋势。它们通常用于财务、经济、市场等领域，用以评估增长或变化情况。</p><h2 id="1-同比（Year-on-Year-YoY）"><a href="#1-同比（Year-on-Year-YoY）" class="headerlink" title="1. 同比（Year-on-Year, YoY）"></a>1. 同比（Year-on-Year, YoY）</h2><p><strong>同比</strong> 是指将当前数据与 <strong>去年同一时间</strong> 的数据进行比较，通常用于衡量某个指标在过去一年的变化情况。同比可以帮助我们排除季节性波动的影响，聚焦于长周期的变化趋势。</p><h3 id="公式："><a href="#公式：" class="headerlink" title="公式："></a>公式：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">同比增长率 = (当前期数据 - 去年同期数据) / 去年同期数据 × <span class="number">100%</span></span><br><span class="line">or</span><br><span class="line">同比增长率 = 当前期数据 / 去年同期数据 -<span class="number">1</span></span><br></pre></td></tr></table></figure><h3 id="应用场景："><a href="#应用场景：" class="headerlink" title="应用场景："></a>应用场景：</h3><ul><li>比较年度数据，观察一个指标在一年中的表现变化。</li><li>适合分析业务、市场等领域的长期趋势。</li><li>常用于评估收入、利润、销售额等长期经营指标的增长或衰退。</li></ul><h3 id="示例："><a href="#示例：" class="headerlink" title="示例："></a>示例：</h3><p>假设某公司2024年1月的销售额为 500 万元，2023年1月的销售额为 400 万元。则：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">同比增长率 = (<span class="number">500</span> - <span class="number">400</span>) / <span class="number">400</span> × <span class="number">100%</span> = <span class="number">25%</span></span><br></pre></td></tr></table></figure><p>这表明公司2024年1月的销售额比2023年1月增长了 25%。</p><hr><h2 id="2-环比（Month-on-Month-MoM-或-Quarter-on-Quarter-QoQ）"><a href="#2-环比（Month-on-Month-MoM-或-Quarter-on-Quarter-QoQ）" class="headerlink" title="2. 环比（Month-on-Month, MoM 或 Quarter-on-Quarter, QoQ）"></a>2. 环比（Month-on-Month, MoM 或 Quarter-on-Quarter, QoQ）</h2><p><strong>环比</strong> 是指将当前数据与 <strong>上一个时间周期</strong> 的数据进行比较，通常用于比较当前月份与上个月，或当前季度与上个季度的变化情况。环比分析更侧重于短期波动，适用于快速反应市场或业务的变动。</p><h3 id="公式：-1"><a href="#公式：-1" class="headerlink" title="公式："></a>公式：</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">环比增长率 = (当前期数据 - 上期数据) / 上期数据 × <span class="number">100%</span></span><br><span class="line">or</span><br><span class="line">环比增长率 = 当前期数据 / 上期数据 -<span class="number">1</span></span><br></pre></td></tr></table></figure><h3 id="应用场景：-1"><a href="#应用场景：-1" class="headerlink" title="应用场景："></a>应用场景：</h3><ul><li>适用于分析短期数据波动，观察上个月或上个季度的变化。</li><li>可以帮助识别季节性变化或突发事件对数据的影响。</li><li>常用于监控和优化业务的短期发展趋势。</li></ul><h3 id="示例：-1"><a href="#示例：-1" class="headerlink" title="示例："></a>示例：</h3><p>假设某公司2024年1月的销售额为 500 万元，2023年12月的销售额为 450 万元。则：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">环比增长率 = (<span class="number">500</span> - <span class="number">450</span>) / <span class="number">450</span> × <span class="number">100%</span> = <span class="number">11.11%</span></span><br></pre></td></tr></table></figure><p>这表明公司2024年1月的销售额比2023年12月增长了 11.11%。</p><hr><h2 id="3-同比与环比的区别"><a href="#3-同比与环比的区别" class="headerlink" title="3. 同比与环比的区别"></a>3. 同比与环比的区别</h2><table><thead><tr><th>维度</th><th><strong>同比（YoY）</strong></th><th><strong>环比（MoM &#x2F; QoQ）</strong></th></tr></thead><tbody><tr><td><strong>比较对象</strong></td><td>当前期与<strong>去年同一时间</strong>的比较</td><td>当前期与<strong>上一个周期</strong>（如上个月、上个季度）的比较</td></tr><tr><td><strong>适用场景</strong></td><td>长期趋势分析，适合观察年度增长或衰退趋势</td><td>短期趋势分析，适合分析月度或季度变化</td></tr><tr><td><strong>影响因素</strong></td><td>消除了季节性波动和短期波动的影响</td><td>可能受到季节性变化、节假日等短期因素影响</td></tr><tr><td><strong>数据周期</strong></td><td>通常用于年度、年度环比等</td><td>通常用于月度、季度等周期</td></tr></tbody></table><hr><h2 id="总结："><a href="#总结：" class="headerlink" title="总结："></a>总结：</h2><ul><li><strong>同比</strong>：主要用于分析长期趋势，帮助我们了解一年或多年的业绩变化，消除了季节性因素的干扰。</li><li><strong>环比</strong>：侧重于短期数据的波动，适用于观察月度或季度的变化，可能会受到季节性因素的影响。</li></ul><p>通过同比和环比的分析，我们可以更加全面、准确地理解数据的趋势，并据此做出相应的决策。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在数据分析中，&lt;strong&gt;同比&lt;/strong&gt; 和 &lt;strong&gt;环比&lt;/strong&gt; 是两种常用的统计比较方法，用于衡量数据随时间变化的趋势。它们通常用于财务、经济、市场等领域，用以评估增长或变化情况。&lt;/p&gt;
&lt;h2 id=&quot;1-同比（Year-on-Year</summary>
      
    
    
    
    <category term="数据分析" scheme="https://cq230.github.io/categories/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/"/>
    
    
    <category term="数据分析" scheme="https://cq230.github.io/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/"/>
    
  </entry>
  
  <entry>
    <title>ChatGPT使用技巧</title>
    <link href="https://cq230.github.io/posts/3fbfae13.html"/>
    <id>https://cq230.github.io/posts/3fbfae13.html</id>
    <published>2024-11-12T11:21:02.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>本文整理自@<a href="https://www.youtube.com/@Tan_Dongdong">檀东东·Tango</a>，以下是原视频链接。</p><ul><li><a href="https://www.youtube.com/watch?v=bpWJ-F-PyTk">YouTube</a></li></ul><h1 id="1-5W1H"><a href="#1-5W1H" class="headerlink" title="1.5W1H"></a>1.5W1H</h1><ul><li>简单：<br>who\what\when\where-不需要 ChatGPT 也能轻松完成</li><li>复杂：<br>why-基于广泛事实依据推理<br>how-基于目标提供路径工具</li></ul><h1 id="2-沟通视图"><a href="#2-沟通视图" class="headerlink" title="2.沟通视图"></a>2.沟通视图</h1><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731413899413_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731413899413_image.png"></p><h2 id="盲区"><a href="#盲区" class="headerlink" title="盲区"></a>盲区</h2><ul><li>我想了解xxx，我应该向你问哪些问题？</li><li>请给我列出xxx领域&#x2F;行业相关的，最常用的50个概念，并做简单解释。如果有英文缩写，请给出完整的英文解释。</li><li>请详细介绍一下xxx的主要生平事迹。</li><li>请详细介绍一下xxx公司的发展历程。</li></ul><h2 id="公开区"><a href="#公开区" class="headerlink" title="公开区"></a>公开区</h2><ol><li><strong>检验认知</strong><ul><li>对于xxx主题&#x2F;技能，你认为哪些是我必须理解和掌握的核心要点？</li><li>我理解的xxx是这样的，你觉得我的理解对吗？</li><li>我对xxx有一些想法，你能帮我批判性地分析一下这些想法的优点和缺点吗？</li><li>我正在考虑xxx的决定，你能帮我分析一下可能的结果和影响吗？</li></ul></li><li><strong>扩充认知</strong><ul><li>我知道xxx的概念，我想知道更多关于xxx的信息。</li><li>我在xxx问题上遇到困难，你能提供一些可能的解决方案或建议吗？</li><li>我想要深入学习xxx，你能推荐一些进阶的学习资源或学习路径吗？</li><li>我想要在xxx领域有所创新，你能提供一些启发或想法吗？</li><li>我想在xxx领域提升自己，你能根据最新的研究和趋势给我一些建议吗？</li><li>我正在考虑学习xxx，你能给我一些关于这个领域未来发展的观点吗？</li><li>（背景信息）我要做关于xxx的研究，我认为原因是xxx，还有其他可能的原因吗？给出一些可能的研究假设。</li><li>我是一个xxx行业新手，马上要采访这个行业的资深大佬，我应该向他请教哪些有价值的问题？</li></ul></li></ol><h2 id="隐私区"><a href="#隐私区" class="headerlink" title="隐私区"></a>隐私区</h2><ul><li>你怎么看待这种现象？</li><li>可能的原因有哪些？</li><li>这会对xxx产生什么样的影响？</li><li>你觉得xxx应该怎么做？</li></ul><h2 id="未知区"><a href="#未知区" class="headerlink" title="未知区"></a>未知区</h2><ul><li>如果xxx，这对xxx会产生什么影响？</li></ul><h1 id="3-达克模型"><a href="#3-达克模型" class="headerlink" title="3.达克模型"></a>3.达克模型</h1><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731435199801_image.png" alt="https://cloudflare-imgbed-ebz.pages.dev/file/1731435199801_image.png"></p><h2 id="反向提问"><a href="#反向提问" class="headerlink" title="反向提问"></a>反向提问</h2><p><code>为了检验自己的认知，可以使用反问提问的方法。</code></p><ul><li><p>为了测试我对xxx的了解程度，你会问我什么问题来检验我的水平，最少10个。</p></li><li><p>我是xxx领域的专家，你会问我哪些问题来检验我的专业水平？最少10个。</p></li><li><p>我已经很精通xxx了，我想知道我是否还有需要学习的地方？</p></li><li><p><strong>你问我答</strong>：</p><blockquote><p>现在我们玩个“你问我答”的游戏，<br>目的是为了测试我在xxx方面的专业水平。<br>你负责提问，我负责回答。<br>你要根据我的答案进行反馈、评价、补充。<br>如果我说不知道，你就直接输出正确答案，然后你继续提问。</p></blockquote></li></ul><h1 id="4-知道做到"><a href="#4-知道做到" class="headerlink" title="4.知道做到"></a>4.知道做到</h1><ol><li>知道自己要干什么，但是不知道 ChatGPT 能帮你什么。<ul><li>我想做xxx，你能给我提供什么帮助？</li></ul></li><li>知道要 ChatGPT 帮你做什么，但是你不知道给它输入什么具体的信息。<ul><li>我想要你xxx，我应该给你输入什么信息？</li></ul></li><li>直接给 ChatGPT 指令。任务目标、背景信息、输出要求都非常清晰。<ul><li>请推荐（书名、电视剧、电影、网站、博主、专家、学习资源）</li><li>请翻译…请总结…请润色…</li></ul></li></ol><h1 id="5-角色关系"><a href="#5-角色关系" class="headerlink" title="5.角色关系"></a>5.角色关系</h1><p><code>赋予 ChatGPT 特定的角色</code></p><h2 id="角色扮演"><a href="#角色扮演" class="headerlink" title="角色扮演"></a>角色扮演</h2><ul><li>如果你是xxx，在某种情况下，xxx会说什么，会怎么想，会怎么看，会怎么做？</li></ul><h2 id="模拟情景"><a href="#模拟情景" class="headerlink" title="模拟情景"></a>模拟情景</h2><p>eg1-<strong>营销管培生成长建议</strong></p><p><a href="https://chatgpt.com/share/6733bf1e-2524-800c-b420-0a860717e88e">ChatGPT 聊天共享链接</a></p><p>eg2-<strong>设计老年人回忆录 APP</strong></p><p><a href="https://chatgpt.com/share/6733bfc0-100c-800c-ae7f-499f0a4f2e62">ChatGPT 共享聊天链接</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;本文整理自@&lt;a href=&quot;https://www.youtube.com/@Tan_Dongdong&quot;&gt;檀东东·Tango&lt;/a&gt;，以下是原视频链接。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=bpWJ-</summary>
      
    
    
    
    <category term="实用技巧" scheme="https://cq230.github.io/categories/%E5%AE%9E%E7%94%A8%E6%8A%80%E5%B7%A7/"/>
    
    
    <category term="AI" scheme="https://cq230.github.io/tags/AI/"/>
    
    <category term="ChatGPT" scheme="https://cq230.github.io/tags/ChatGPT/"/>
    
    <category term="工具" scheme="https://cq230.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>图床搭建方案</title>
    <link href="https://cq230.github.io/posts/5af86876.html"/>
    <id>https://cq230.github.io/posts/5af86876.html</id>
    <published>2024-11-08T11:16:38.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>博客搭建完成后，我开始考虑使用图床，了解当下主流图床平台的过程中，在 <a href="http://linux.do/">Linux.do</a> 论坛发现了<a href="https://linux.do/t/topic/251653">免费图片托管解决方案</a>，基于 Cloudflare Pages 和 Telegram，支持 Telegram Bot 存储渠道和 Cloudflare R2 存储渠道，在 <a href="https://github.com/MarSeventh/CloudFlare-ImgBed">GitHub 上有详细的教程</a>。由于开通 Cloudflare R2 存储桶需要绑定支付方式，我手上暂时没有可用信用卡，因此只实现了 Telegram Bot 存储渠道。</p><h1 id="功能介绍"><a href="#功能介绍" class="headerlink" title="功能介绍"></a>功能介绍</h1><ul><li>支持绝大多数常见<strong>图片、视频、动图</strong>等</li><li>支持 <strong>Telegram Bot</strong>, <strong>Cloudflare R2</strong> 等多种存储渠道一键切换</li><li>支持自定义压缩质量，自定义开启前后端压缩功能</li><li>支持 <strong>MarkDown、HTML、BBCode 和原始链接</strong>四种格式复制</li><li>支持身份认证、防滥用</li><li>页面背景支持<strong>单图</strong>、<strong>自定义多图轮播</strong>、<strong>bing 随机图轮播</strong>等多种模式</li><li>自定义图床名称和 Logo</li><li>自定义网站标题和 Icon</li><li><strong>无限图片储存数量</strong>，你可以上传不限数量的图片</li><li>无需购买服务器，托管于 Cloudflare 的网络上，当使用量不超过 Cloudflare 的免费额度时，完全免费</li><li>无需购买域名，可以使用 Cloudflare Pages 提供的<code>*.pages.dev</code>的免费二级域名，同时也支持绑定自定义域名</li><li>支持<strong>图片审查</strong> API，可根据需要开启，开启后不良图片将自动屏蔽，不再加载</li></ul><h1 id="使用限制"><a href="#使用限制" class="headerlink" title="使用限制"></a>使用限制</h1><ul><li>Telegram Bot 渠道：上传文件大小限制为20MB，提供客户端和服务端压缩功能</li><li>Cloudflare R2 渠道：上传大小不限，但超过免费额度会扣费，详见 <a href="https://developers.cloudflare.com/r2/pricing/">Pricing | Cloudflare R2 docs</a></li></ul><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731095330160_image.png" alt="上传界面"></p><hr><p><img src="https://cloudflare-imgbed-ebz.pages.dev/file/1731095292179_image.png" alt="后台管理界面"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;博客搭建完成后，我开始考虑使用图床，了解当下主流图床平台的过程中，在 &lt;a href=&quot;http://linux.do/&quot;&gt;Linux.do&lt;/a&gt; 论坛发现了&lt;a href=&quot;https://linux.do/t/topic/251653&quot;&gt;免费图片托管解决方案&lt;/a&gt;，</summary>
      
    
    
    
    <category term="折腾" scheme="https://cq230.github.io/categories/%E6%8A%98%E8%85%BE/"/>
    
    
    <category term="图床" scheme="https://cq230.github.io/tags/%E5%9B%BE%E5%BA%8A/"/>
    
    <category term="服务搭建" scheme="https://cq230.github.io/tags/%E6%9C%8D%E5%8A%A1%E6%90%AD%E5%BB%BA/"/>
    
    <category term="Telegram" scheme="https://cq230.github.io/tags/Telegram/"/>
    
    <category term="Cloudflare" scheme="https://cq230.github.io/tags/Cloudflare/"/>
    
  </entry>
  
  <entry>
    <title>Python-装饰器</title>
    <link href="https://cq230.github.io/posts/2d63a785.html"/>
    <id>https://cq230.github.io/posts/2d63a785.html</id>
    <published>2024-08-13T11:14:12.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>Python 装饰器（Decorator）是一种允许在不修改函数或方法代码的情况下，动态地为其添加额外功能的设计模式。装饰器常用于对函数的输入或输出进行处理、增加日志记录、性能监控、访问控制等。理解装饰器的关键在于理解 Python 函数是“第一类对象”（First-class objects），即函数可以作为参数传递，也可以作为返回值返回。</p><h1 id="1-基本原理"><a href="#1-基本原理" class="headerlink" title="1. 基本原理"></a>1. 基本原理</h1><p>装饰器本质上是一个<strong>函数</strong>，它接收一个函数作为参数，并返回一个新的函数（可以是原函数或修改后的函数）。这个新函数通常包含了附加的功能逻辑。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">decorator_function</span>(<span class="params">original_function</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper_function</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        <span class="comment"># 在原函数执行前可以添加一些操作</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Before executing the function.&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        result = original_function(*args, **kwargs)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 在原函数执行后可以添加一些操作</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;After executing the function.&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> wrapper_function</span><br></pre></td></tr></table></figure><h1 id="2-基本装饰器的实现和使用"><a href="#2-基本装饰器的实现和使用" class="headerlink" title="2. 基本装饰器的实现和使用"></a>2. 基本装饰器的实现和使用</h1><p>我们可以通过 @decorator_name 的方式来使用装饰器，它会将 decorator_function 应用到目标函数上。</p><p><strong>示例</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 定义装饰器</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_decorator</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Something is happening before the function is called.&quot;</span>)</span><br><span class="line">        func()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Something is happening after the function is called.&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用装饰器</span></span><br><span class="line"><span class="meta">@my_decorator</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">say_hello</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Hello!&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调用</span></span><br><span class="line">say_hello()</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">输出</span><br><span class="line"></span><br><span class="line">Something <span class="keyword">is</span> happening before the function <span class="keyword">is</span> called.</span><br><span class="line">Hello!</span><br><span class="line">Something <span class="keyword">is</span> happening after the function <span class="keyword">is</span> called.</span><br></pre></td></tr></table></figure><p>在这个例子中，my_decorator 是装饰器函数，它接收 say_hello 作为参数并返回 wrapper。当 say_hello 被调用时，实际调用的是 wrapper 函数，这样可以在原函数的前后增加功能。</p><h1 id="3-带参数的装饰器"><a href="#3-带参数的装饰器" class="headerlink" title="3. 带参数的装饰器"></a>3. 带参数的装饰器</h1><p>装饰器可以接收参数。要实现这一点，我们需要再嵌套一层函数。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">repeat</span>(<span class="params">num_times</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator_repeat</span>(<span class="params">func</span>):</span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(num_times):</span><br><span class="line">                func(*args, **kwargs)</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator_repeat</span><br><span class="line"></span><br><span class="line"><span class="meta">@repeat(<span class="params">num_times=<span class="number">3</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>(<span class="params">name</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Hello <span class="subst">&#123;name&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">greet(<span class="string">&quot;Alice&quot;</span>)</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">输出</span><br><span class="line"></span><br><span class="line">Hello Alice</span><br><span class="line">Hello Alice</span><br><span class="line">Hello Alice</span><br></pre></td></tr></table></figure><p>在这里，repeat 是一个带参数的装饰器。repeat(num_times) 返回 decorator_repeat，然后 decorator_repeat 装饰 greet 函数。每次调用 greet(“Alice”)，它会根据 num_times 重复执行。</p><h1 id="4-装饰器的应用场景"><a href="#4-装饰器的应用场景" class="headerlink" title="4. 装饰器的应用场景"></a>4. 装饰器的应用场景</h1><p>•<strong>日志记录</strong>：记录函数的执行时间、参数和返回值等。</p><p>•<strong>性能测试</strong>：计算函数执行时间，判断性能。</p><p>•<strong>访问控制</strong>：验证用户权限。</p><p>•<strong>缓存</strong>：将函数结果缓存起来，避免重复计算。</p><h1 id="5-functools-wraps-保留原函数元数据"><a href="#5-functools-wraps-保留原函数元数据" class="headerlink" title="5. functools.wraps 保留原函数元数据"></a>5. functools.wraps 保留原函数元数据</h1><p>使用装饰器后，原函数的元数据（如函数名和文档字符串）会被覆盖。为了解决这个问题，可以使用 functools.wraps 来保持原函数的信息。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Calling decorated function&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br></pre></td></tr></table></figure><p>这样，装饰器 wrapper 会保留被装饰函数的元数据。</p><h1 id="6-类方法的装饰器"><a href="#6-类方法的装饰器" class="headerlink" title="6. 类方法的装饰器"></a>6. 类方法的装饰器</h1><p>装饰器也可以用于类的方法，特别是在需要对实例方法或类方法应用通用功能（如日志记录或权限检查）时。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">method_decorator</span>(<span class="params">method</span>):</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">method</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">self, *args, **kwargs</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Calling method <span class="subst">&#123;method.__name__&#125;</span> with args <span class="subst">&#123;args&#125;</span> and kwargs <span class="subst">&#123;kwargs&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> method(<span class="variable language_">self</span>, *args, **kwargs)</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span>:</span><br><span class="line"><span class="meta">    @method_decorator</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">display</span>(<span class="params">self, value</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Display: <span class="subst">&#123;value&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用</span></span><br><span class="line">obj = MyClass()</span><br><span class="line">obj.display(<span class="string">&quot;Test&quot;</span>)</span><br></pre></td></tr></table></figure><h1 id="7-总结"><a href="#7-总结" class="headerlink" title="7. 总结"></a>7. 总结</h1><p>装饰器是非常强大的工具，可以在函数前后添加额外逻辑。使用装饰器可以有效提升代码的可复用性和可维护性。掌握装饰器可以帮助开发者在实际项目中更加优雅地管理代码。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Python 装饰器（Decorator）是一种允许在不修改函数或方法代码的情况下，动态地为其添加额外功能的设计模式。装饰器常用于对函数的输入或输出进行处理、增加日志记录、性能监控、访问控制等。理解装饰器的关键在于理解 Python 函数是“第一类对象”（First-cla</summary>
      
    
    
    
    <category term="编程学习" scheme="https://cq230.github.io/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Python" scheme="https://cq230.github.io/tags/Python/"/>
    
    <category term="编程" scheme="https://cq230.github.io/tags/%E7%BC%96%E7%A8%8B/"/>
    
    <category term="Python基础" scheme="https://cq230.github.io/tags/Python%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>CentOS安装Python3环境</title>
    <link href="https://cq230.github.io/posts/44579ac3.html"/>
    <id>https://cq230.github.io/posts/44579ac3.html</id>
    <published>2024-02-29T11:10:35.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、安装环境依赖"><a href="#一、安装环境依赖" class="headerlink" title="一、安装环境依赖"></a>一、安装环境依赖</h1><h2 id="1-安装环境依赖："><a href="#1-安装环境依赖：" class="headerlink" title="1. 安装环境依赖："></a>1. 安装环境依赖：</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel</span><br></pre></td></tr></table></figure><h2 id="2-安装-gcc-编译器（有可能已经安装）"><a href="#2-安装-gcc-编译器（有可能已经安装）" class="headerlink" title="2. 安装 gcc 编译器（有可能已经安装）"></a>2. 安装 gcc 编译器（有可能已经安装）</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install gcc -y</span><br></pre></td></tr></table></figure><h1 id="二、安装-Python3"><a href="#二、安装-Python3" class="headerlink" title="二、安装 Python3"></a><strong>二、安装 Python3</strong></h1><p>以 Python3.7 为例讲解。</p><h2 id="1-下载-Python-安装包"><a href="#1-下载-Python-安装包" class="headerlink" title="1. 下载 Python 安装包"></a>1. 下载 Python 安装包</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz</span><br></pre></td></tr></table></figure><p>因为下载很慢，所以可以在本地通过更快的方式下载后再上传到服务器。</p><h2 id="2-将安装包移动到-usr-local-文件夹下"><a href="#2-将安装包移动到-usr-local-文件夹下" class="headerlink" title="2. 将安装包移动到 /usr/local 文件夹下"></a>2. 将安装包移动到 <code>/usr/local</code> 文件夹下</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mv</span> Python-3.7.4.tgz /usr/local/</span><br></pre></td></tr></table></figure><h2 id="3-在-local-目录下创建-Python3-目录"><a href="#3-在-local-目录下创建-Python3-目录" class="headerlink" title="3. 在 local 目录下创建 Python3 目录"></a>3. 在 local 目录下创建 Python3 目录</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> /usr/local/python3</span><br></pre></td></tr></table></figure><h2 id="4-进入的-Python-安装包压缩包所在的目录"><a href="#4-进入的-Python-安装包压缩包所在的目录" class="headerlink" title="4. 进入的 Python 安装包压缩包所在的目录"></a>4. 进入的 Python 安装包压缩包所在的目录</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /usr/local/</span><br></pre></td></tr></table></figure><h2 id="5-解压安装包"><a href="#5-解压安装包" class="headerlink" title="5. 解压安装包"></a>5. 解压安装包</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -xvf Python-3.7.4.tgz</span><br></pre></td></tr></table></figure><h2 id="6-进入解压后的目录"><a href="#6-进入解压后的目录" class="headerlink" title="6. 进入解压后的目录"></a>6. 进入解压后的目录</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /usr/local/Python-3.7.4/</span><br></pre></td></tr></table></figure><h2 id="7-配置安装目录"><a href="#7-配置安装目录" class="headerlink" title="7. 配置安装目录"></a>7. 配置安装目录</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./configure --prefix=/usr/local/python3</span><br></pre></td></tr></table></figure><h2 id="8-编译源码"><a href="#8-编译源码" class="headerlink" title="8. 编译源码"></a>8. 编译源码</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">make</span><br></pre></td></tr></table></figure><h2 id="9-执行源码安装"><a href="#9-执行源码安装" class="headerlink" title="9. 执行源码安装"></a>9. 执行源码安装</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">make install</span><br></pre></td></tr></table></figure><p>这一步可能会出现报错 <code>ModuleNotFoundError: No module named &#39;_ctypes&#39;</code>，这是因为缺少依赖包 <code>libffi-devel</code>，解决方法可参考 <a href="https://cloud.tencent.com/developer/article/1667260?from_column=20421&from=20421">https://cloud.tencent.com/developer/article/1667260</a>。</p><h2 id="10-创建软连接"><a href="#10-创建软连接" class="headerlink" title="10. 创建软连接"></a>10. 创建软连接</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ln</span> -s /usr/local/python3/bin/python3  /usr/bin/python3</span><br></pre></td></tr></table></figure><h2 id="11-测试"><a href="#11-测试" class="headerlink" title="11. 测试"></a>11. 测试</h2><p>输入 <code>python3</code> 打印：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Python 3.7.4 (default, Sep  6 2020, 09:22:23)</span><br><span class="line">[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux</span><br><span class="line">Type <span class="string">&quot;help&quot;</span>, <span class="string">&quot;copyright&quot;</span>, <span class="string">&quot;credits&quot;</span> or <span class="string">&quot;license&quot;</span> <span class="keyword">for</span> more information.</span><br><span class="line">&gt;&gt;&gt;</span><br></pre></td></tr></table></figure><p>即说明 Python 安装成功。</p><h1 id="三、安装-pip3"><a href="#三、安装-pip3" class="headerlink" title="三、安装 pip3"></a><strong>三、安装 pip3</strong></h1><h2 id="1-安装依赖（非必要）"><a href="#1-安装依赖（非必要）" class="headerlink" title="1. 安装依赖（非必要）"></a>1. 安装依赖（非必要）</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> yum install openssl-devel -y</span><br><span class="line"><span class="built_in">sudo</span> yum install zlib-devel -y</span><br></pre></td></tr></table></figure><h2 id="2-安装-setuptools"><a href="#2-安装-setuptools" class="headerlink" title="2. 安装 setuptools"></a>2. 安装 setuptools</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载安装文件</span></span><br><span class="line">wget --no-check-certificate https://pypi.python.org/packages/source/s/setuptools/setuptools-19.6.tar.gz#md5=c607dd118eae682c44ed146367a17e26</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压</span></span><br><span class="line">tar -zxvf setuptools-19.6.tar.gz</span><br><span class="line"><span class="built_in">cd</span> setuptools-19.6</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行安装</span></span><br><span class="line"><span class="built_in">sudo</span> python3 setup.py build</span><br><span class="line"><span class="built_in">sudo</span> python3 setup.py install</span><br></pre></td></tr></table></figure><h2 id="3-安装-pip3"><a href="#3-安装-pip3" class="headerlink" title="3. 安装 pip3"></a>3. 安装 pip3</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载安装文件</span></span><br><span class="line">wget --no-check-certificate https://pypi.python.org/packages/source/p/pip/pip-20.2.2.tar.gz#md5=3a73c4188f8dbad6a1e6f6d44d117eeb</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压</span></span><br><span class="line">tar -zxvf pip-20.2.2.tar.gz</span><br><span class="line"><span class="built_in">cd</span> pip-20.2.2</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行安装</span></span><br><span class="line">python3 setup.py build</span><br><span class="line"><span class="built_in">sudo</span> python3 setup.py install</span><br></pre></td></tr></table></figure><h2 id="4-测试"><a href="#4-测试" class="headerlink" title="4. 测试"></a>4. 测试</h2><p>安装完成后，输入 <code>pip3 -V</code>，打印：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip 20.2.2 from /usr/local/python3/lib/python3.7/site-packages/pip (python 3.7)</span><br></pre></td></tr></table></figure><p>则说明安装成功，可以正常安装需要的第三方库了，需要注意：</p><p>在使用时应该是 <code>pip3 xxx</code>，而不是 <code>pip xxx</code>，使之与 Python2 相区别。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、安装环境依赖&quot;&gt;&lt;a href=&quot;#一、安装环境依赖&quot; class=&quot;headerlink&quot; title=&quot;一、安装环境依赖&quot;&gt;&lt;/a&gt;一、安装环境依赖&lt;/h1&gt;&lt;h2 id=&quot;1-安装环境依赖：&quot;&gt;&lt;a href=&quot;#1-安装环境依赖：&quot; class=&quot;he</summary>
      
    
    
    
    <category term="实战开发" scheme="https://cq230.github.io/categories/%E5%AE%9E%E6%88%98%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Linux" scheme="https://cq230.github.io/tags/Linux/"/>
    
    <category term="环境配置" scheme="https://cq230.github.io/tags/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/"/>
    
    <category term="Python" scheme="https://cq230.github.io/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>Git推送报错解决办法</title>
    <link href="https://cq230.github.io/posts/82b527ab.html"/>
    <id>https://cq230.github.io/posts/82b527ab.html</id>
    <published>2024-02-23T11:07:04.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h3 id="报错信息"><a href="#报错信息" class="headerlink" title="报错信息"></a>报错信息</h3><blockquote><p>Failed to connect to <a href="http://github.com/">github.com</a> port 443</p></blockquote><h3 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h3><ol><li>修改 DNS 为： 114.114.114.114</li><li><a href="https://blog.csdn.net/zpf1813763637/article/details/128340109">修改代理</a></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;报错信息&quot;&gt;&lt;a href=&quot;#报错信息&quot; class=&quot;headerlink&quot; title=&quot;报错信息&quot;&gt;&lt;/a&gt;报错信息&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Failed to connect to &lt;a href=&quot;http://github.com/&quot;</summary>
      
    
    
    
    <category term="实战开发" scheme="https://cq230.github.io/categories/%E5%AE%9E%E6%88%98%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="开发踩坑" scheme="https://cq230.github.io/tags/%E5%BC%80%E5%8F%91%E8%B8%A9%E5%9D%91/"/>
    
    <category term="Git" scheme="https://cq230.github.io/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>练习爬取豆瓣电影Top250</title>
    <link href="https://cq230.github.io/posts/aad9beba.html"/>
    <id>https://cq230.github.io/posts/aad9beba.html</id>
    <published>2024-01-17T11:02:27.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">    练习爬取豆瓣电影排名 top250</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># 定义一个空列表，用来存储电影信息</span></span><br><span class="line">movies = []</span><br><span class="line"><span class="comment"># 定义请求头，否则请求会返回 418</span></span><br><span class="line">headers = &#123;</span><br><span class="line">    <span class="string">&#x27;User-Agent&#x27;</span>: <span class="string">&#x27;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment"># 每页25条</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="number">250</span>, <span class="number">25</span>):</span><br><span class="line">    <span class="comment"># 定义爬虫的目标网址</span></span><br><span class="line">    url = <span class="string">f&quot;https://movie.douban.com/top250?start=<span class="subst">&#123;i&#125;</span>&quot;</span></span><br><span class="line">    <span class="comment"># 发送请求，获取网页内容</span></span><br><span class="line">    response = requests.get(url, headers=headers)</span><br><span class="line">    <span class="comment"># 判断请求是否成功</span></span><br><span class="line">    <span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line">        <span class="comment"># 使用BeautifulSoup解析网页</span></span><br><span class="line">        soup = BeautifulSoup(response.text, <span class="string">&quot;html.parser&quot;</span>)</span><br><span class="line">        <span class="comment"># 找到所有的电影条目</span></span><br><span class="line">        items = soup.find_all(<span class="string">&quot;div&quot;</span>, class_=<span class="string">&quot;item&quot;</span>)</span><br><span class="line">        <span class="comment"># 遍历每个条目，提取电影信息</span></span><br><span class="line">        <span class="keyword">for</span> item <span class="keyword">in</span> items:</span><br><span class="line">            <span class="comment"># 提取电影的排名</span></span><br><span class="line">            rank = item.find(<span class="string">&quot;em&quot;</span>).text</span><br><span class="line">            <span class="comment"># 提取电影的标题</span></span><br><span class="line">            title = item.find(<span class="string">&quot;span&quot;</span>, class_=<span class="string">&quot;title&quot;</span>).text</span><br><span class="line">            <span class="comment"># 提取电影的评分</span></span><br><span class="line">            rating = item.find(<span class="string">&quot;span&quot;</span>, class_=<span class="string">&quot;rating_num&quot;</span>).text</span><br><span class="line">            <span class="comment"># 评价人数</span></span><br><span class="line">            str1 = item.find(<span class="string">&quot;div&quot;</span>, class_=<span class="string">&quot;star&quot;</span>).text</span><br><span class="line">            count = str1[<span class="number">7</span>:-<span class="number">4</span>]</span><br><span class="line">            <span class="comment"># 评价,有些评价为空</span></span><br><span class="line">            comment = <span class="string">&quot;&quot;</span></span><br><span class="line">            str2 = item.find(<span class="string">&quot;span&quot;</span>, class_=<span class="string">&quot;inq&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> str2:</span><br><span class="line">                comment = str2.text</span><br><span class="line">            <span class="comment"># 将电影信息存储到一个字典中</span></span><br><span class="line">            movie = &#123;</span><br><span class="line">                <span class="string">&quot;排名&quot;</span>: rank,</span><br><span class="line">                <span class="string">&quot;电影名&quot;</span>: title,</span><br><span class="line">                <span class="string">&quot;评分&quot;</span>: rating,</span><br><span class="line">                <span class="string">&quot;评价人数&quot;</span>: count,</span><br><span class="line">                <span class="string">&quot;评论&quot;</span>: comment</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment"># 将电影信息添加到列表中</span></span><br><span class="line">            movies.append(movie)</span><br><span class="line"></span><br><span class="line"><span class="comment"># for m in movies:</span></span><br><span class="line"><span class="comment">#     print(str(m))</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入数据到csv文件</span></span><br><span class="line">df = pd.DataFrame(movies)</span><br><span class="line"><span class="comment"># utf-8-sig编码可以保证csv文件用Excel打开不乱码</span></span><br><span class="line">df.to_csv(<span class="string">&quot;douban_top250.csv&quot;</span>, index=<span class="literal">False</span>, encoding=<span class="string">&quot;utf-8-sig&quot;</span>)</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight python&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span clas</summary>
      
    
    
    
    <category term="编程学习" scheme="https://cq230.github.io/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Python" scheme="https://cq230.github.io/tags/Python/"/>
    
    <category term="编程" scheme="https://cq230.github.io/tags/%E7%BC%96%E7%A8%8B/"/>
    
    <category term="爬虫" scheme="https://cq230.github.io/tags/%E7%88%AC%E8%99%AB/"/>
    
  </entry>
  
  <entry>
    <title>Go-变量和常量</title>
    <link href="https://cq230.github.io/posts/ec498015.html"/>
    <id>https://cq230.github.io/posts/ec498015.html</id>
    <published>2024-01-05T10:58:43.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h1 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h1><p>变量定义的标准格式为：变量声明以关键字 var 开头，后置变量类型，行尾不加分号。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//var 变量名 变量类型</span></span><br><span class="line"><span class="keyword">var</span> name <span class="keyword">type</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// go 语言支持同时定义多个相同类型的变量</span></span><br><span class="line"><span class="comment">// var 变量名1 变量名2 变量类型</span></span><br><span class="line"><span class="keyword">var</span> name1 name2 <span class="keyword">type</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 批量定义变量</span></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">name <span class="type">string</span></span><br><span class="line">age  <span class="type">int</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>当一个变量被声明之后，如果没有显式的给它赋值，系统自动赋予它该类型的零值：</p><table><thead><tr><th>类型</th><th>默认值</th></tr></thead><tbody><tr><td>整型</td><td>0</td></tr><tr><td>浮点型</td><td>0.0</td></tr><tr><td>字符串</td><td>空字符串</td></tr><tr><td>布尔型</td><td>false</td></tr><tr><td>切片、函数、指针变量</td><td>nil</td></tr></tbody></table><h2 id="变量初始化"><a href="#变量初始化" class="headerlink" title="变量初始化"></a>变量初始化</h2><p>短变量声明并初始化：（只能使用在函数内部）</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="comment">// := 自动推导</span></span><br><span class="line">name := <span class="string">&quot;jack&quot;</span></span><br><span class="line">age := <span class="number">18</span></span><br><span class="line"></span><br><span class="line">fmt.Println(name, age)</span><br><span class="line"><span class="comment">// 打印变量类型</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;%T,%T&quot;</span>, name, age)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="变量地址"><a href="#变量地址" class="headerlink" title="变量地址"></a>变量地址</h2><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"><span class="keyword">var</span> num <span class="type">int</span></span><br><span class="line">num = <span class="number">100</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;num = %d,内存地址：%p\n&quot;</span>, num, &amp;num)</span><br><span class="line"></span><br><span class="line">num = <span class="number">200</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;num = %d,内存地址：%p&quot;</span>, num, &amp;num)</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="变量交换"><a href="#变量交换" class="headerlink" title="变量交换"></a>变量交换</h2><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">a := <span class="number">100</span></span><br><span class="line">b := <span class="number">200</span></span><br><span class="line">a, b = b, a</span><br><span class="line">fmt.Println(a, b)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="匿名变量"><a href="#匿名变量" class="headerlink" title="匿名变量"></a>匿名变量</h2><p>匿名变量的特点是一个下划线 “_” ，”_” 本身就是一个特殊的标识符，被称为空白标识符。它可以像其它标识符那样用于变量的声明或赋值（任何类型都可以赋值给它），<strong>但任何赋给这个标识符的值都将被抛弃</strong>，因此这些值不能在后续的代码中使用，也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时，只需要在变量声明的地方使用下划线替换即可。例如：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">test01</span><span class="params">()</span></span> (<span class="type">int</span>, <span class="type">int</span>) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">100</span>, <span class="number">200</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">a, _ := test01() <span class="comment">// 只获取第一个返回值</span></span><br><span class="line">_, b := test01() <span class="comment">// 只获取第二个返回值</span></span><br><span class="line">fmt.Println(a, b)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="变量的作用域"><a href="#变量的作用域" class="headerlink" title="变量的作用域"></a>变量的作用域</h2><h3 id="局部变量"><a href="#局部变量" class="headerlink" title="局部变量"></a>局部变量</h3><p>在函数体内声明的变量称之为局部变量，它们的作用域只在函数体内，函数的参数和返回值变量都属于局部变量。</p><h3 id="全局变量"><a href="#全局变量" class="headerlink" title="全局变量"></a>全局变量</h3><p>在函数体外声明的变量称之为局部变量，全局变量只需在一个源文件中定义，就可以在所有源文件中使用，当然，不包含这个全局变量的源文件需要使用 “import” 关键字引入全局变量所在的源文件之后才能使用这个全局变量。</p><p>全局变量声明<strong>必须</strong>以 var 关键字开头，如果想要在外部包中使用全局变量的首字母必须大写。</p><p>全局变量和局部变量名称可以相同，但是函数体内的局部变量会被优先考虑。（就近原则）</p><h1 id="常量"><a href="#常量" class="headerlink" title="常量"></a>常量</h1><p>常量是一个简单值的标识符，在程序运行时，不会被修改的量。</p><p>常量中的数据类型只可以是 布尔型、数字型（整数型、浮点型和复数）和字符串型。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//显式类型定义</span></span><br><span class="line"><span class="keyword">const</span> a <span class="type">string</span> = <span class="string">&quot;hello,world!&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//隐式类型定义</span></span><br><span class="line"><span class="keyword">const</span> b = <span class="string">&quot;hello,world!&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//多个相同类型的声明</span></span><br><span class="line"><span class="keyword">const</span> c_name1, c_name2 = value1, value2</span><br><span class="line"></span><br><span class="line"><span class="comment">//批量定义常量</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span>(</span><br><span class="line">d = <span class="string">&quot;haha&quot;</span></span><br><span class="line">e = <span class="string">&quot;hey&quot;</span></span><br><span class="line">f          <span class="comment">// f 的值为&quot;hey&quot;</span></span><br><span class="line">g          <span class="comment">// g 的值为&quot;hey&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">一组常量中，如果某个常量没有初始值，默认和上一行一致</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><h3 id="iota"><a href="#iota" class="headerlink" title="iota"></a>iota</h3><p>iota，特殊常量，可以认为是一个可以被编译器修改的常量。</p><p>iota 在 const <strong>关键字出现时将被重置为 0</strong> (const 内部的第一行之前)，const 中每新增一行常量声明将使 iota 计数一次 (iota 可理解为 const 语句块中的行索引)。</p><p>第一个 iota 等于 0，每当 iota 在新的一行被使用时，它的值都会自动加 1；所以 a&#x3D;0, b&#x3D;1, c&#x3D;2 可以简写为如下形式：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> (</span><br><span class="line">    a = <span class="literal">iota</span></span><br><span class="line">    b</span><br><span class="line">    c</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><strong>iota 用法</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    <span class="keyword">const</span> (</span><br><span class="line">            a = <span class="literal">iota</span>   <span class="comment">//0</span></span><br><span class="line">            b          <span class="comment">//1</span></span><br><span class="line">            c          <span class="comment">//2</span></span><br><span class="line">            d = <span class="string">&quot;ha&quot;</span>   <span class="comment">//独立值，iota += 1</span></span><br><span class="line">            e          <span class="comment">//&quot;ha&quot;   iota += 1</span></span><br><span class="line">            f = <span class="number">100</span>    <span class="comment">//iota +=1</span></span><br><span class="line">            g          <span class="comment">//100  iota +=1</span></span><br><span class="line">            h = <span class="literal">iota</span>   <span class="comment">//7,恢复计数</span></span><br><span class="line">            i          <span class="comment">//8</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line">a2 = <span class="literal">iota</span> <span class="comment">//0，因为 const 关键字出现时 iota 被重置为0</span></span><br><span class="line">b2</span><br><span class="line">c2</span><br><span class="line">)</span><br><span class="line">    fmt.Println(a,b,c,d,e,f,g,h,i)</span><br><span class="line">         <span class="comment">// 0 1 2 ha ha 100 100 7 8</span></span><br><span class="line"></span><br><span class="line">fmt.Println(a2,b2,c2)</span><br><span class="line"><span class="comment">// 0,1,2</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>再看个有趣的的 iota 实例：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line">    i=<span class="number">1</span>&lt;&lt;<span class="literal">iota</span></span><br><span class="line">    j=<span class="number">3</span>&lt;&lt;<span class="literal">iota</span></span><br><span class="line">    k</span><br><span class="line">    l</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    fmt.Println(<span class="string">&quot;i=&quot;</span>,i)</span><br><span class="line">    fmt.Println(<span class="string">&quot;j=&quot;</span>,j)</span><br><span class="line">    fmt.Println(<span class="string">&quot;k=&quot;</span>,k)</span><br><span class="line">    fmt.Println(<span class="string">&quot;l=&quot;</span>,l)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">i= 1</span></span><br><span class="line"><span class="comment">j= 6</span></span><br><span class="line"><span class="comment">k= 12</span></span><br><span class="line"><span class="comment">l= 24 </span></span><br><span class="line"><span class="comment">iota 表示从 0 开始自动加 1，所以 i=1&lt;&lt;0, j=3&lt;&lt;1（&lt;&lt; 表示左移的意思）k=3&lt;&lt;2，l=3&lt;&lt;3</span></span><br><span class="line"><span class="comment">简单表述:</span></span><br><span class="line"><span class="comment">i=1：左移 0 位，不变仍为 1。</span></span><br><span class="line"><span class="comment">j=3：左移 1 位，变为二进制 110，即 6。</span></span><br><span class="line"><span class="comment">k=3：左移 2 位，变为二进制 1100，即 12。</span></span><br><span class="line"><span class="comment">l=3：左移 3 位，变为二进制 11000，即 24。</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;变量&quot;&gt;&lt;a href=&quot;#变量&quot; class=&quot;headerlink&quot; title=&quot;变量&quot;&gt;&lt;/a&gt;变量&lt;/h1&gt;&lt;p&gt;变量定义的标准格式为：变量声明以关键字 var 开头，后置变量类型，行尾不加分号。&lt;/p&gt;
&lt;figure class=&quot;highlight</summary>
      
    
    
    
    <category term="编程学习" scheme="https://cq230.github.io/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="Go" scheme="https://cq230.github.io/tags/Go/"/>
    
    <category term="Go基础" scheme="https://cq230.github.io/tags/Go%E5%9F%BA%E7%A1%80/"/>
    
    <category term="编程" scheme="https://cq230.github.io/tags/%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>IDEA快捷键</title>
    <link href="https://cq230.github.io/posts/610bdf07.html"/>
    <id>https://cq230.github.io/posts/610bdf07.html</id>
    <published>2022-05-30T16:12:10.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<table><thead><tr><th align="center">快捷键</th><th align="center">操作</th></tr></thead><tbody><tr><td align="center">Ctrl + D</td><td align="center">复制当前行到下一行</td></tr><tr><td align="center">Ctrl + Y</td><td align="center">删除当前行</td></tr><tr><td align="center">Alt + insert</td><td align="center">创建构造器</td></tr><tr><td align="center">Shift + Ctrl + Enter</td><td align="center">快速补全行末分号</td></tr><tr><td align="center">Ctrl+Alt+L</td><td align="center">代码格式</td></tr><tr><td align="center">Shift + Ctrl + V</td><td align="center">打开粘贴板的历史记录</td></tr><tr><td align="center">Ctrl + H</td><td align="center">查看当前类的继承关系</td></tr><tr><td align="center">Ctrl + O</td><td align="center">快速重写方法</td></tr><tr><td align="center">shift + Alt + ↓</td><td align="center">移动当前行到下一行</td></tr><tr><td align="center">ctrl + Alt + T</td><td align="center">将代码包在一个块中</td></tr><tr><td align="center">Ctrl+F12</td><td align="center">查看当前结构</td></tr><tr><td align="center">shift+Esc</td><td align="center">关闭当前选项页</td></tr></tbody></table>]]></content>
    
    
      
      
    <summary type="html">&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;快捷键&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;操作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;Ctrl + D&lt;/td&gt;
&lt;td </summary>
      
    
    
    
    <category term="编程学习" scheme="https://cq230.github.io/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="idea" scheme="https://cq230.github.io/tags/idea/"/>
    
    <category term="IDE" scheme="https://cq230.github.io/tags/IDE/"/>
    
  </entry>
  
  <entry>
    <title>Java-this和super</title>
    <link href="https://cq230.github.io/posts/13fbb470.html"/>
    <id>https://cq230.github.io/posts/13fbb470.html</id>
    <published>2022-03-25T11:20:34.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h1 id="this"><a href="#this" class="headerlink" title="this"></a>this</h1><h4 id="可以用来修饰或调用"><a href="#可以用来修饰或调用" class="headerlink" title="可以用来修饰或调用"></a>可以用来修饰或调用</h4><p>属性、方法、构造器</p><h4 id="this修饰属性和方法时"><a href="#this修饰属性和方法时" class="headerlink" title="this修饰属性和方法时"></a>this修饰属性和方法时</h4><p>this理解为：当前对象或者当前正在创建的对象（this.表示先在本类中查找，找不到再去父类找）</p><p>在类的方法中，我们可以使用”this.属性”或”this.方法”的方式，调用当前对象属性或方法。但是通常情况下，我们都选择省略”this.”。特殊情况下，如果方法的形参和类的属性同名时（例如set方法），我们必须显式的使用”this.变量”的方式，表明此变量是属性，而非形参。</p><p>在类的构造器中，我们可以使用”this.属性”或”this.方法”的方式，调用当前正在创建的对象属性或方法。但是通常情况下，我们都选择省略”this.”。特殊情况下，如果方法的形参和类的属性同名时，我们必须显式的使用”this.变量”的方式，表明此变量是属性，而非形参。</p><h4 id="this调用构造器时"><a href="#this调用构造器时" class="headerlink" title="this调用构造器时"></a>this调用构造器时</h4><p>我们在类的构造器中，可以显式的使用”this(形参列表)”的方式，调用本类中指定的<strong>其他</strong>构造器；</p><p>构造器中不能通过”this(形参列表)”的方式调用自己；</p><p>如果一个类中有n个构造器，则最多有<strong>n-1</strong>个构造器中使用了”this(形参列表)”,至少<strong>1</strong>个使用”super(形参列表)”；</p><p>规定：”this(形参列表)”必须声明在当前构造器的<strong>首行</strong>,所以一个构造器内部最多只能声明一个”this(形参列表)”，用来调用其他的构造器；</p><p>注意调用逻辑不能陷入死循环。</p><h1 id="super"><a href="#super" class="headerlink" title="super"></a>super</h1><ol><li><strong>super理解为：父类的…</strong></li><li>super(形参列表)指调用父类对应的构造器，也必须声明在当前构造器<strong>首行</strong>；</li><li>因此在类的构造器中”this(形参列表)”或”super(形参列表)”只能二选一，不能同时出现；</li><li>在构造器的首行，没有显式的声明”this(形参列表)”或”super(形参列表)”，则<strong>默认</strong>调用的是父类中空参构造器:super()</li></ol><h1 id="总结："><a href="#总结：" class="headerlink" title="总结："></a>总结：</h1><p>类的构造器首行不是”this(形参列表)”就是”super(形参列表)”</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;this&quot;&gt;&lt;a href=&quot;#this&quot; class=&quot;headerlink&quot; title=&quot;this&quot;&gt;&lt;/a&gt;this&lt;/h1&gt;&lt;h4 id=&quot;可以用来修饰或调用&quot;&gt;&lt;a href=&quot;#可以用来修饰或调用&quot; class=&quot;headerlink&quot; title=</summary>
      
    
    
    
    <category term="编程学习" scheme="https://cq230.github.io/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="编程" scheme="https://cq230.github.io/tags/%E7%BC%96%E7%A8%8B/"/>
    
    <category term="Java" scheme="https://cq230.github.io/tags/Java/"/>
    
    <category term="Java 基础" scheme="https://cq230.github.io/tags/Java-%E5%9F%BA%E7%A1%80/"/>
    
    <category term="Java 关键字" scheme="https://cq230.github.io/tags/Java-%E5%85%B3%E9%94%AE%E5%AD%97/"/>
    
  </entry>
  
  <entry>
    <title>Java-static</title>
    <link href="https://cq230.github.io/posts/80c02825.html"/>
    <id>https://cq230.github.io/posts/80c02825.html</id>
    <published>2022-03-13T09:43:32.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<h4 id="静态方法调用"><a href="#静态方法调用" class="headerlink" title="静态方法调用"></a>静态方法调用</h4><p>直接 <em>类名.方法名</em> 调用</p><h4 id="非静态方法调用"><a href="#非静态方法调用" class="headerlink" title="非静态方法调用"></a>非静态方法调用</h4><p>需要实例化对应的类来调用</p><h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><ul><li><p>因为static和非static类型的方法加载时间不同，因此static类型的方法<strong>无法</strong>调用<strong>非static类型的方法或属性</strong></p></li><li><p>创建了类的多个对象后，多个对象<strong>共享</strong>同一个静态变量；当某一个对象修改了该静态变量后，会导致其他对象<strong>再</strong>调用该静态变量时，是修改过的。（在修改之前调用静态变量则是修改之前的值）</p></li><li><p>static方法中，不能使用this、super关键字</p></li><li><p>static类型的结构随着类的加载而加载</p></li><li><p>静态结构前没有声明的都是省略的 “类名.”</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h4 id=&quot;静态方法调用&quot;&gt;&lt;a href=&quot;#静态方法调用&quot; class=&quot;headerlink&quot; title=&quot;静态方法调用&quot;&gt;&lt;/a&gt;静态方法调用&lt;/h4&gt;&lt;p&gt;直接 &lt;em&gt;类名.方法名&lt;/em&gt; 调用&lt;/p&gt;
&lt;h4 id=&quot;非静态方法调用&quot;&gt;&lt;a href=&quot;#非</summary>
      
    
    
    
    <category term="编程学习" scheme="https://cq230.github.io/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="编程" scheme="https://cq230.github.io/tags/%E7%BC%96%E7%A8%8B/"/>
    
    <category term="Java" scheme="https://cq230.github.io/tags/Java/"/>
    
    <category term="Java 基础" scheme="https://cq230.github.io/tags/Java-%E5%9F%BA%E7%A1%80/"/>
    
    <category term="Java 关键字" scheme="https://cq230.github.io/tags/Java-%E5%85%B3%E9%94%AE%E5%AD%97/"/>
    
  </entry>
  
  <entry>
    <title>Java-函数式接口</title>
    <link href="https://cq230.github.io/posts/23e07c64.html"/>
    <id>https://cq230.github.io/posts/23e07c64.html</id>
    <published>2022-02-24T12:08:43.000Z</published>
    <updated>2026-03-24T15:40:31.350Z</updated>
    
    <content type="html"><![CDATA[<p>如果一个接口中，只声明了一个抽象方法，则此接口就称为函数式接口。</p><p>我们可以在一个接口上使用 @FunctionalInterface 注解，这样做可以检查它是否是一个函数式接口。</p><p>在 java.util.function 包下定义了Java 8 的丰富的函数式接口</p><ul><li>Java从诞生日起就是一直倡导“一切皆对象”，在 Java 里面面向对象(OOP)编程是一切。但是随着 python、scala 等语言的兴起和新技术的挑战，Java 不得不做出调整以便支持更加广泛的技术要求，也即 java 不但可以支持 OOP 还可以支持 OOF（面向函数编程）</li><li>在函数式编程语言当中，函数被当做一等公民对待。在将函数作为一等公民的编程语言中，Lambda 表达式的类型是函数。但是在 Java8 中，有所不同。在 Java8 中，Lambda 表达式是对象，而不是函数，它们必须依附于一类特别的对象类型——函数式接口。</li><li>简单的说，在 Java8 中，Lambda 表达式就是一个函数式接口的实例。这就是 Lambda 表达式和函数式接口的关系。也就是说，只要一个对象是函数式接口的实例，那么该对象就可以用 Lambda 表达式来表示。<br>所以以前用匿名实现类表示的现在都可以用 Lambda 表达式来写。</li></ul><h4 id="Java内置的函数式接口介绍及使用举例"><a href="#Java内置的函数式接口介绍及使用举例" class="headerlink" title="Java内置的函数式接口介绍及使用举例"></a>Java内置的函数式接口介绍及使用举例</h4><p>前4种为 Java 内置的四大核心函数式接口</p><table><thead><tr><th align="center">函数式接口</th><th align="center">参数类型</th><th align="center">返回类型</th><th align="center">用途</th></tr></thead><tbody><tr><td align="center"><strong>Consumer 消费型接口</strong></td><td align="center">T</td><td align="center">void</td><td align="center">对类型为T的对象应用操作，包含方法：void accept(T t)</td></tr><tr><td align="center"><strong>Supplier 供给型接口</strong></td><td align="center">无</td><td align="center">T</td><td align="center">返回类型为T的对象，包含方法：T get()</td></tr><tr><td align="center"><strong>Function&lt;T, R&gt; 函数型接口</strong></td><td align="center">T</td><td align="center">R</td><td align="center">对类型为T的对象应用操作，并返回结果。结果是R类型的对象。包含方法：R apply(T t)</td></tr><tr><td align="center"><strong>Predicate 断定型接口</strong></td><td align="center">T</td><td align="center">boolean</td><td align="center">确定类型为T的对象是否满足某约束，并返回boolean 值。包含方法：boolean test(T t)</td></tr><tr><td align="center">BiFunction&lt;T,U,R&gt;</td><td align="center">T, U</td><td align="center">R</td><td align="center">对类型为T,U参数应用操作，返回R类型的结果。包含方法为：Rapply(T t,U u);</td></tr><tr><td align="center">UnaryOperator(Function子接口)</td><td align="center">T</td><td align="center">T</td><td align="center">对类型为T的对象进行一元运算，并返回T类型的结果。包含方法为：Tapply(T t);</td></tr><tr><td align="center">BinaryOperator(BiFunction子接口)</td><td align="center">T,T</td><td align="center">T</td><td align="center">对类型为T的对象进行二元运算，并返回T类型的结果。包含方法为：Tapply(T t1,T t2);</td></tr><tr><td align="center">BiConsumer&lt;T,U&gt;</td><td align="center">T,U</td><td align="center">void</td><td align="center">对类型为T,U参数应用操作。包含方法为：voidaccept(Tt,Uu)</td></tr><tr><td align="center">BiPredicate&lt;T,U&gt;</td><td align="center">T,U</td><td align="center">boolean</td><td align="center">包含方法为：booleantest(Tt,Uu)</td></tr><tr><td align="center">ToIntFunction</td><td align="center">T</td><td align="center">int</td><td align="center">计算int值的函数</td></tr><tr><td align="center">ToLongFunction</td><td align="center">T</td><td align="center">long</td><td align="center">计算long值的函数</td></tr><tr><td align="center">ToDoubleFunction</td><td align="center">T</td><td align="center">double</td><td align="center">计算double值的函数</td></tr><tr><td align="center">IntFunction</td><td align="center">int</td><td align="center">R</td><td align="center">参数为int类型的函数</td></tr><tr><td align="center">LongFunction</td><td align="center">long</td><td align="center">R</td><td align="center">参数为long类型的函数</td></tr><tr><td align="center">DoubleFunction</td><td align="center">double</td><td align="center">R</td><td align="center">参数为double类型的函数</td></tr></tbody></table>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;如果一个接口中，只声明了一个抽象方法，则此接口就称为函数式接口。&lt;/p&gt;
&lt;p&gt;我们可以在一个接口上使用 @FunctionalInterface 注解，这样做可以检查它是否是一个函数式接口。&lt;/p&gt;
&lt;p&gt;在 java.util.function 包下定义了Java 8 </summary>
      
    
    
    
    <category term="编程学习" scheme="https://cq230.github.io/categories/%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="编程" scheme="https://cq230.github.io/tags/%E7%BC%96%E7%A8%8B/"/>
    
    <category term="Java" scheme="https://cq230.github.io/tags/Java/"/>
    
    <category term="Java 基础" scheme="https://cq230.github.io/tags/Java-%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
</feed>
