<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQL &#8211; 良的世界</title>
	<atom:link href="https://www.lemonary.cn/tag/sql/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.lemonary.cn</link>
	<description></description>
	<lastBuildDate>Thu, 02 Apr 2026 06:09:30 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.lemonary.cn/wp-content/uploads/2024/12/logo-150x150.jpg</url>
	<title>SQL &#8211; 良的世界</title>
	<link>https://www.lemonary.cn</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SQL优化之注入HINT</title>
		<link>https://www.lemonary.cn/sql%e4%bc%98%e5%8c%96%e4%b9%8b%e6%b3%a8%e5%85%a5hint/</link>
					<comments>https://www.lemonary.cn/sql%e4%bc%98%e5%8c%96%e4%b9%8b%e6%b3%a8%e5%85%a5hint/#comments</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Thu, 19 Mar 2026 09:25:54 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[HINT]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=2600</guid>

					<description><![CDATA[一、说明 功能说明：为 SQL 注入 HINT 规则。SF_INJECT_HINT 方法创建的 HINT 规则 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">一、说明</h2>



<p>功能说明：为 SQL 注入 HINT 规则。SF_INJECT_HINT 方法创建的 HINT 规则无生效类别限制，如果需要为该 HINT 规则设置生效类别，可通过 SF_ALTER_HINT 过程修改实现。</p>



<p>参数说明：<br>sql_text：待注入 HINT 规则的 SQL 语句。SQL 语句必须是语法正确的增删改查语句。<br>hint_text：待注入的 HINT 规则，必须指定为非 NULL 值。<br>name：HINT 规则的名称，指定为 NULL 值时系统为其命名。<br>description：对 HINT 规则的详细描述。<br>validate：HINT 规则是否生效。TRUE 是；FALSE 否。<br>fuzzy：指定 SQL 的匹配规则为精准匹配或模糊匹配，该参数值不能为 NULL。<br>当该参数缺省时，表示使用定义 1 的语法，仅支持精准匹配。<br>该参数类型为 BOOLEAN 时，使用定义 2 或定义 3 的语法，值为 TRUE 时，为模糊匹配；<br>值为 FALSE 时，为精准匹配。<br>精准匹配时，待注入 HINT 规则的 SQL 语句必须为语法正确的 INSERT/DELETE/UPDATE/SELECT/MERGE INTO 语句（语句以 EXPLAIN/EXPLAIN FOR 开头时，去掉 EXPLAIN/EXPLAIN FOR 后的语句必须完全正确），精准匹配要求 SQL 语句完全匹配，不支持 SQL 语句中的子查询匹配；<br>模糊匹配时，待注入 HINT 规则的 SQL 语句应为非 NULL 值。<br>该参数类型为 INT 时，使用定义 4 或定义 5 的语法，值为 0 表示精确匹配，值为 1 表示模糊匹配，值为 2 表示通过 sql_text_id精确匹配。<br>fuzzy 取值为 2 时，参数 sql_text 应该输入长度为 13 的 sql_text_id 字段，如果 fuzzy 取值为 2 但参数 sql_text 依旧输入 SQL 语句，预期会报错。<br>need_clear：是否同步清空所有缓存的计划，该参数值不能为 NULL。<br>当该参数缺省时，表示使用定义 1、定义 2 或定义 4 的语法，此时默认不清空缓存计划。<br>指定该参数时，使用定义 3 和定义 5 的语法，值为 TRUE 时，清空缓存的计划；值为 FALSE 时，模糊匹配和通过 sql_text_id 匹配时，不清空缓存的计划，需要手动清除对应 SQL 的计划后，指定的 HINT 才能生效，精确匹配时，默认会删除受影响 SQL 的计划缓存。</p>



<p>返回值：执行成功返回名称，执行失败报错误信息。</p>



<h2 class="wp-block-heading">二、使用</h2>



<pre class="wp-block-code"><code>--模糊匹配
SF_INJECT_HINT('SQL语句', 'HINT', 'INJECT名称', '注释', TRUE, TRUE);</code></pre>



<p>注入HINT后需要清空对应SQL的计划缓存</p>



<pre class="wp-block-code"><code>BEGIN
    FOR RS IN (SELECT * FROM V$CACHEPLN WHERE SQLSTR LIKE '%SQL语句%') LOOP
        EXECUTE IMMEDIATE 'SP_CLEAR_PLAN_CACHE(' || RS.CACHE_ITEM || ');';
    END LOOP;
END;</code></pre>



<p>查询已经注入的HINT-系统视图</p>



<pre class="wp-block-code"><code>SELECT * FROM SYSINJECTHINT ORDER BY CRTDATE DESC;</code></pre>



<p>删除注入的HINT</p>



<pre class="wp-block-code"><code>SF_DEINJECT_HINT('INJECT名称');</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/sql%e4%bc%98%e5%8c%96%e4%b9%8b%e6%b3%a8%e5%85%a5hint/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>一条SQL实现高考赋分制</title>
		<link>https://www.lemonary.cn/%e4%b8%80%e6%9d%a1sql%e5%ae%9e%e7%8e%b0%e9%ab%98%e8%80%83%e8%b5%8b%e5%88%86%e5%88%b6/</link>
					<comments>https://www.lemonary.cn/%e4%b8%80%e6%9d%a1sql%e5%ae%9e%e7%8e%b0%e9%ab%98%e8%80%83%e8%b5%8b%e5%88%86%e5%88%b6/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Wed, 11 Jun 2025 02:08:37 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[赋分制]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=1963</guid>

					<description><![CDATA[原文地址：高考赋分制解析【上】——从理解规则到计算实践，一文读懂！高考赋分制解析【下】——从模型设计到SQL实 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>原文地址：<br><a href="https://mp.weixin.qq.com/s?__biz=MzAxNDc3ODE5MQ==&amp;mid=2653413364&amp;idx=1&amp;sn=0662a41734e51af43fe8a78817bc6b85&amp;scene=21#wechat_redirect">高考赋分制解析【上】——从理解规则到计算实践，一文读懂！</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzAxNDc3ODE5MQ==&amp;mid=2653413437&amp;idx=1&amp;sn=6040a5dc5d124de2cf57539e82ff671c&amp;chksm=81ffab9210ec7ad646565445d6aa5158947b6d63e3ed26f8aa441c82e7cfd84ce024cda25fee&amp;mpshare=1&amp;scene=2&amp;srcid=0610e5HHicqHLyIDDHYLLmvM&amp;sharer_shareinfo=d64f4c2a38ff0be52faafd1f6d85b747&amp;sharer_shareinfo_first=48d2e48b90f7a70bbaac97b961248438#rd">高考赋分制解析【下】——从模型设计到SQL实现，一文读懂！</a></p>



<h2 class="wp-block-heading">一、赋分制的由来</h2>



<p>想象一下：化学考试特别难，全省平均只有45分；而生物考试简单，平均高达75分。此时如果直接用原始分计入高考总分，选化学的同学岂不是太吃亏了？</p>



<p>所以赋分制就是要建立一个公平竞技场，让不同科目的成绩能够公平比较！</p>



<h2 class="wp-block-heading">二、赋分规则详解</h2>



<p>根据福建省教育厅文件，赋分规则分三部分：需赋分科目、等级划分和分数转换。物理、历史直接使用原始分，满分100分；政治、地理、化学、生物则需赋分转换，最终同样满分100分。</p>



<p>为什么物理和历史不用赋分呢？</p>



<p>好问题！因为物理和历史是首选科目，每个学生必须二选一，它们被认为具有较强的区分度，所以直接使用原始分。</p>



<p>接下来是等级划分，赋分制将所有考生按原始分从高到低排列，划分为5个等级，<strong>人数比例分别为</strong>：A级15%、B级35%、C级35%、D级13%、E级2%。<strong>每个等级对应不同的赋分区间</strong>：A级对应86-100分，B级71-85分，C级56-70分，D级41-55分，E级30-40分。</p>



<h2 class="wp-block-heading">三、赋分制的模拟</h2>



<p>假设全省仅有20名同学参加高考化学考试，考题特别难，分数从高到低排列如下。</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="813" height="1024" src="https://www.lemonary.cn/wp-content/uploads/2025/06/image-25-813x1024.png" alt="" class="wp-image-1964" srcset="https://www.lemonary.cn/wp-content/uploads/2025/06/image-25-813x1024.png 813w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-25-238x300.png 238w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-25-768x967.png 768w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-25.png 1017w" sizes="(max-width: 813px) 100vw, 813px" /></figure>



<p>首先是按比例划分等级，全部20名同学按照之前说的比例划分，结果是：</p>



<ul class="wp-block-list">
<li>A级（前15%）：3人（20×15%=3）</li>



<li>B级（接着35%）：7人（20×35%=7）</li>



<li>C级（接着35%）：7人（20×35%=7）</li>



<li>D级（接着13%）：2人（20×13%=2.6）</li>



<li>E级（最后2%）：1人（20×2%=0.4）</li>
</ul>



<p>注意，因为人数计算有小数，实际分配会有微调。在此例中，D级和E级的人数就有调整。</p>



<p>于是，20个学生的等级分就出来了。现在，让我们按照赋分制的规则来给这20名同学分配等级和赋分，如图所示。</p>



<figure class="wp-block-image size-full"><img decoding="async" width="993" height="732" src="https://www.lemonary.cn/wp-content/uploads/2025/06/image-26.png" alt="" class="wp-image-1965" srcset="https://www.lemonary.cn/wp-content/uploads/2025/06/image-26.png 993w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-26-300x221.png 300w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-26-768x566.png 768w" sizes="(max-width: 993px) 100vw, 993px" /></figure>



<p>完整的表格</p>



<figure class="wp-block-image size-large"><img decoding="async" width="778" height="1024" src="https://www.lemonary.cn/wp-content/uploads/2025/06/image-27-778x1024.png" alt="" class="wp-image-1966" srcset="https://www.lemonary.cn/wp-content/uploads/2025/06/image-27-778x1024.png 778w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-27-228x300.png 228w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-27-768x1011.png 768w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-27.png 993w" sizes="(max-width: 778px) 100vw, 778px" /></figure>



<p>那这些同学的赋分到底是多少呢？让我们看看几个具体同学的情况。</p>



<ul class="wp-block-list">
<li>李学神：原始分66，排名第1，属于A级，是全部考生的最高分，直接得100分；郝厉害：原始分是60分，排名第3，也是A级，处于A级的下边界。获得A级最低赋分，即86分。</li>
</ul>



<ul class="wp-block-list">
<li>牛二爷：原始分是59分，排名第4，处于B级最高，得88分；甄聪明：原始分是50，排名第10，处于B级最低，得71分。</li>



<li>小牛：原始分是49，排名第11，处于C级最高，得70分；小强：原始分是40，排名第17，处于C级最低，得56分。</li>



<li>牛漂亮：原始分18，排名第18，处于D级最高，得55分；羊美丽：原始分8，排名第19，处于D级最低，得41分。</li>



<li>朱坚强：原始分0分，排名第20，处于E级最低（亦是全部考生最低），得30分。</li>
</ul>



<p>如此，一些赋分就可填写了，如下图所示。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="746" height="1024" src="https://www.lemonary.cn/wp-content/uploads/2025/06/image-28-746x1024.png" alt="" class="wp-image-1967" srcset="https://www.lemonary.cn/wp-content/uploads/2025/06/image-28-746x1024.png 746w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-28-219x300.png 219w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-28-768x1054.png 768w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-28.png 990w" sizes="auto, (max-width: 746px) 100vw, 746px" /></figure>



<p>不过，这些不处在头尾，而是位于中间的同学，如张丫霸，艾学习等，他们的赋分又该如何计算呢？</p>



<h2 class="wp-block-heading">四、赋分制公式</h2>



<p>其实就是把考生在原始分中的排位，平移到赋分的分数段中。用一个公式来表达这个转换过程：<br><strong>赋分 = 等级下限分 + [(原始分 &#8211; 等级原始分下限) ÷ (等级原始分上限 &#8211; 等级原始分下限)] × (等级上限分 &#8211; 等级下限分)</strong></p>



<p>来，接下来我们应用公式算算张丫霸的赋分。</p>



<pre class="wp-block-code"><code>张丫霸（63）的原始分
= 等级下限分+&#91;(原始分-等级原始分下限)÷(等级原始分上限-等级原始分下限)]×(等级上限分-等级下限分)
= 86 + &#91;(63 - 60) ÷ (66 - 60)] × (100 - 86)
= 86 + &#91;3 ÷ 6] × 14
= 86 + 7
= 93</code></pre>



<p>也就是说，张丫霸化学成绩经过赋分转换后，从原始的63分变成了93分！</p>



<p>这正是赋分制的调节作用。你63分在A级原始分范围内的相对位置，对应到了A级赋分范围内的93分。</p>



<p>其实总分第一即满分的规矩，通过代公式也能计算得到，牛学神66分同时也是最高分，带入公式计算会发现，（66 &#8211; 60) ÷ (66 &#8211; 60)=1，然后86+1× 14=100分。</p>



<h2 class="wp-block-heading">五、公式带入</h2>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="856" height="1024" src="https://www.lemonary.cn/wp-content/uploads/2025/06/image-29-856x1024.png" alt="" class="wp-image-1968" srcset="https://www.lemonary.cn/wp-content/uploads/2025/06/image-29-856x1024.png 856w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-29-251x300.png 251w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-29-768x919.png 768w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-29.png 993w" sizes="auto, (max-width: 856px) 100vw, 856px" /></figure>



<p>记住这几个要点：政治、地理、化学、生物四科需要赋分转换；按全省排名分为A-E五级；每个等级内，通过等比例转换计算最终赋分。</p>



<p>接下来，我尝试和大家探讨探讨如何写出高效健壮的SQL，来模拟赋分制的系统实现。</p>



<h2 class="wp-block-heading">六、<strong>数据准备</strong></h2>



<pre class="wp-block-code"><code>--创建学生成绩表：这就是我们的数字化考场
CREATE TABLE student_scores (
    student_id NUMBER(10),                -- 学生ID，每个学生的唯一标识
    student_name VARCHAR2(50),            -- 学生姓名
    raw_score NUMBER(5,2),                -- 原始分数，支持小数点后两位
    grade CHAR(1),                        -- 等级(A-E)，系统计算后填入
    converted_score NUMBER(5,2)           -- 赋分结果，这就是最终的神奇转换结果
);</code></pre>



<pre class="wp-block-code"><code>
-- 重现上次的考试现场：20名同学的化学考试原始成绩
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (1, '牛学神', 66);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (2, '张丫霸', 63);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (3, '郝厉害', 60);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (4, '牛二爷', 59);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (5, '艾学习', 58);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (6, '孙奋斗', 55);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (7, '钱多多', 53);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (8, '陈努力', 52);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (9, '王大锤', 51);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (10, '甄聪明', 50);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (11, '小牛', 49);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (12, '小马', 47);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (13, '小翠', 45);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (14, '小丽', 43);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (15, '小南', 42);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (16, '小北', 41);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (17, '小强', 40);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (18, '牛漂亮', 18);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (19, '羊美丽', 8);
INSERT INTO student_scores(student_id, student_name, raw_score) VALUES (20, '朱坚强', 0);
COMMIT;</code></pre>



<h2 class="wp-block-heading">七、<strong>算法核心</strong></h2>



<h3 class="wp-block-heading"><strong>第一步</strong></h3>



<p><strong>排名计算 &#8211; 给每个学生找到自己的位置</strong></p>



<pre class="wp-block-code"><code>-- 第一步：排名计算 - 这是一切计算的基础
ranked_students AS (
    SELECT 
        student_id,
        student_name,
        raw_score,
        -- 使用RANK()函数处理同分情况，确保同分学生得到相同排名
        RANK() OVER (ORDER BY raw_score DESC) as rank_num,
        -- COUNT() OVER()获取总人数，这个技巧避免了额外的子查询
        COUNT(*) OVER () as total_count
    FROM student_scores
)</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>说明：<br><strong>ROW_NUMBER()</strong>：1,2,3,4&#8230;（强制给同分学生不同排名）<br><strong>RANK()</strong>：1,2,2,2,5,6&#8230;（同分学生得到相同排名，后续排名跳跃）</p>
</blockquote>



<h3 class="wp-block-heading">第二步</h3>



<p><strong>等级划分 &#8211; 按福建省标准分配ABCDE五个等级</strong></p>



<p>接下来是关键的等级分配，这一步必须严格按照福建省教育厅的规定：A级15%，B级35%，C级35%，D级13%，E级2%。</p>



<pre class="wp-block-code"><code>-- 第二步：等级划分 - 严格按照福建省的15%、35%、35%、13%、2%比例
graded_students AS (
    SELECT 
        student_id, student_name, raw_score, rank_num, total_count,
        CASE
            -- A级：前15%（对于20人：CEIL(20 × 0.15) = 3，即前3名）
            WHEN rank_num &lt;= CEIL(total_count * 0.15) THEN 'A'
            -- B级：前15%之后到前50%（第4名到第10名）
            WHEN rank_num &lt;= CEIL(total_count * 0.50) THEN 'B'
            -- C级：前50%之后到前85%（第11名到第17名）
            WHEN rank_num &lt;= CEIL(total_count * 0.85) THEN 'C'
            -- D级：前85%之后到前98%（第18名到第19名）
            WHEN rank_num &lt;= CEIL(total_count * 0.98) THEN 'D'
            -- E级：剩下的就是最后2%（第20名）
            ELSE 'E'
        END as grade
    FROM ranked_students
)</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>说明：<br>这里使用累积比例的简洁写法。由于CASE语句按顺序执行，前15%先被分配A级，剩下的15%-50%自动成为B级，以此类推。这比写区间判断更简洁且不易出错。</p>
</blockquote>



<h3 class="wp-block-heading"><strong>第三步</strong></h3>



<p><strong>计算等级边界 &#8211; 找出每个等级的分数范围</strong></p>



<p>现在我们要找出每个等级内的最高分和最低分，这些边界值是等比例转换公式的关键参数。</p>



<pre class="wp-block-code"><code>-- 第三步：计算每个等级内的原始分数范围，这些数据是公式计算的基础
grade_ranges AS (
    SELECT 
        grade,
        -- 找出等级内的最高和最低原始分数
        MAX(raw_score) as max_raw_score,      -- 等级内最高原始分
        MIN(raw_score) as min_raw_score,      -- 等级内最低原始分
        -- 对应的赋分区间上限（福建省标准）
        CASE grade 
            WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
            WHEN 'D' THEN 55 ELSE 40 
        END as max_converted_score,
        -- 对应的赋分区间下限（福建省标准）
        CASE grade 
            WHEN 'A' THEN 86 WHEN 'B' THEN 71 WHEN 'C' THEN 56 
            WHEN 'D' THEN 41 ELSE 30 
        END as min_converted_score
    FROM graded_students 
    GROUP BY grade
)</code></pre>



<p>这一步很巧妙！通过GROUP BY grade，我们能分别统计出每个等级的分数范围。比如A级的原始分范围是60-66分（郝厉害到牛学神），对应的赋分范围是86-100分。</p>



<p>这样A级原始分的60-66这6分的跨度，要映射到赋分的86-100这14分的跨度上。这就是等比例转换的数学基础。</p>



<h3 class="wp-block-heading"><strong>第四步</strong></h3>



<p><strong>特殊情况处理 &#8211; 让系统更加智能和健壮</strong></p>



<p>现在到了算法的核心部分，我们必须处理所有可能的边界情况，确保系统的数学稳定性和业务合理性。</p>



<pre class="wp-block-code"><code>-- 第四步：特殊情况处理与等比例转换公式应用
final_calculation AS (
    SELECT 
        gs.student_id, gs.student_name, gs.raw_score, gs.rank_num, gs.grade,
        CASE
            -- 特殊处理1：全省第1名必须获得满分100分
            WHEN gs.rank_num = 1 THEN 100
            -- 特殊处理2：全省最低分必须获得保底30分
            WHEN gs.raw_score = (SELECT MIN(raw_score) FROM student_scores) THEN 30
            -- 特殊处理3：单人等级的健壮性处理
            -- 当等级内只有1人时，max_raw_score = min_raw_score，公式分母为0
            -- 这种情况在20万考生中几乎不可能出现，但为了代码健壮性必须处理
            WHEN gr.max_raw_score = gr.min_raw_score THEN 
                CASE gs.grade
                    WHEN 'A' THEN 100  -- A级唯一学生给满分
                    WHEN 'B' THEN 85   -- B级唯一学生给该级最高分
                    WHEN 'C' THEN 70   -- C级唯一学生给该级最高分
                    WHEN 'D' THEN 55   -- D级唯一学生给该级最高分
                    WHEN 'E' THEN 30   -- E级唯一学生给保底分
                END
            -- 一般情况：应用等比例转换公式
            -- 公式：赋分 = 等级下限 + (个人原始分 - 等级原始分下限) × 
            --              (等级赋分跨度) ÷ (等级原始分跨度)
            ELSE ROUND(
                gr.min_converted_score + 
                (gs.raw_score - gr.min_raw_score) * 
                (gr.max_converted_score - gr.min_converted_score) / 
                (gr.max_raw_score - gr.min_raw_score), 0
            )
        END as converted_score
    FROM graded_students gs, grade_ranges gr
    WHERE gs.grade = gr.grade
)</code></pre>



<p>为什么等级内只有一个人就要特殊处理呢？</p>



<p><strong>如果等级内仅一人，max_raw_score等于min_raw_score，等比例公式的分母就是零，会导致数学错误。虽然这种情况在实际考试中极其罕见，但程序必须处理所有边界条件。我们的处理策略是：ABCD级唯一学生给该级最高分，E级唯一学生给30分，这样确保整个赋分体系始终有最高分100和最低分30。当然，这只是我们的一种设计方案，实际的官方系统可能会有不同的处理逻辑。</strong></p>



<h2 class="wp-block-heading">八、<strong>完整实现</strong></h2>



<p><strong>四步合并一气呵成</strong></p>



<p>形成一个完整、优雅、高效的SQL查询。</p>



<pre class="wp-block-code"><code>-- 福建省高考赋分制完整实现算法
WITH 
-- 第一步：排名计算，确定每个学生的相对位置
ranked_students AS (
    SELECT 
        student_id, student_name, raw_score,
        RANK() OVER (ORDER BY raw_score DESC) as rank_num,
        COUNT(*) OVER () as total_count
    FROM student_scores
),
-- 第二步：等级划分，按福建省15%、35%、35%、13%、2%标准分配
graded_students AS (
    SELECT 
        student_id, student_name, raw_score, rank_num, total_count,
        CASE
            WHEN rank_num &lt;= CEIL(total_count * 0.15) THEN 'A'
            WHEN rank_num &lt;= CEIL(total_count * 0.50) THEN 'B'
            WHEN rank_num &lt;= CEIL(total_count * 0.85) THEN 'C'
            WHEN rank_num &lt;= CEIL(total_count * 0.98) THEN 'D'
            ELSE 'E'
        END as grade
    FROM ranked_students
),
-- 第三步：计算各等级的原始分数范围和对应的赋分区间
grade_ranges AS (
    SELECT 
        grade,
        MAX(raw_score) as max_raw_score,
        MIN(raw_score) as min_raw_score,
        CASE grade 
            WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
            WHEN 'D' THEN 55 ELSE 40 
        END as max_converted_score,
        CASE grade 
            WHEN 'A' THEN 86 WHEN 'B' THEN 71 WHEN 'C' THEN 56 
            WHEN 'D' THEN 41 ELSE 30 
        END as min_converted_score
    FROM graded_students 
    GROUP BY grade
),
-- 第四步：特殊情况处理与等比例转换公式应用
final_calculation AS (
    SELECT 
        gs.student_id, gs.student_name, gs.raw_score, gs.rank_num, gs.grade,
        CASE
            -- 特殊情况1：全省第1名固定100分
            WHEN gs.rank_num = 1 THEN 100
            -- 特殊情况2：全省最低分固定30分
            WHEN gs.raw_score = (SELECT MIN(raw_score) FROM student_scores) THEN 30
            -- 特殊情况3：单人等级处理（健壮性考虑）
            WHEN gr.max_raw_score = gr.min_raw_score THEN 
                CASE gs.grade
                    WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
                    WHEN 'D' THEN 55 WHEN 'E' THEN 30
                END
            -- 一般情况：应用等比例转换公式
            ELSE ROUND(
                gr.min_converted_score + 
                (gs.raw_score - gr.min_raw_score) * 
                (gr.max_converted_score - gr.min_converted_score) / 
                (gr.max_raw_score - gr.min_raw_score), 0
            )
        END as converted_score
    FROM graded_students gs, grade_ranges gr
    WHERE gs.grade = gr.grade
)
-- 显示最终的赋分结果，按排名顺序展示
SELECT 
    rank_num as "排名", 
    student_name as "姓名", 
    raw_score as "原始分", 
    grade as "等级", 
    converted_score as "赋分"
FROM final_calculation 
ORDER BY rank_num;</code></pre>



<h2 class="wp-block-heading">九、<strong>见证奇迹</strong></h2>



<p><strong>完美复现手工计算结果</strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="850" height="892" src="https://www.lemonary.cn/wp-content/uploads/2025/06/image-31.png" alt="" class="wp-image-1970" srcset="https://www.lemonary.cn/wp-content/uploads/2025/06/image-31.png 850w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-31-286x300.png 286w, https://www.lemonary.cn/wp-content/uploads/2025/06/image-31-768x806.png 768w" sizes="auto, (max-width: 850px) 100vw, 850px" /></figure>



<h2 class="wp-block-heading">十、<strong>完美收官</strong></h2>



<p><strong>从手工到智能实现</strong></p>



<h3 class="wp-block-heading">第一步</h3>



<p><strong>批量更新：将计算结果写回数据库</strong></p>



<p>光有查询结果还不够，我们需要把这些宝贵的计算结果保存到数据库中，供后续的高考录取系统使用，代码思路如下。</p>



<pre class="wp-block-code"><code>UPDATE student_scores 
SET (grade, converted_score) = (
    SELECT grade, converted_score 
    FROM (
        -- 这里是完整的赋分计算逻辑
        WITH 
        ranked_students AS (
            SELECT 
                student_id, student_name, raw_score,
                RANK() OVER (ORDER BY raw_score DESC) as rank_num,
                COUNT(*) OVER () as total_count
            FROM student_scores
        ),
        graded_students AS (
            SELECT 
                student_id, student_name, raw_score, rank_num, total_count,
                CASE
                    WHEN rank_num &lt;= CEIL(total_count * 0.15) THEN 'A'
                    WHEN rank_num &lt;= CEIL(total_count * 0.50) THEN 'B'
                    WHEN rank_num &lt;= CEIL(total_count * 0.85) THEN 'C'
                    WHEN rank_num &lt;= CEIL(total_count * 0.98) THEN 'D'
                    ELSE 'E'
                END as grade
            FROM ranked_students
        ),
        grade_ranges AS (
            SELECT 
                grade,
                MAX(raw_score) as max_raw_score,
                MIN(raw_score) as min_raw_score,
                CASE grade 
                    WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
                    WHEN 'D' THEN 55 ELSE 40 
                END as max_converted_score,
                CASE grade 
                    WHEN 'A' THEN 86 WHEN 'B' THEN 71 WHEN 'C' THEN 56 
                    WHEN 'D' THEN 41 ELSE 30 
                END as min_converted_score
            FROM graded_students 
            GROUP BY grade
        ),
        final_calculation AS (
            SELECT 
                gs.student_id, gs.grade,
                CASE
                    WHEN gs.rank_num = 1 THEN 100
                    WHEN gs.raw_score = (SELECT MIN(raw_score) FROM student_scores) THEN 30
                    WHEN gr.max_raw_score = gr.min_raw_score THEN 
                        CASE gs.grade
                            WHEN 'A' THEN 100 WHEN 'B' THEN 85 WHEN 'C' THEN 70 
                            WHEN 'D' THEN 55 WHEN 'E' THEN 30
                        END
                    ELSE ROUND(
                        gr.min_converted_score + 
                        (gs.raw_score - gr.min_raw_score) * 
                        (gr.max_converted_score - gr.min_converted_score) / 
                        (gr.max_raw_score - gr.min_raw_score), 0
                    )
                END as converted_score
            FROM graded_students gs, grade_ranges gr
            WHERE gs.grade = gr.grade
        )
        SELECT grade, converted_score FROM final_calculation 
        WHERE student_id = student_scores.student_id
    )
);
commit;</code></pre>



<h3 class="wp-block-heading">第二步</h3>



<p><strong>数据质量检查：确保系统的可靠性</strong></p>



<p>任何一个生产系统都必须有完善的质量检查机制，特别是像高考赋分这样关系到学生前途的重要计算。</p>



<pre class="wp-block-code"><code>-- 全面的数据质量检查
SELECT '数据完整性检查' as 检查类型, '总人数' as 检查项目, COUNT(*) as 检查结果 
FROM student_scores
UNION ALL
SELECT '数据完整性检查', '已赋分人数', COUNT(*) 
FROM student_scores WHERE converted_score IS NOT NULL
UNION ALL
SELECT '等级分布检查', grade || '级人数', COUNT(*) 
FROM student_scores WHERE grade IS NOT NULL GROUP BY grade
UNION ALL
SELECT '分数范围检查', '赋分最高分', MAX(converted_score) 
FROM student_scores WHERE converted_score IS NOT NULL
UNION ALL
SELECT '分数范围检查', '赋分最低分', MIN(converted_score) 
FROM student_scores WHERE converted_score IS NOT NULL
UNION ALL
SELECT '逻辑性检查', '原始分与赋分差异数', COUNT(*)
FROM student_scores 
WHERE converted_score IS NOT NULL AND converted_score &lt; raw_score
ORDER BY 检查类型, 检查项目;</code></pre>



<p>这个检查查询能帮我们发现各种潜在问题，比如是否所有学生都完成了赋分，等级分布是否合理，赋分是否在30-100的正常范围内，以及赋分后是否真的比原始分更高等等。</p>



<h3 class="wp-block-heading">第三步</h3>



<p><strong>性能优化考虑：面向大规模数据</strong></p>



<p>如果考生数据量达数百万甚至更多，这套算法性能如何？</p>



<p>优化手段：</p>



<ol class="wp-block-list">
<li>为raw_score字段建立索引，加速排序；</li>



<li>使用并行查询提高大数据量的处理速度；</li>



<li>分批处理，比如按学校或地区分批计算等等。</li>
</ol>



<h2 class="wp-block-heading">总结</h2>



<p>原来复杂的赋分制可以用一条这么优雅简洁的SQL实现了，代码改变世界。</p>



<p>这个案例展示了如何将教育政策精确转化为数据库算法。从排名到赋分，每一步都体现了技术服务教育公平的理念。技术让复杂的教育评价变得高效精准，更重要的是，它让公平变得可验证、可重现、易维护。每一行代码背后，都承载着对教育公平的坚持。</p>



<p><strong>需要特别说明的是，本文展示的仅仅是一个极简示例。</strong>真实的高考赋分系统要<strong>复杂得多</strong>！是绝不可能仅用一条SQL就能搞定的，需要考虑太多场景细节了。比如当大量学生出现同分时，可能会打破等级比例：按15%划分A级理论上应该有3000人，但如果第2999名到第3500名都是同分，系统需要决定是严格按比例切分还是保持同分同级。不同省份对此可能有不同的处理策略。</p>



<p>此外还有数据校验、异常处理、并发控制、审计日志等工程问题，以及如何处理缺考、作弊、成绩复议等特殊情况。</p>



<p>但无论系统多复杂，核心思想不变：<strong>通过等级划分和等比例转换，将原始分数映射到标准分数区间，实现不同科目分数的可比性。</strong>这正是新高考制度的精髓所在。</p>



<p><strong>祝莘莘高考学子们：</strong></p>



<p><strong>&nbsp; &nbsp; &nbsp; 心想事成，金榜题名！！！</strong></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e4%b8%80%e6%9d%a1sql%e5%ae%9e%e7%8e%b0%e9%ab%98%e8%80%83%e8%b5%8b%e5%88%86%e5%88%b6/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>达梦数据库统计信息的相关SQL</title>
		<link>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e7%bb%9f%e8%ae%a1%e4%bf%a1%e6%81%af%e7%9a%84%e7%9b%b8%e5%85%b3sql/</link>
					<comments>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e7%bb%9f%e8%ae%a1%e4%bf%a1%e6%81%af%e7%9a%84%e7%9b%b8%e5%85%b3sql/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Mon, 30 Dec 2024 09:02:37 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[DBMS_STATS]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=1219</guid>

					<description><![CDATA[一、前言 对象统计信息描述数据是如何在数据库中存储的。统计信息是优化器的代价计算的依据，可以帮助优化器较精确地 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="一、前言">一、前言</h2>



<p>对象统计信息描述数据是如何在数据库中存储的。统计信息是优化器的代价计算的依据，可以帮助优化器较精确地估算成本，对执行计划的选择起着至关重要的作用。</p>



<p>达梦数据库的统计信息分三种类型：表统计信息、列统计信息、索引统计信息。通过直方图来表示。统计信息生成过程分以下三个步骤：</p>



<ul class="wp-block-list">
<li>确定采样的数据：根据数据对象，确定需要分析哪些数据。
<ul class="wp-block-list">
<li>表：计算表的行数、所占的页数目、平均记录长度</li>



<li>列：统计列数据的分布情况</li>



<li>索引：统计索引列的数据分布情况</li>
</ul>
</li>



<li>确定采样率
<ul class="wp-block-list">
<li>根据数据对象的大小，通过内部算法，确定数据的采样率。采样率与数据量成反比。</li>
</ul>
</li>



<li>生成直方图
<ul class="wp-block-list">
<li>有两种类型的直方图：频率直方图和等高直方图。根据算法分析表的数据分布特征，确定直方图的类型。频率直方图的每个桶(保存统计信息的对象)的高度不同，等高直方图每个桶的高度相同。例如，对列生成统计信息，当列值分布比较均匀时，会采用等高直方图，否则，采用频率直方图。</li>
</ul>
</li>
</ul>



<p>在执行查询时，如果数据对象存在统计信息，代价算法可以根据统计信息中的数据，比较精确地计算出操作所需花费的成本，以此来确定连接方式、对象访问路径、连接顺序，选择最优的执行计划。</p>



<p>用户也可以通过修改 OPTIMIZER_DYNAMIC_SAMPLING 参数值在缺乏统计信息时进行动态统计信息收集。</p>



<h2 class="wp-block-heading" id="二、收集统计信息">二、收集统计信息</h2>



<pre class="wp-block-code"><code>--收集全库统计信息
CALL SP_DB_STAT_INIT ();

--收集指定用户下所有表所有列的统计信息：
DBMS_STATS.GATHER_SCHEMA_STATS('OWNNAME',100,TRUE,'FOR ALL COLUMNS SIZE AUTO');

--收集指定用户下所有索引的统计信息：
DBMS_STATS.GATHER_SCHEMA_STATS('OWNNAME', 1.0, TRUE, 'FOR ALL INDEXED SIZE AUTO');
--或 收集单个索引统计信息：
DBMS_STATS.GATHER_INDEX_STATS('OWNNAME','INDNAME');

--收集指定用户下某表统计信息：
DBMS_STATS.GATHER_TABLE_STATS('OWNNAME','TABNAME',NULL,100,TRUE,'FOR ALL COLUMNS SIZE AUTO');

--收集某表某列的统计信息：
STAT 100 ON TABNAME(COLNAME);</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>注意：<br>以上DBMS_STATS包收集方式均可在最后添加参数<code>DEGREE</code>：收集的并行度，默认为 1。以提高收集效率。DBMS_STATS包方法的具体使用手册可从数据库安装路径doc目录下《DM8系统包使用手册.pdf》查看。</p>
</blockquote>



<p>另外</p>



<pre class="wp-block-code"><code>--收集表统计信息
call SP_TAB_STAT_INIT('OWNNAME','TABNAME');
--收集索引统计信息
call SP_INDEX_STAT_INIT('OWNNAME','INDNAME');
--生成表上所有索引的统计信息
call SP_TAB_INDEX_STAT_INIT ('OWNNAME', 'TABNAME');</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>注意<br>GATHER_TABLE_STATS 是用于收集和更新统计信息的操作，帮助查询优化器更好地优化查询执行计划。它支持并行度、采样、索引统计信息等选项，适用于数据量大的表和定期更新统计信息的场景。<br>SP_TAB_STAT_INIT、SP_INDEX_STAT_INIT 是用于初始化或重置表的统计信息的存储过程，通常在表结构变化或数据量较少时使用。它会将统计信息重置为默认值，适用于需要重新收集统计信息的特殊场景。</p>
</blockquote>



<p><strong>生产环境收集统计信息</strong></p>



<p>有时我们需要在生产环境收集统计信息，即便在业务低谷时报错：-7120达梦数据库报错回滚记录版本太旧，无法获取用户记录。可以在执行前修改如下参数：</p>



<pre class="wp-block-code"><code>ALTER SESSION SET 'ENABLE_IGNORE_PURGE_REC' = 1;</code></pre>



<p>然后再执行收集统计信息的语句。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>备注<br><code>ENABLE_IGNORE_PURGE_REC</code>，动态会话级参数，当返回 EC_RN_NREC_PURGED（-7120）错误（回滚记录版本太旧，无法获取用户记录）时的处理策略；<br>0：报错；1：忽略这一条记录，继续执行；2：报错并生成日志；3：忽略这一条记录，继续执行，并生成日志</p>
</blockquote>



<h2 class="wp-block-heading" id="三、查看统计信息">三、查看统计信息</h2>



<p>（1）查看单个表统计信息</p>



<pre class="wp-block-code"><code>DBMS_STATS.TABLE_STATS_SHOW('OWNNAME','TABNAME');</code></pre>



<p>（2）批量查看某用户下表的统计信息</p>



<pre class="wp-block-code"><code>SELECT 'DBMS_STATS.TABLE_STATS_SHOW('''||OWNER||''','''||TABLE_NAME||''');' FROM DBA_TABLES WHERE OWNER = 'OWNNAME';</code></pre>



<p>使用上述SQL执行后的结果集的SQL进行批量查询。</p>



<h2 class="wp-block-heading">四、其他SQL</h2>



<p>（1）查询表的最近一次收集统计信息的时间</p>



<pre class="wp-block-code"><code>SELECT
    *
FROM
    (SELECT
         O.OBJECT_TYPE AS "对象类型",
         O.OWNER AS "用户",
         O.OBJECT_NAME AS "对象名称",
         C.COLUMN_NAME AS "列名称",
         C.COLUMN_ID AS "列ID",
         C.DATA_TYPE AS "数据类型",
         S.T_TOTAL AS "总行数",
         S.N_SMAPLE AS "采样个数",
         S.N_DISTINCT AS "不同值的个数",
         S.N_NULL AS "空值个数",
         S.V_MIN AS "列的最小值",
         S.V_MAX AS "列的最大值",
         S.N_BUCKETS AS "直方图桶数目",
         S.LAST_GATHERED AS "最后收集时间"
     FROM
         SYSSTATS S
         INNER JOIN DBA_OBJECTS O ON S.ID = O.OBJECT_ID
         INNER JOIN DBA_TAB_COLUMNS C ON O.OWNER = C.OWNER AND O.OBJECT_NAME = C.TABLE_NAME AND S.COLID + 1 = C.COLUMN_ID
     WHERE
         O.OWNER = 'OWNNAME'
         AND O.OBJECT_NAME = 'TABNAME'
         AND O.OBJECT_TYPE = 'TABLE')
ORDER BY
    C.COLUMN_ID;</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>备注：收集全库统计信息时不会更新这个时间，除全库收集外其余收集方式均可更新这个时间。SYSSTATS的COLID字段为-1时包含全库收集统计信息时间。</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e7%bb%9f%e8%ae%a1%e4%bf%a1%e6%81%af%e7%9a%84%e7%9b%b8%e5%85%b3sql/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>达梦数据库中的一些巧妙的SQL</title>
		<link>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%ad%e7%9a%84%e4%b8%80%e4%ba%9b%e5%b7%a7%e5%a6%99%e7%9a%84sql/</link>
					<comments>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%ad%e7%9a%84%e4%b8%80%e4%ba%9b%e5%b7%a7%e5%a6%99%e7%9a%84sql/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Fri, 20 Dec 2024 07:16:12 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=1147</guid>

					<description><![CDATA[一、前言 本文可能不局限于DM数据库，用法是否可用需自行验证。 二、汇总 2.1 字段中是否包含中文 2.2  [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="一、前言">一、前言</h2>



<p>本文可能不局限于DM数据库，用法是否可用需自行验证。</p>



<h2 class="wp-block-heading" id="二、汇总">二、汇总</h2>



<h3 class="wp-block-heading" id="2.1-字段中是否包含中文">2.1 字段中是否包含中文</h3>



<pre class="wp-block-code"><code>--查询NAME字段包含中文的
SELECT NAME FROM CS_ZW WHERE ASCIISTR(NAME) LIKE '%\%';
--查询NAME字段不包含中文的
SELECT NAME FROM CS_ZW WHERE ASCIISTR(NAME) NOT LIKE '%\%';</code></pre>



<h3 class="wp-block-heading" id="2.2-TIMESTAMP类型取小时部分">2.2 TIMESTAMP类型取小时部分</h3>



<pre class="wp-block-code"><code>SELECT DATE_FORMAT(SYSDATE(),'%H') AS HOUR FROM DUAL;</code></pre>



<h3 class="wp-block-heading" id="2.3-查询表的创建时间">2.3 查询表的创建时间</h3>



<pre class="wp-block-code"><code>SELECT NAME,CRTDATE AS "创建时间" FROM SYSOBJECTS WHERE "TYPE$" = 'SCHOBJ' AND "SUBTYPE$" = 'UTAB' AND NAME = '表名';</code></pre>



<h3 class="wp-block-heading" id="2.4-大字段类型查询时直接显示内容">2.4 大字段类型查询时直接显示内容</h3>



<p>创建测试表及数据</p>



<pre class="wp-block-code"><code>CREATE TABLE DZD (ID INT,B1 BLOB,C1 CLOB);

INSERT INTO TEST.DZD VALUES (1,'ABCDEF','MAKDJIDIJFIJDIJIENG');
INSERT INTO TEST.DZD VALUES (2,'BCDEF','NGJDJIEJIJFIJEWNAAS');
INSERT INTO TEST.DZD VALUES (3,'CDEF','JMEIGNABEIJGIIANAI');</code></pre>



<p>方法一：【BLOB类型不适用】</p>



<pre class="wp-block-code"><code>SELECT ID,C1::VARCHAR FROM TEST.DZD;</code></pre>



<p>方法二：</p>



<pre class="wp-block-code"><code>--DBMS_LOB.SUBSTR(BLOB/CLOB/TEXT)
SELECT ID,DBMS_LOB.SUBSTR(B1),DBMS_LOB.SUBSTR(C1) FROM TEST.DZD;</code></pre>



<p>方法三：</p>



<p>DM管理工具配置连接属性，高级中添加参数clobAsString=true</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="674" height="254" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-107.png" alt="" class="wp-image-2476" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-107.png 674w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-107-300x113.png 300w" sizes="auto, (max-width: 674px) 100vw, 674px" /></figure>



<p>方法四：</p>



<p>使用新工具<a href="https://www.sqlark.com/">SQLark（百灵连接）</a></p>



<h3 class="wp-block-heading" id="2.5-时间戳与日期类型的相互转化">2.5 时间戳与日期类型的相互转化</h3>



<p>日期类型转时间戳</p>



<pre class="wp-block-code"><code>SELECT UNIX_TIMESTAMP(SYSDATE);</code></pre>



<p>时间戳转日期类型</p>



<pre class="wp-block-code"><code>SELECT FROM_UNIXTIME(1591644470);
SELECT FROM_UNIXTIME(1591644470,'YYYY-MM-DD');
SELECT FROM_UNIXTIME(1591644470,'YYYY-MM-DD HH24:MI:SS');</code></pre>



<p><strong>另-13位时间戳转化方法</strong></p>



<pre class="wp-block-code"><code>--截取后直接转化
SELECT FROM_UNIXTIME(SUBSTR(1709187648234,0,10)) FROM DUAL;
--TO_CHAR方式
SELECT TO_CHAR(1709187648234*1.0 / (1000*60*60*24) + TO_DATE('1970-01-01 08:00:00','YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS');
SELECT TO_CHAR(1709187648234*1.0 / (1000*60*60*24) + TO_DATE('1970-01-01 08:00:00.000','YYYY-MM-DD HH24:MI:SS.MS'),'YYYY-MM-DD HH24:MI:SS.MS');</code></pre>



<h3 class="wp-block-heading" id="2.6-求一个或多个数中的最大值">2.6 求一个或多个数中的最大值</h3>



<p>创建测试表及数据</p>



<pre class="wp-block-code"><code>CREATE TABLE SCORE(ID INT PRIMARY KEY,NAME VARCHAR(30),YW INT,SX INT,YY INT);</code></pre>



<p>使用SQLark的数据生成功能，生成成绩单数据。</p>



<pre class="wp-block-code"><code>--查询每名学生自己最好的一科成绩
SELECT GREATEST(YW,SX,YY),* FROM SCORE;</code></pre>



<p>如图</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="969" height="857" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-89.png" alt="" class="wp-image-1148" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-89.png 969w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-89-300x265.png 300w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-89-768x679.png 768w" sizes="auto, (max-width: 969px) 100vw, 969px" /></figure>



<h3 class="wp-block-heading" id="2.7-计算一个字符串中某个字符的个数">2.7 计算一个字符串中某个字符的个数</h3>



<p>示例：计算该字符串中“F”的个数。</p>



<pre class="wp-block-code"><code>SELECT LENGTH('ADSLFKJAKSJFDLJADSASF')-LENGTH(REPLACE('ADSLFKJAKSJFDLJADSASF', 'F' ,''));</code></pre>



<p>思路：</p>



<p>思路：</p>



<ol class="wp-block-list">
<li>将字符串中所找字符替换成&#8221;【就是将所找字符替换成空】</li>



<li>利用数学：字符串长度-字符串去掉所找字符后的长度=字符的个数</li>
</ol>



<p>总结：</p>



<pre class="wp-block-code"><code>SELECT LENGTH('字符串')-LENGTH(REPLACE('字符串', '所找字符' ,''));</code></pre>



<h3 class="wp-block-heading" id="2.8-列转置（列转行）">2.8 列转置（列转行）</h3>



<p>创建测试表及数据</p>



<pre class="wp-block-code"><code>--创建表
CREATE TABLE LZH (ID INT, DT DATETIME (6), TEXT VARCHAR(8188));
--插入测试数据
INSERT INTO LZH (ID, DT, TEXT) VALUES (1, '2024-01-24 14:26:26.000000', 'a,B,c,d');
INSERT INTO LZH (ID, DT, TEXT) VALUES (5, '2024-01-24 14:26:26.000000', 'e,f,G');
INSERT INTO LZH (ID, DT, TEXT) VALUES (7, '2024-01-24 14:26:26.000000', 'h,i,J');</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="590" height="138" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-90.png" alt="" class="wp-image-1149" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-90.png 590w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-90-300x70.png 300w" sizes="auto, (max-width: 590px) 100vw, 590px" /></figure>



<p>将TEXT字段以逗号分隔，拆分成新的一行数据</p>



<pre class="wp-block-code"><code>SELECT
	A.*,
	REGEXP_SUBSTR(A.TEXT, '&#91;^,]{1,}', 1, B.L) AS SPLIT_FIELD
FROM
	LZH A
	JOIN (
		SELECT
			LEVEL AS L
		FROM
			DUAL
		CONNECT BY LEVEL &lt;= 100
	) B ON REGEXP_COUNT (A.TEXT, ',') + 1 &gt;= B.L
ORDER BY
	3;</code></pre>



<p>效果：</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="808" height="366" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-91.png" alt="" class="wp-image-1150" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-91.png 808w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-91-300x136.png 300w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-91-768x348.png 768w" sizes="auto, (max-width: 808px) 100vw, 808px" /></figure>



<h3 class="wp-block-heading" id="2.9-虚拟列">2.9 虚拟列</h3>



<p>表中某字段由其他字段计算所得-计算列</p>



<pre class="wp-block-code"><code>CREATE TABLE SCORE_TEST (
	YW CHAR(10),
	SX CHAR(10),
	YY CHAR(10),
	ZF CHAR(10) AS (YW + SX + YY)
);

INSERT INTO SCORE_TEST(YW,SX,YY) VALUES ('92','73','84');

SELECT * FROM SCORE_TEST;</code></pre>



<p>如图：</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="538" height="71" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-92.png" alt="" class="wp-image-1151" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-92.png 538w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-92-300x40.png 300w" sizes="auto, (max-width: 538px) 100vw, 538px" /></figure>



<h3 class="wp-block-heading" id="2.10-二进制转化为相应的ASCII码文本">2.10 二进制转化为相应的ASCII码文本</h3>



<pre class="wp-block-code"><code>SELECT UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(BLOB字段)) FROM TABLENAME;</code></pre>



<h3 class="wp-block-heading">2.11 分区表将手机号以最后一位数字分十个子表</h3>



<p>思路：使用虚拟列进行范围分区</p>



<pre class="wp-block-code"><code>CREATE TABLE PHONEINFO (NAME VARCHAR(20),PHONE VARCHAR(20),PHONE_TAIL_NUMBER CHAR(1) AS (SUBSTR(PHONE,-1)))
PARTITION BY LIST(PHONE_TAIL_NUMBER) (
	PARTITION P0 VALUES ('0'),
	PARTITION P1 VALUES ('1'),
	PARTITION P2 VALUES ('2'),
	PARTITION P3 VALUES ('3'),
	PARTITION P4 VALUES ('4'),
	PARTITION P5 VALUES ('5'),
	PARTITION P6 VALUES ('6'),
	PARTITION P7 VALUES ('7'),
	PARTITION P8 VALUES ('8'),
	PARTITION P9 VALUES ('9')
);</code></pre>



<h3 class="wp-block-heading">2.12 CONCAT函数拼接时间字段</h3>



<p>CONCAT函数拼接传参的字段时，时间字段不会带上单引号，可以在拼接字段时带上引号来解决</p>



<pre class="wp-block-code"><code>SELECT CONCAT('BETWEEN ',SYSDATE)--无单引号
UNION ALL
SELECT CONCAT('BETWEEN ',''''||SYSDATE||'''');--加单引号</code></pre>



<h3 class="wp-block-heading">2.13 自定义函数实现字段拼接【CURSOR】</h3>



<p>创建测试数据</p>



<pre class="wp-block-code"><code>CREATE TABLE ZIDUANPINJIE (ID INT NOT NULL PRIMARY KEY, NAME VARCHAR(30));

INSERT INTO ZIDUANPINJIE VALUES (1,'武汉');
INSERT INTO ZIDUANPINJIE VALUES (2,'达梦');
INSERT INTO ZIDUANPINJIE VALUES (3,'数据库');
INSERT INTO ZIDUANPINJIE VALUES (4,'股份');
INSERT INTO ZIDUANPINJIE VALUES (5,'有限');
INSERT INTO ZIDUANPINJIE VALUES (6,'公司');
INSERT INTO ZIDUANPINJIE VALUES (7,'成功');
INSERT INTO ZIDUANPINJIE VALUES (8,'上市');
INSERT INTO ZIDUANPINJIE VALUES (9,'科创板');

COMMIT;</code></pre>



<p>函数定义</p>



<pre class="wp-block-code"><code>CREATE OR REPLACE FUNCTION PINJIE_ALL_DATA_FROM_TABLE
RETURN VARCHAR IS
	V_RESULT VARCHAR(100);
	CURSOR TABLE_CURSOR IS
		SELECT * FROM ZIDUANPINJIE;
BEGIN
	FOR REC IN TABLE_CURSOR LOOP
		V_RESULT := V_RESULT || REC.NAME;
		--V_RESULT := V_RESULT ||'||' || REC.NAME; -- 添加分隔符
	END LOOP;
	
	RETURN V_RESULT;
END;
/</code></pre>



<p>验证效果</p>



<pre class="wp-block-code"><code>SELECT "TEST"."PINJIE_ALL_DATA_FROM_TABLE"();</code></pre>



<p>效果如图</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="529" height="74" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-97.png" alt="" class="wp-image-1269" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-97.png 529w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-97-300x42.png 300w" sizes="auto, (max-width: 529px) 100vw, 529px" /></figure>



<h3 class="wp-block-heading">2.14 IMAGA字段转换为VARCHAR字段</h3>



<p>创建测试表</p>



<pre class="wp-block-code"><code>CREATE TABLE IMG2VAR (ID INT PRIMARY KEY, PICTURE IMAGE);</code></pre>



<p>使用SQLark的数据生成功能生成测试数据</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="595" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-98-1024x595.png" alt="" class="wp-image-1273" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-98-1024x595.png 1024w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-98-300x174.png 300w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-98-768x446.png 768w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-98-1536x893.png 1536w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-98.png 1800w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>转换</p>



<pre class="wp-block-code"><code>SELECT UTL_I18N.RAW_TO_CHAR(SUBSTRBLB(PICTURE,1,3000),'UTF8') FROM TEST.IMG2VAR;</code></pre>



<p>效果图</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="763" height="370" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-99.png" alt="" class="wp-image-1274" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-99.png 763w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-99-300x145.png 300w" sizes="auto, (max-width: 763px) 100vw, 763px" /></figure>



<h3 class="wp-block-heading">2.15 LIKE转义ESCAPE</h3>



<p>创建测试数据</p>



<pre class="wp-block-code"><code>CREATE TABLE TEST_LIKE (ID INT, NAME VARCHAR(200));

INSERT INTO TEST_LIKE VALUES(1,'FFFF');
INSERT INTO TEST_LIKE VALUES(2,'FF_FF');
INSERT INTO TEST_LIKE VALUES(3,'FF\FF');
INSERT INTO TEST_LIKE VALUES(4,'FF_A\A');
INSERT INTO TEST_LIKE VALUES(5,'FF_\FF');
INSERT INTO TEST_LIKE VALUES(6,'FF\_FF');
INSERT INTO TEST_LIKE VALUES(7,'FF%_%FF');

COMMIT;</code></pre>



<p><strong>一些查询示例：</strong></p>



<p>（1）查询包含“_”的数据</p>



<pre class="wp-block-code"><code>SELECT * FROM TEST_LIKE WHERE NAME LIKE '%\_%' ESCAPE '\';
--或者
SELECT * FROM TEST_LIKE WHERE NAME LIKE '%A_%' ESCAPE 'A';</code></pre>



<p>效果图：</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="339" height="205" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-100.png" alt="" class="wp-image-1277" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-100.png 339w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-100-300x181.png 300w" sizes="auto, (max-width: 339px) 100vw, 339px" /></figure>



<p>（2）查询包含“\_”的数据</p>



<pre class="wp-block-code"><code>SELECT * FROM TEST_LIKE WHERE NAME LIKE '%\\\_%' ESCAPE '\';</code></pre>



<p>效果图：</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="337" height="73" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-101.png" alt="" class="wp-image-1280" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-101.png 337w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-101-300x65.png 300w" sizes="auto, (max-width: 337px) 100vw, 337px" /></figure>



<p>（3）查询包含“%”的数据</p>



<pre class="wp-block-code"><code>SELECT * FROM TEST_LIKE WHERE REGEXP_LIKE (NAME,'%');
--或者
SELECT * FROM TEST_LIKE WHERE NAME LIKE '%\%%' ESCAPE '\';</code></pre>



<p>效果图：</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="339" height="73" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-102.png" alt="" class="wp-image-1281" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-102.png 339w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-102-300x65.png 300w" sizes="auto, (max-width: 339px) 100vw, 339px" /></figure>



<h3 class="wp-block-heading">2.16 字符串按分割符拆分成一列的多行</h3>



<p>示例：将字符串 “AAA|BBB|CCC|DDD|EEE”，按 “|” 截取</p>



<p>方法一：SUBSTRING_INDEX嵌套</p>



<pre class="wp-block-code"><code>SELECT SUBSTRING_INDEX('AAA|BBB|CCC|DDD|EEE','|',1) UNION ALL							--AAA
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('AAA|BBB|CCC|DDD|EEE','|', 2),'|',-1) UNION ALL	--BBB
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('AAA|BBB|CCC|DDD|EEE','|', 3),'|',-1) UNION ALL	--CCC
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('AAA|BBB|CCC|DDD|EEE','|', 4),'|',-1) UNION ALL	--DDD
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('AAA|BBB|CCC|DDD|EEE','|', 5),'|',-1);			--EEE</code></pre>



<p>方法二：REGEXP_SUBSTR结合LEVEL</p>



<pre class="wp-block-code"><code>SELECT
    REGEXP_SUBSTR('AAA|BBB|CCC|DDD|EEE', '&#91;^|]+', 1, LEVEL) AS SPLIT_VALUE
FROM
    DUAL CONNECT BY
    REGEXP_SUBSTR('AAA|BBB|CCC|DDD|EEE', '&#91;^|]+', 1, LEVEL) IS NOT NULL;</code></pre>



<p>方法三：</p>



<pre class="wp-block-code"><code>DECLARE
    V_COUNT INT;
    V_DATA VARCHAR(50);
    V_SQL VARCHAR(5000);
BEGIN
    SELECT LENGTH('AAA|BBB|CCC|DDD|EEE') - LENGTH(REPLACE('AAA|BBB|CCC|DDD|EEE', '|', '')) + 1 INTO V_COUNT FROM DUAL;
    EXECUTE IMMEDIATE 'DROP TABLE IF EXISTS RESULT_TABLE;';
    EXECUTE IMMEDIATE 'CREATE TABLE RESULT_TABLE (RES VARCHAR2(50));';
    FOR I IN 1 .. V_COUNT LOOP
        SET V_DATA = SUBSTRING_INDEX(SUBSTRING_INDEX('AAA|BBB|CCC|DDD|EEE', '|', I), '|', -1);
        V_SQL = 'INSERT INTO RESULT_TABLE VALUES (:1);';
        EXECUTE IMMEDIATE V_SQL USING V_DATA;
    END LOOP;
END;</code></pre>



<p>查询验证</p>



<pre class="wp-block-code"><code>SELECT * FROM RESULT_TABLE;</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="199" height="205" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-103.png" alt="" class="wp-image-1315"/></figure>



<p>总结模板：</p>



<pre class="wp-block-code"><code>DECLARE 
    V_COUNT INT;
    V_DATA VARCHAR(50);
    V_SQL VARCHAR(5000);
    
BEGIN
	SELECT LENGTH('字符串')-LENGTH(REPLACE('字符串', '分割符' ,''))+1 INTO V_COUNT FROM DUAL;
    EXECUTE IMMEDIATE 'DROP TABLE IF EXISTS RESULT_TABLE;';
    EXECUTE IMMEDIATE 'CREATE TABLE RESULT_TABLE (RES VARCHAR2(50));';
	FOR I IN 1..V_COUNT LOOP
    	SET V_DATA = SUBSTRING_INDEX(SUBSTRING_INDEX('字符串','分割符', I),'分割符',-1);
    	V_SQL = 'INSERT INTO RESULT_TABLE VALUES (:1);';
		EXECUTE IMMEDIATE V_SQL USING V_DATA;
    END LOOP;
END;
--查询结果
SELECT * FROM RESULT_TABLE;</code></pre>



<h3 class="wp-block-heading">2.17 时间转化成秒数</h3>



<pre class="wp-block-code"><code>SELECT TIME_TO_SEC('03:00:00') from dual;</code></pre>



<p>效果：</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="360" height="73" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-104.png" alt="" class="wp-image-1319" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-104.png 360w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-104-300x61.png 300w" sizes="auto, (max-width: 360px) 100vw, 360px" /></figure>



<h3 class="wp-block-heading">2.18 TRIM不掉的空格</h3>



<p>有时“空格”不一定是“空格”，而是“制表符”，而<code>TRIM</code>函数默认修剪字符是空格，所以需要简单补充下SQL</p>



<pre class="wp-block-code"><code>--去掉前后的制表符
SELECT TRIM(BOTH '	' FROM 字段名);
--去掉首端的制表符
SELECT TRIM(LEADING '	' FROM 字段名);
--去掉末端的制表符
SELECT TRIM(TRAILING '	' FROM 字段名);</code></pre>



<h2 class="wp-block-heading" id="三、后续">三、后续</h2>



<p>该文档不定时更新，如有发现新的SQL，随时同步到该文章。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%ad%e7%9a%84%e4%b8%80%e4%ba%9b%e5%b7%a7%e5%a6%99%e7%9a%84sql/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>达梦数据库与会话相关的SQL和命令</title>
		<link>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%8e%e4%bc%9a%e8%af%9d%e7%9b%b8%e5%85%b3%e7%9a%84sql%e5%92%8c%e5%91%bd%e4%bb%a4/</link>
					<comments>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%8e%e4%bc%9a%e8%af%9d%e7%9b%b8%e5%85%b3%e7%9a%84sql%e5%92%8c%e5%91%bd%e4%bb%a4/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Wed, 18 Dec 2024 03:23:48 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=1109</guid>

					<description><![CDATA[一、简单概述 通俗来讲，会话(Session) 是通信双方从开始通信到通信结束期间的一个上下文（Context [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">一、简单概述</h2>



<p>通俗来讲，会话(Session) 是通信双方从开始通信到通信结束期间的一个上下文（Context）。这个上下文是一段位于服务器端的内存：记录了本次连接的客户端机器、通过哪个应用程序、哪个用户登录等信息。</p>



<p>连接（Connection）：连接是从客户端到ORACLE实例的一条物理路径。连接可以在网络上建立，或者在本机通过IPC机制建立。通常会在客户端进程与一个专用服务器或一个调度器之间建立连接。</p>



<p>会话(Session) 是和连接(Connection)是同时建立的，两者是对同一件事情不同层次的描述。简单讲，连接(Connection)是物理上的客户端同服务器的通信链路，会话(Session)是逻辑上的用户同服务器的通信交互。</p>



<h2 class="wp-block-heading">二、常用SQL</h2>



<h3 class="wp-block-heading">2.1 操作系统命令查询会话数</h3>



<pre class="wp-block-code"><code>lsof -i:5236|grep dmserver|wc -l
或者
netstat -nat|awk '{print $4}'|grep 5236|wc -l</code></pre>



<h3 class="wp-block-heading">2.2 SQL查询会话数</h3>



<pre class="wp-block-code"><code>--当前数据库连接的会话总数
SELECT COUNT(*) FROM V$SESSIONS;
--当前数据库活动的会话总数
SELECT COUNT(*) FROM V$SESSIONS WHERE STATE='ACTIVE';
--详细查询
SELECT SQL_TEXT,CLNT_IP,STATE,COUNT(*) FROM V$SESSIONS GROUP BY SQL_TEXT,CLNT_IP,STATE ORDER BY STATE,COUNT(*) DESC;</code></pre>



<h3 class="wp-block-heading">2.3 查看数据库设置的最大会话数</h3>



<pre class="wp-block-code"><code>SELECT PARA_NAME, PARA_VALUE FROM V$DM_INI WHERE PARA_NAME = 'MAX_SESSIONS';
--或者
SELECT NAME, VALUE FROM V$PARAMETER WHERE NAME = 'MAX_SESSIONS';</code></pre>



<h3 class="wp-block-heading">2.4 总览活动会话数、会话总数、最大会话数</h3>



<pre class="wp-block-code"><code>SELECT 'ACTIVE_SESSION:' AS SESS,COUNT(1) AS COUNT FROM V$SESSIONS WHERE STATE='ACTIVE' UNION ALL
SELECT 'CURRENT_SESSION:',COUNT(1) FROM V$SESSIONS UNION ALL
SELECT 'MAX_SESSION:',PARA_VALUE FROM V$DM_INI WHERE PARA_NAME='MAX_SESSIONS';</code></pre>



<h3 class="wp-block-heading">2.5 查看会话主要信息</h3>



<pre class="wp-block-code"><code>SELECT SQL_TEXT, 
       STATE, 
       USER_NAME, 
       CURR_SCH, 
       CLNT_TYPE, 
       CLNT_HOST, 
       APPNAME, 
       CLNT_IP, 
       CLNT_VER 
FROM V$SESSIONS;</code></pre>



<h3 class="wp-block-heading">2.6 查看SQL的来源</h3>



<pre class="wp-block-code"><code>SELECT
    REGEXP_SUBSTR(CLNT_IP, '&#91;0-9]+\.&#91;0-9]+\.&#91;0-9]+\.&#91;0-9]+'),
    COUNT(*),
    STATE,
    CLNT_VER
FROM
    V$SESSIONS
GROUP BY
    REGEXP_SUBSTR(CLNT_IP, '&#91;0-9]+\.&#91;0-9]+\.&#91;0-9]+\.&#91;0-9]+'),
    STATE,
    CLNT_VER
ORDER BY
    2 DESC;</code></pre>



<h3 class="wp-block-heading">2.7 调整MAX_SESSIONS，可使用该SQL评估</h3>



<pre class="wp-block-code"><code>SELECT
    *,
    ROUND(总的 / (SELECT
                    TRUNC(SUM(TOTAL_SIZE * 1.0) / 1024 / 1024)
                FROM
                    V$MEM_POOL), 2) * 100 || '%' 占总的百分比,
    ROUND(总的 / DECODE(水位, 0, 1, 水位), 2) * 100 || '%' 与水位百分比
FROM
    (SELECT
         REGEXP_REPLACE(NAME, '&#91;0-9]') 类别,
         COUNT(*) 池数,
         TRUNC(SUM((ORG_SIZE / 1024.0 / 1024))) 初始,
         TRUNC(SUM((DATA_SIZE / 1024.0 / 1024))) 在用,
         TRUNC(SUM((TOTAL_SIZE / 1024.0 / 1024))) 总的,
         TRUNC(SUM((TARGET_SIZE / 1024.0 / 1024))) 水位
     FROM
         V$MEM_POOL
     GROUP BY
         REGEXP_REPLACE(NAME, '&#91;0-9]')
     UNION ALL
     SELECT
         'MEM_TOTAL',
         1,
         TRUNC(SUM((ORG_SIZE / 1024.0 / 1024))) 初始,
         TRUNC(SUM((DATA_SIZE / 1024.0 / 1024))) 在用,
         TRUNC(SUM(TOTAL_SIZE * 1.0) / 1024 / 1024) 总的,
         TRUNC(SUM((TARGET_SIZE / 1024.0 / 1024))) 水位
     FROM
         V$MEM_POOL) ALL_MEM
WHERE
    总的 &gt; 0
ORDER BY
    总的 DESC;</code></pre>



<p>查看SESSION占总的百分比，评估可以调整的幅度。</p>



<p>实际配置不建议超过推荐值的150%，配置过大的话，业务高峰期间会话数太高，有发生OOM的可能。<br>【推荐值：参数优化脚本根据服务器配置执行后所得的值】</p>



<h3 class="wp-block-heading">2.8 查看阻塞会话</h3>



<pre class="wp-block-code"><code>SELECT TW.*,SS.CLNT_IP,SESS_ID,SS.SQL_TEXT FROM V$TRXWAIT TW JOIN V$SESSIONS SS ON SS.THRD_ID=TW.THRD_ID ORDER BY WAIT_TIME DESC;
--或者
SELECT * FROM V$SESSIONS WHERE TRX_ID IN (SELECT WAIT_FOR_ID FROM V$TRXWAIT);</code></pre>



<h3 class="wp-block-heading">2.9 清除阻塞会话的源头</h3>



<pre class="wp-block-code"><code>--使用结果集的SQL清除
SELECT 'SP_CLOSE_SESSION('||SESS_ID||');' FROM V$SESSIONS WHERE TRX_ID IN (SELECT WAIT_FOR_ID FROM V$TRXWAIT);
SELECT 'SP_CLOSE_SESSION('||SESS_ID||');' FROM V$SESSIONS S, V$LOCK L WHERE S.TRX_ID = L.TRX_ID AND L.BLOCKED = 1;
--或者根据SESS_ID清除
SP_CLOSE_SESSION(SESS_ID);
--如果清除不掉，先取消，再清除
SP_CANCEL_SESSION_OPERATION(SESS_ID);
SP_CLOSE_SESSION(SESS_ID);</code></pre>



<h3 class="wp-block-heading">2.10 清除所有会话</h3>



<pre class="wp-block-code"><code>BEGIN
    FOR REC IN (SELECT * FROM V$SESSIONS WHERE SESS_ID &lt;&gt; SESSID()) LOOP
        SP_CLOSE_SESSION(REC.SESS_ID);
    END LOOP;
END;</code></pre>



<h3 class="wp-block-heading">2.11 清除某张表的缓存计划</h3>



<pre class="wp-block-code"><code>SELECT 'SP_CLEAR_PLAN_CACHE('||CACHE_ITEM||');' FROM V$CACHEPLN WHERE SQLSTR LIKE '%TABLENAME%';
--或者
SELECT * FROM V$CACHEPLN WHERE SQLSTR LIKE '%TABLENAME%';
SP_CLEAR_PLAN_CACHE(CACHE_ITEM);</code></pre>



<h3 class="wp-block-heading">2.12 批量清除缓存</h3>



<pre class="wp-block-code"><code>BEGIN
    FOR RS IN (SELECT * FROM V$CACHEPLN WHERE SQLSTR LIKE '%SQL语句%') LOOP
        EXECUTE IMMEDIATE 'SP_CLEAR_PLAN_CACHE(' || RS.CACHE_ITEM || ');';
    END LOOP;
END;</code></pre>



<h3 class="wp-block-heading">2.13 查询当前锁的状态</h3>



<pre class="wp-block-code"><code>SELECT * FROM V$LOCK;
--或者
SELECT
    LC.LMODE,
    LC.TABLE_ID,
    LC.BLOCKED,
    VTW.ID AS TRX_ID,
    VS.SESS_ID,
    VS.SQL_TEXT,
    VS.APPNAME,
    VS.CLNT_IP
FROM
    V$LOCK LC
    LEFT JOIN V$TRXWAIT VTW ON (LC.TRX_ID = VTW.ID)
    LEFT JOIN V$TRX VT ON (VTW.ID = VT.ID)
    LEFT JOIN V$SESSIONS VS ON (VT.SESS_ID = VS.SESS_ID)
WHERE
    VS.SQL_TEXT IS NOT NULL;</code></pre>



<h3 class="wp-block-heading">2.14 查询历史执行语句记录-有SQL耗时字段</h3>



<pre class="wp-block-code"><code>SELECT
    SQL_HIS.SEQ_NO,
    SQL_HIS.SESS_ID,
    SQL_HIS.TRX_ID,
    SQL_HIS.TOP_SQL_TEXT,
    SQL_HIS.TIME_USED / 1000 TIMEUSED_MS,
    SQL_HIS.START_TIME,
    UNIX_TIMESTAMP(SQL_HIS.START_TIME), --SQL_HIS.COMMAND_TYPE,
    SESS_HIS.USER_NAME,
    SESS_HIS.CLNT_IP,
    SESS_HIS.APPNAME
FROM
    V$SQL_HISTORY SQL_HIS
    LEFT JOIN V$SESSION_HISTORY SESS_HIS ON SQL_HIS.SESS_ID = SESS_HIS.SESS_ID AND SQL_HIS.SESS_SEQ = SESS_HIS.SESS_SEQ
WHERE
    SF_GET_EP_SEQNO(SQL_HIS.ROWID) = SF_GET_SELF_EP_SEQNO()
    AND SQL_ID &gt; 0
    AND IS_OVER = 'Y'
    AND CAST((TO_DATE(START_TIME, 'YYYY-MM-DD HH24:MI:SS') + CAST(TIME_USED / 1000000 AS INTERVAL SECOND)) AS DATETIME(0)) &lt; SYSDATE 
    --AND TOP_SQL_TEXT NOT LIKE ?     --排除某些SQL
    --AND TIME_USED/1000/1000 &gt;= 1         --执行耗时 1S 以上的SQL
    --AND START_TIME BETWEEN '2025-07-18 00:00:00' AND '2025-07-18 23:59:59.999'		--某一天
ORDER BY
    SQL_HIS.TIME_USED DESC;</code></pre>



<h3 class="wp-block-heading">2.15 历史最大连接数</h3>



<pre class="wp-block-code"><code>SELECT
    TO_CHAR(CREATE_TIME, 'YYYY-MM-DD HH24') HH,
    COUNT(1) CNT
FROM
    SYS.V$SESSION_HISTORY
GROUP BY
    TO_CHAR(CREATE_TIME, 'YYYY-MM-DD HH24')
ORDER BY
    1 DESC;</code></pre>



<h3 class="wp-block-heading">2.16 当前慢SQL</h3>



<pre class="wp-block-code"><code>--1--
SELECT
    'SP_CANCEL_SESSION_OPERATION (''' || SESS_ID || ''');' AS                 "取消会话",
    'SP_CLOSE_SESSION(''' || SESS_ID || ''');' AS                             "清除会话",
    DATEDIFF(SS, LAST_RECV_TIME, SYSDATE) AS                                  SQL_USED_TIME,
    DBMS_LOB.SUBSTR(SF_GET_SESSION_SQL(SESS_ID)) AS                           SQL_TXT,
    CURR_SCH,
    USER_NAME,
    SESS_ID,
    REPLACE(SUBSTR(CLNT_IP, 1, INSTR(CLNT_IP, ':', -1) -1), '::FFFF:', '') AS CLNT_IP,
    (SELECT
         INS_CNT || ' ' || DEL_CNT || ' ' || UPD_CNT IDU
     FROM
         V$TRX
     WHERE
         ID = A.TRX_ID
         AND TRX_ID &gt; 0) AS                                                   "INS | DEL | UPD - COUNT",
    PARSE_TIME,
    HARD_PARSE_TIME,
    LOGIC_READ_CNT,
    PHY_READ_CNT,
    IO_WAIT_TIME,
    MAX_MEM_USED
FROM
    V$SESSIONS A
    LEFT JOIN V$SQL_STAT B ON A.SESS_ID = B.SESSID
WHERE
    1 = 1
    AND A.STATE = 'ACTIVE'
ORDER BY
    3 DESC;
--2--
SELECT
    'SP_CANCEL_SESSION_OPERATION (''' || SESS_ID || ''');' AS                                      "取消会话",
    'SP_CLOSE_SESSION(''' || SESS_ID || ''');' AS                                                  "清除会话",
    SQL_USED_TIME,
    SQL_TXT,
    CURR_SCH,
    USER_NAME,
    SESS_ID,
    CLNT_IP,
    "INS | DEL | UPD - COUNT",
    PARSE_TIME,
    HARD_PARSE_TIME,
    LOGIC_READ_CNT,
    PHY_READ_CNT,
    ROUND(PHY_READ_CNT * 1.0 * PAGE / 1024 / 1024 / DECODE(SQL_USED_TIME, 0, 1, SQL_USED_TIME)) AS PHY_READ_MB,
    IO_WAIT_TIME,
    MAX_MEM_USED
FROM
    (SELECT
         DATEDIFF(SS, LAST_RECV_TIME, SYSDATE) AS                                  SQL_USED_TIME,
         DBMS_LOB.SUBSTR(SF_GET_SESSION_SQL(SESS_ID)) AS                           SQL_TXT,
         CURR_SCH,
         USER_NAME,
         SESS_ID,
         REPLACE(SUBSTR(CLNT_IP, 1, INSTR(CLNT_IP, ':', -1) -1), '::FFFF:', '') AS CLNT_IP,
         (SELECT
              INS_CNT || ' ' || DEL_CNT || ' ' || UPD_CNT IDU
          FROM
              V$TRX
          WHERE
              ID = A.TRX_ID
              AND TRX_ID &gt; 0) AS                                                   "INS | DEL | UPD - COUNT",
         PARSE_TIME,
         HARD_PARSE_TIME,
         LOGIC_READ_CNT,
         PHY_READ_CNT,
         IO_WAIT_TIME,
         MAX_MEM_USED
     FROM
         V$SESSIONS A
         LEFT JOIN V$SQL_STAT B ON A.SESS_ID = B.SESSID
     WHERE
         1 = 1
         AND A.STATE = 'ACTIVE')
ORDER BY
    3 DESC;</code></pre>



<h3 class="wp-block-heading">2.17 IO等待、逻辑读、物理读</h3>



<pre class="wp-block-code"><code>SELECT
	ST.IO_WAIT_TIME,
	ST.LOGIC_READ_CNT,
	ST.PHY_READ_CNT,
	DATEDIFF (MS, S.LAST_RECV_TIME, CURRENT_TIMESTAMP) AS TIME_USED,
	DBMS_LOB.SUBSTR (SF_GET_SESSION_SQL (S.SESS_ID)) AS FULLSQL,
	S.STATE,
	ST.*
FROM
	V$SQL_STAT ST
	INNER JOIN V$SESSIONS S ON ST.SESSID = S.SESS_ID
WHERE
	S.STATE = 'ACTIVE'
ORDER BY
	LOGIC_READ_CNT DESC,
	IO_WAIT_TIME DESC;</code></pre>



<h3 class="wp-block-heading">2.18 清除运行过久的SQL</h3>



<pre class="wp-block-code"><code>BEGIN
    FOR REC IN (SELECT
                    SESS_ID
                FROM
                    (SELECT DATEDIFF(SS, LAST_RECV_TIME, SYSDATE) SS, SF_GET_SESSION_SQL(SESS_ID), * FROM V$SESSIONS WHERE STATE = 'ACTIVE')
                WHERE
                    SS &gt; 30) LOOP
        SP_CLOSE_SESSION(REC.SESS_ID);
    END LOOP;
END;</code></pre>



<h3 class="wp-block-heading">2.19 空闲会话清理</h3>



<pre class="wp-block-code"><code>SELECT 
    SESS_ID,
    USER_NAME,
    CLNT_IP,
    STATE,
    TRX_ID,
    DATEDIFF(SS, LAST_RECV_TIME, SYSDATE) AS IDLE_SECONDS,
    DATEDIFF(SS, LAST_RECV_TIME, SYSDATE) / 86400 AS IDLE_DAYS,
    CREATE_TIME,
    LAST_RECV_TIME,
    SQL_TEXT,
    CASE 
        WHEN TRX_ID = 0 THEN '低风险'
        ELSE '中风险（有事务但无DML）'
    END AS RISK_LEVEL,
    'SP_CLOSE_SESSION(' || SESS_ID || ');' AS KILL_SESS_SQL
FROM 
    V$SESSIONS S
WHERE 
    S.STATE = 'IDLE'
    AND NOT EXISTS (
        SELECT 1 
        FROM V$TRX T
        WHERE T.ID = S.TRX_ID
          AND T.STATUS = 'ACTIVE'
          AND (T.INS_CNT &gt; 0 OR T.UPD_CNT &gt; 0 OR T.DEL_CNT &gt; 0 OR T.UPD_INS_CNT &gt; 0)
    )
ORDER BY 
    IDLE_SECONDS DESC;</code></pre>



<h3 class="wp-block-heading">2.20 自定义查询会话的存储过程</h3>



<pre class="wp-block-code"><code>--分别通过SESS_ID、THRD_ID、TRX_ID定位会话
CREATE OR REPLACE PROCEDURE SYSDBA.GSBD(
    P_TYPE IN VARCHAR2, -- 查询类型：'SESS', 'THRD', 'TRX'
    P_ID   IN BIGINT    -- 对应的 ID 值
)
AS
BEGIN
    -- 根据类型选择不同的查询
    IF P_TYPE = 'SESS' THEN
        -- 按 SESS_ID 查询
        SELECT 'SP_CANCEL_SESSION_OPERATION (' || SESS_ID || ')' AS CANCEL_CMD,
               'SP_CLOSE_SESSION(' || SESS_ID || ')'            AS CLOSE_CMD,
               THRD_ID,
               SESS_ID,
               TRX_ID,
               DATEDIFF(SS, LAST_RECV_TIME, SYSDATE)           AS "EXECTIME(S)",
               STATE,
               TO_CHAR(SF_GET_SESSION_SQL(SESS_ID))            AS "SQL",
               (SELECT INS_CNT || ' ' || DEL_CNT || ' ' || UPD_CNT
                FROM V$TRX
                WHERE ID = TRX_ID
                  AND TRX_ID &gt; 0)                              AS "更新的数据IDU",
               CURR_SCH,
               USER_NAME,
               CLNT_HOST,
               CLNT_IP,
               CLNT_TYPE,
               OSNAME,
               LEFT(LAST_SEND_TIME, 19)
        FROM V$SESSIONS
        WHERE SESS_ID = P_ID
          AND SESS_ID &lt;&gt; SESSID;   -- 排除当前会话

    ELSIF P_TYPE = 'THRD' THEN
        -- 按 THRD_ID 查询
        SELECT 'SP_CANCEL_SESSION_OPERATION (' || SESS_ID || ')' AS CANCEL_CMD,
               'SP_CLOSE_SESSION(' || SESS_ID || ')'            AS CLOSE_CMD,
               THRD_ID,
               SESS_ID,
               TRX_ID,
               DATEDIFF(SS, LAST_RECV_TIME, SYSDATE)           AS "EXECTIME(S)",
               STATE,
               TO_CHAR(SF_GET_SESSION_SQL(SESS_ID))            AS "SQL",
               (SELECT INS_CNT || ' ' || DEL_CNT || ' ' || UPD_CNT
                FROM V$TRX
                WHERE ID = TRX_ID
                  AND TRX_ID &gt; 0)                              AS "更新的数据IDU",
               CURR_SCH,
               USER_NAME,
               CLNT_HOST,
               CLNT_IP,
               CLNT_TYPE,
               OSNAME,
               LEFT(LAST_SEND_TIME, 19)
        FROM V$SESSIONS
        WHERE THRD_ID = P_ID
          AND SESS_ID &lt;&gt; SESSID;

    ELSIF P_TYPE = 'TRX' THEN
        -- 按 TRX_ID 查询
        SELECT 'SP_CANCEL_SESSION_OPERATION (' || SESS_ID || ')' AS CANCEL_CMD,
               'SP_CLOSE_SESSION(' || SESS_ID || ')'            AS CLOSE_CMD,
               THRD_ID,
               SESS_ID,
               TRX_ID,
               DATEDIFF(SS, LAST_RECV_TIME, SYSDATE)           AS "EXECTIME(S)",
               STATE,
               TO_CHAR(SF_GET_SESSION_SQL(SESS_ID))            AS "SQL",
               (SELECT INS_CNT || ' ' || DEL_CNT || ' ' || UPD_CNT
                FROM V$TRX
                WHERE ID = TRX_ID
                  AND TRX_ID &gt; 0)                              AS "更新的数据IDU",
               CURR_SCH,
               USER_NAME,
               CLNT_HOST,
               CLNT_IP,
               CLNT_TYPE,
               OSNAME,
               LEFT(LAST_SEND_TIME, 19)
        FROM V$SESSIONS
        WHERE TRX_ID = P_ID
          AND SESS_ID &lt;&gt; SESSID;

    ELSE
        -- 非法类型提示
        RAISE_APPLICATION_ERROR(-20001, '参数 P_TYPE 必须是 SESS, THRD 或 TRX');
    END IF;
END;

--调用方法
CALL SYSDBA.GSBD('SESS', 12345);
CALL SYSDBA.GSBD('THRD', 67890);
CALL SYSDBA.GSBD('TRX', 987654321);</code></pre>



<h3 class="wp-block-heading">2.21 查找全表扫描的高频SQL</h3>



<pre class="wp-block-code"><code>WITH EXEC_STATS AS (
    SELECT SQL_ID, MAX(N_EXEC) AS N_EXEC
    FROM V$SQLTEXT
    GROUP BY SQL_ID
)
SELECT 
    H.SQL_ID,
    H.TOP_SQL_TEXT,                  -- 历史记录中的 SQL 文本（可直接用于查看）
    H.SQL_PLAN,                      -- 执行计划文本（包含操作符和代价）
    E.N_EXEC AS 执行次数
FROM 
    V$PLN_HISTORY H
    JOIN EXEC_STATS E ON H.SQL_ID = E.SQL_ID
WHERE 
    REGEXP_LIKE(H.SQL_PLAN, '#CSCN2: *\&#91; *(&#91;1-9]&#91;0-9]{2,}),')   -- 代价 ≥ 100 的全表扫描
    AND E.N_EXEC &gt; 0                 -- 只统计有执行记录的 SQL
    AND H.TOP_SQL_TEXT NOT LIKE '%/*+%'
ORDER BY 
    E.N_EXEC DESC;                   -- 执行次数高的排在前面</code></pre>



<h2 class="wp-block-heading">三、补充-阻塞会话相关</h2>



<h3 class="wp-block-heading">3.1 阻塞源-SQL有问题用第三个</h3>



<pre class="wp-block-code"><code>SELECT
    BLOCKER.SESS_ID AS BLOCKER_SESSID,
    BLOCKER.STATE AS BLOCKER_SESS_STATE,
    BLOCKER.TRX_ID AS BLOCKER_TRXID,
    SQLTEXT.SQL_ID AS BLOCKER_SQLID,
    BLOCKED.SESS_ID AS BLOCKED_SESSID,
    BLOCKED.TRX_ID AS BLOCKED_TRXID,
    SUBSTR(SF_GET_SESSION_SQL(BLOCKER.SESS_ID), 0, 130) AS BLOCKER_FULLSQL,
    REPLACE(BLOCKER.CLNT_IP, '::FFFF:') || '-' || BLOCKER.CLNT_HOST AS BLOCKER_CLNT_IP,
    DATEDIFF(SS, BLOCKED.LAST_RECV_TIME, SYSDATE) AS "BLOCKED_TIME(S)",
    TRXWAIT.WAIT_TIME / 1000 AS "WAIT_TIME(S)"
FROM
    V$TRXWAIT TRXWAIT,
    V$SESSIONS BLOCKED,
    V$SESSIONS BLOCKER,
    V$SQLTEXT SQLTEXT
WHERE
    TRXWAIT.ID = BLOCKED.TRX_ID
    AND TRXWAIT.WAIT_FOR_ID = BLOCKER.TRX_ID
    AND TRXWAIT.WAIT_FOR_ID NOT IN (SELECT ID FROM V$TRXWAIT)
    AND BLOCKER.SQL_TEXT = SQLTEXT.SQL_TEXT
ORDER BY
    BLOCKER_SESSID DESC,
    "BLOCKED_TIME(S)" DESC;</code></pre>



<h3 class="wp-block-heading">3.2 被阻塞-SQL有问题用第三个</h3>



<pre class="wp-block-code"><code>SELECT
    BLOCKED.SESS_ID AS BLOCKED_SESSID,
    BLOCKED.STATE AS BLOCKED_SESS_STATE,
    BLOCKED.TRX_ID AS BLOCKED_TRXID,
    BLOCKED.SQL_ID AS BLOCKED_SQLID,
    BLOCKER.SESS_ID AS BLOCKER_SESSID,
    BLOCKER.TRX_ID AS BLOCKER_TRXID,
    SUBSTR(SF_GET_SESSION_SQL(BLOCKED.SESS_ID), 0, 130) AS BLOCKED_FULLSQL,
    REPLACE(BLOCKED.CLNT_IP, '::FFFF:') || '-' || BLOCKER.CLNT_HOST AS BLOCKED_CLNT_IP,
    DATEDIFF(SS, BLOCKED.LAST_RECV_TIME, SYSDATE) AS "BLOCKED_TIME(S)"
FROM
    V$TRXWAIT TRXWAIT,
    V$SESSIONS BLOCKED,
    V$SESSIONS BLOCKER
WHERE
    TRXWAIT.WAIT_FOR_ID = BLOCKED.TRX_ID
    AND TRXWAIT.WAIT_FOR_ID = BLOCKER.TRX_ID
ORDER BY
    BLOCKER_SESSID DESC,
    "BLOCKED_TIME(S)" DESC;</code></pre>



<h3 class="wp-block-heading">3.3 一条SQL查阻塞</h3>



<p>因为结果集按WAIT_TIME降序排序，所以最上面的事务大概率就是阻塞源</p>



<pre class="wp-block-code"><code>WITH A AS
     (SELECT SESS_ID  AS WAITOR_SESSID,
             SQL_TEXT AS WAITOR_SQL,
             ID       AS WAITOR,
             WAIT_FOR_ID, -- 所等待的事物ID
             WAIT_TIME
        FROM SYS."V$SESSIONS" V,
             V$TRXWAIT        T
       WHERE TRX_ID = ID
     )
     ,
     B AS
     (SELECT SESS_ID          AS WAIT_FOR_SESSID,
             SQL_TEXT         AS WAIT_FOR_SQL,
             ID               AS WAITOR
        FROM SYS."V$SESSIONS"    V,
             V$TRXWAIT           T
       WHERE TRX_ID = WAIT_FOR_ID
     )
  SELECT A.*,
         B.WAIT_FOR_SESSID,
         B.WAIT_FOR_SQL,
         'SP_CANCEL_SESSION_OPERATION(' || WAIT_FOR_SESSID || ');' AS CANCEL_SESS_SQL,
         'SP_CLOSE_SESSION(' || WAIT_FOR_SESSID || ');'            AS KILL_SESS_SQL
    FROM A, B
   WHERE A.WAITOR = B.WAITOR
ORDER BY WAIT_TIME DESC;</code></pre>



<p>完善-带被阻塞事务的数量</p>



<pre class="wp-block-code"><code>WITH A AS
     (SELECT SESS_ID  AS WAITOR_SESSID,
             SQL_TEXT AS WAITOR_SQL,
             ID       AS WAITOR,
             WAIT_FOR_ID, -- 所等待的事物ID
             WAIT_TIME
        FROM SYS."V$SESSIONS" V,
             V$TRXWAIT        T
       WHERE TRX_ID = ID
     )
     ,
     B AS
     (SELECT SESS_ID          AS WAIT_FOR_SESSID,
             SQL_TEXT         AS WAIT_FOR_SQL,
             ID               AS WAITOR
        FROM SYS."V$SESSIONS"    V,
             V$TRXWAIT           T
       WHERE TRX_ID = WAIT_FOR_ID
     )
SELECT WAITOR_SESSID,
       WAITOR,
       WAITOR_SQL,
       WAIT_TIME,
       WAIT_FOR_SESSID,
       WAIT_FOR_ID,
       WAIT_FOR_SQL,
       BLOCKED_COUNT,
       'SP_CANCEL_SESSION_OPERATION(' || WAIT_FOR_SESSID || ');' AS CANCEL_SESS_SQL,
       'SP_CLOSE_SESSION(' || WAIT_FOR_SESSID || ');'            AS KILL_SESS_SQL
FROM (
    SELECT A.*,
           B.WAIT_FOR_SESSID,
           B.WAIT_FOR_SQL,
           COUNT(*) OVER (PARTITION BY B.WAIT_FOR_SESSID) AS BLOCKED_COUNT,
           ROW_NUMBER() OVER (PARTITION BY B.WAIT_FOR_SESSID ORDER BY A.WAIT_TIME DESC) AS RN
    FROM A, B
    WHERE A.WAITOR = B.WAITOR
) t
WHERE RN = 1
ORDER BY WAIT_TIME DESC;</code></pre>



<h3 class="wp-block-heading">3.4 阻塞源头</h3>



<pre class="wp-block-code"><code>SELECT
    'SP_CANCEL_SESSION_OPERATION(' || S.SESS_ID || ');' AS 取消会话,
    'SP_CLOSE_SESSION(' || S.SESS_ID || ');' AS            清除会话,
    S.SESS_ID AS                                           会话ID,
    S.TRX_ID AS                                            事务ID,
    S.USER_NAME AS                                         用户名,
    S.SQL_TEXT AS                                          当前SQL,
    S.STATE AS                                             会话状态,
    S.CLNT_IP AS                                           客户端IP,
    S.APPNAME AS                                           应用名,
    S.CREATE_TIME AS                                       会话创建时间
FROM
    V$SESSIONS S
WHERE
    S.TRX_ID IN (SELECT DISTINCT T.WAIT_FOR_ID
                 FROM V$TRXWAIT T
                 WHERE T.WAIT_FOR_ID NOT IN (SELECT ID FROM V$TRXWAIT));</code></pre>



<h2 class="wp-block-heading">另附</h2>



<p>相关的系统动态视图</p>



<ul class="wp-block-list">
<li>显示会话的具体信息：V$SESSIONS</li>



<li>显示所有活动事务的信息：V$TRX</li>



<li>显示事务等待信息：V$TRXWAIT</li>



<li>显示活动事务视图信息：V$TRX_VIEW</li>



<li>显示当前系统中锁的状态：V$LOCK</li>



<li>显示死锁的历史信息：V$DEADLOCK_HISTORY</li>



<li>当前正在执行的SQL语句的资源开销：V$SQL_STAT
<ul class="wp-block-list">
<li>需要ENABLE_MONITOR=1</li>
</ul>
</li>



<li>历史SQL语句执行的资源开销：V$SQL_STAT_HISTORY
<ul class="wp-block-list">
<li>需要ENABLE_MONITOR=1</li>
</ul>
</li>



<li>显示系统最近1000条执行时间超过预定值的SQL语句：V$LONG_EXEC_SQLS
<ul class="wp-block-list">
<li>需要ENABLE_MONITOR=1、MONITOR_TIME=1</li>



<li>预定值可通过<code>SP_SET_LONG_TIME(5000);</code>&#8211;修改为5S以上的SQL</li>



<li><code>SELECT SF_GET_LONG_TIME();</code>&#8211;可查看当前值</li>
</ul>
</li>



<li>显示系统自启动以来执行时间最长的20条SQL语句：V$SYSTEM_LONG_EXEC_SQLS
<ul class="wp-block-list">
<li>需要ENABLE_MONITOR=1、MONITOR_TIME=1</li>
</ul>
</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%8e%e4%bc%9a%e8%af%9d%e7%9b%b8%e5%85%b3%e7%9a%84sql%e5%92%8c%e5%91%bd%e4%bb%a4/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>达梦数据库服务器分析 CPU 爆满的原因</title>
		<link>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e6%9c%8d%e5%8a%a1%e5%99%a8%e5%88%86%e6%9e%90-cpu-%e7%88%86%e6%bb%a1%e7%9a%84%e5%8e%9f%e5%9b%a0/</link>
					<comments>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e6%9c%8d%e5%8a%a1%e5%99%a8%e5%88%86%e6%9e%90-cpu-%e7%88%86%e6%bb%a1%e7%9a%84%e5%8e%9f%e5%9b%a0/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Wed, 11 Dec 2024 09:19:11 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=957</guid>

					<description><![CDATA[一、问题描述 当发现达梦数据库所在服务器CPU爆满需要排查时，可通过如下方法排查。 二、问题排查 （1）查询已 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="一、问题描述">一、问题描述</h2>



<p>当发现达梦数据库所在服务器CPU爆满需要排查时，可通过如下方法排查。</p>



<h2 class="wp-block-heading" id="二、问题排查">二、问题排查</h2>



<h3 class="wp-block-heading">（1）查询已执行超过2秒的活动SQL</h3>



<pre class="wp-block-code"><code>SELECT * FROM
	(
		SELECT
			SESS_ID,
			SQL_TEXT,
			DATEDIFF (SS, LAST_RECV_TIME, SYSDATE) Y_EXETIME,
			SF_GET_SESSION_SQL (SESS_ID) FULLSQL,
			CLNT_IP
		FROM
			V$SESSIONS
		WHERE
			STATE = 'ACTIVE'
	)
WHERE
	Y_EXETIME &gt;= 2;</code></pre>



<h3 class="wp-block-heading">（2）查询是否有阻塞</h3>



<pre class="wp-block-code"><code>SELECT * FROM V$TRXWAIT;</code></pre>



<h3 class="wp-block-heading">（3）查询被阻塞的会话和引起阻塞的会话</h3>



<pre class="wp-block-code"><code>SELECT
	SYSDATE STATTIME,
	DATEDIFF (SS, S1."LAST_SEND_TIME", SYSDATE) SS,
	'被阻塞的会话' WT,
	S1."SESS_ID" WT_SESS_ID,
	S1."SQL_TEXT" WT_SQL_TEXT,
	S1."STATE" WT_STATE,
	S1."TRX_ID" WT_TRX_ID,
	S1."USER_NAME" WT_USER_NAME,
	S1."CLNT_IP" WT_CLNT_IP,
	S1."APPNAME" WT_APPNAME,
	S1."LAST_SEND_TIME" WT_LAST_SEND_TIME,
	'引起阻塞的会话' FM,
	S2."SESS_ID" FM_SESS_ID,
	S2."SQL_TEXT" FM_SQL_TEXT,
	S2."STATE" FM_STATE,
	S2."TRX_ID" FM_TRX_ID,
	S2."USER_NAME" FM_USER_NAME,
	S2."CLNT_IP" FM_CLNT_IP,
	S2."APPNAME" FM_APPNAME,
	S2."LAST_SEND_TIME" FM_LAST_SEND_TIME
FROM
	V$SESSIONS S1,
	V$SESSIONS S2,
	V$TRXWAIT W
WHERE
	S1.TRX_ID = W.ID
	AND S2.TRX_ID = W.WAIT_FOR_ID;</code></pre>



<p>根据查到的引起阻塞的信息，确认是否可以杀掉阻塞源头的会话，可以杀掉源头尝试消除阻塞。</p>



<p>杀掉后如未完全消除阻塞，需要再次确认是否可以进行进一步的清除会话，以完全解除阻塞。</p>



<p>可能用到的SQL-查杀所有引起阻塞的会话</p>



<pre class="wp-block-code"><code>DECLARE
V_CNT INT;
BEGIN
	SELECT COUNT(SESS_ID) INTO V_CNT FROM "SYS"."V$SESSIONS" WHERE STATE = 'ACTIVE';
    IF V_CNT&gt;=50 THEN
		FOR REC IN (SELECT SESS_ID FROM V$SESSIONS WHERE STATE = 'ACTIVE' AND SQL_TEXT LIKE '<strong>UPDATE %</strong>' AND CLNT_IP&lt;&gt;'::1' ORDER BY 1)
	    LOOP
			EXECUTE IMMEDIATE 'CALL SP_CLOSE_SESSION('||REC.SESS_ID||');';
	    END LOOP;
    END IF;
END;
/</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>注意<br>由于一般阻塞都是由更新数据引起，所以SQL中以清除UPDATE语句为例。</p>
</blockquote>



<p>上述是比较常见的情况，一些特别情况下（系统刚刚迁移过来）可能是由于SQL语句的执行计划不太好，就需要排查慢SQL或者并发比较高的SQL。排查方法如下：</p>



<pre class="wp-block-code"><code>--查看活动会话中执行时间早的SQL
SQL&gt; select sql_text,user_name,create_time from v$sessions where state='ACTIVE' order by create_time desc;

--查看实时SQL并发
SQL&gt; select sql_text,user_name,count(1) from v$sessions where state='ACTIVE' group by sql_text,user_name order by 3;</code></pre>



<p>重点查看上述结果集中最靠后边的（靠后边都是执行慢或并发高的）一些SQL的执行计划，并优化，便能将CPU的利用率降下来。</p>



<h3 class="wp-block-heading">（4）</h3>



<p>top命令查看占用高的线程PID</p>



<pre class="wp-block-code"><code>top -b -H -n 1 > top_threads.txt</code></pre>



<p>再根据文件中的PID，查询V$SESSIONS视图</p>



<pre class="wp-block-code"><code>SELECT * FROM V$SESSIONS WHERE THRD_ID = '1162792';</code></pre>



<h2 class="wp-block-heading" id="另附">另附</h2>



<p>清除所有会话的方法</p>



<pre class="wp-block-code"><code>BEGIN
        FOR REC IN
        (
                SELECT * FROM V$SESSIONS WHERE SESS_ID &lt;&gt; SESSID()
        )
        LOOP
                SP_CLOSE_SESSION(REC.SESS_ID);
        END LOOP;
END;
/</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93%e6%9c%8d%e5%8a%a1%e5%99%a8%e5%88%86%e6%9e%90-cpu-%e7%88%86%e6%bb%a1%e7%9a%84%e5%8e%9f%e5%9b%a0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>达梦数据库SQL报错之记录超长</title>
		<link>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e8%ae%b0%e5%bd%95%e8%b6%85%e9%95%bf/</link>
					<comments>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e8%ae%b0%e5%bd%95%e8%b6%85%e9%95%bf/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Wed, 11 Dec 2024 09:05:43 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=952</guid>

					<description><![CDATA[一、问题描述 达梦数据库在初始化实例时会有一个参数PAGE_SIZE即页大小，默认是8K。 配置不同的页大小， [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="一、问题描述">一、问题描述</h2>



<p>达梦数据库在初始化实例时会有一个参数<code>PAGE_SIZE</code>即页大小，默认是8K。</p>



<p>配置不同的页大小，字段所支持的存储长度会有所不同，会有如下限制</p>



<div class="wpdt-c row wpDataTableContainerSimpleTable wpDataTables wpDataTablesWrapper
"
    >
        <table id="wpdtSimpleTable-21"
           style="border-collapse:collapse;
                   border-spacing:0px;"
           class="wpdtSimpleTable wpDataTable"
           data-column="4"
           data-rows="5"
           data-wpID="21"
           data-responsive="0"
           data-has-header="0">

                    <tbody>        <tr class="wpdt-cell-row " >
                                <td class="wpdt-cell wpdt-fs-000016 wpdt-bold"
                                            data-cell-id="A1"
                    data-col-index="0"
                    data-row-index="0"
                    style=" width:10.783410138249%;                    padding:10px;
                    "
                    >
                                        数据库页大小                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016 wpdt-bold"
                                            data-cell-id="B1"
                    data-col-index="1"
                    data-row-index="0"
                    style=" width:28.479262672811%;                    padding:10px;
                    "
                    >
                                        每个字符类型字段实际最大长度（字节）                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016 wpdt-bold"
                                            data-cell-id="C1"
                    data-col-index="2"
                    data-row-index="0"
                    style=" width:31.428571428571%;                    padding:10px;
                    "
                    >
                                        每行记录最大字段外其他字段总长度（字节）                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016 wpdt-bold"
                                            data-cell-id="D1"
                    data-col-index="3"
                    data-row-index="0"
                    style=" width:29.308755760369%;                    padding:10px;
                    "
                    >
                                        表空间单个数据文件最小 (MB)/最大 (MB)                    </td>
                                        </tr>
                            <tr class="wpdt-cell-row " >
                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="A2"
                    data-col-index="0"
                    data-row-index="1"
                    style="                    padding:10px;
                    "
                    >
                                        4 KB                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="B2"
                    data-col-index="1"
                    data-row-index="1"
                    style="                    padding:10px;
                    "
                    >
                                        1938                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="C2"
                    data-col-index="2"
                    data-row-index="1"
                    style="                    padding:10px;
                    "
                    >
                                        2047                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="D2"
                    data-col-index="3"
                    data-row-index="1"
                    style="                    padding:10px;
                    "
                    >
                                        16/8388608                    </td>
                                        </tr>
                            <tr class="wpdt-cell-row " >
                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="A3"
                    data-col-index="0"
                    data-row-index="2"
                    style="                    padding:10px;
                    "
                    >
                                        8 KB                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="B3"
                    data-col-index="1"
                    data-row-index="2"
                    style="                    padding:10px;
                    "
                    >
                                        3878                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="C3"
                    data-col-index="2"
                    data-row-index="2"
                    style="                    padding:10px;
                    "
                    >
                                        4095                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="D3"
                    data-col-index="3"
                    data-row-index="2"
                    style="                    padding:10px;
                    "
                    >
                                        32/16777216                    </td>
                                        </tr>
                            <tr class="wpdt-cell-row " >
                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="A4"
                    data-col-index="0"
                    data-row-index="3"
                    style="                    padding:10px;
                    "
                    >
                                        16 KB                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="B4"
                    data-col-index="1"
                    data-row-index="3"
                    style="                    padding:10px;
                    "
                    >
                                        8000                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="C4"
                    data-col-index="2"
                    data-row-index="3"
                    style="                    padding:10px;
                    "
                    >
                                        8195                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="D4"
                    data-col-index="3"
                    data-row-index="3"
                    style="                    padding:10px;
                    "
                    >
                                        64/33554432                    </td>
                                        </tr>
                            <tr class="wpdt-cell-row " >
                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="A5"
                    data-col-index="0"
                    data-row-index="4"
                    style="                    padding:10px;
                    "
                    >
                                        32 KB                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="B5"
                    data-col-index="1"
                    data-row-index="4"
                    style="                    padding:10px;
                    "
                    >
                                        8188                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="C5"
                    data-col-index="2"
                    data-row-index="4"
                    style="                    padding:10px;
                    "
                    >
                                        16176                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="D5"
                    data-col-index="3"
                    data-row-index="4"
                    style="                    padding:10px;
                    "
                    >
                                        128/67108864                    </td>
                                        </tr>
                    </table>
</div><style id='wpdt-custom-style-21'>
.wpdt-fs-000016 { font-size: 16px !important;}
</style>




<p>当表中一行记录所以字段长度之和占用页大小的一半时就会出现记录超长的报错，如图</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="669" height="501" src="https://www.lemonary.cn/wp-content/uploads/2025/03/image-106-edited.png" alt="" class="wp-image-1733" style="object-fit:cover" srcset="https://www.lemonary.cn/wp-content/uploads/2025/03/image-106-edited.png 669w, https://www.lemonary.cn/wp-content/uploads/2025/03/image-106-edited-300x225.png 300w" sizes="auto, (max-width: 669px) 100vw, 669px" /></figure>



<h2 class="wp-block-heading" id="二、问题解决">二、问题解决</h2>



<p>考虑到实际场景，解决方法有如下几种：</p>



<ol class="wp-block-list">
<li>找到表中VARCHAR类型比较长的字段（如 VARCHAR(8000) 这种），修改成 TEXT 类型。</li>



<li>重新初始化数据库，把页大小配置成32KB。这是一个底层参数，在数据库生命周期内都不能更改，所以必须重新初始化。（对于表中 varchar2 类型较长，并且字段较多的情况不太适合，这种情况采用方法1解决）</li>



<li>尝试给表启用超长记录：<code>ALTER TABLE 模式名.表名 ENABLE USING LONG ROW;</code></li>
</ol>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>注意<br>方法1中TEXT大字段是不参与问题描述中的长度计算的，大字段长度能够达到 2 GB。但是要注意，大字段的使用和普通字段是有区别的。</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e8%ae%b0%e5%bd%95%e8%b6%85%e9%95%bf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>达梦数据库SQL报错之字符串截断</title>
		<link>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e5%ad%97%e7%ac%a6%e4%b8%b2%e6%88%aa%e6%96%ad/</link>
					<comments>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e5%ad%97%e7%ac%a6%e4%b8%b2%e6%88%aa%e6%96%ad/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Fri, 06 Dec 2024 06:23:10 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=877</guid>

					<description><![CDATA[一、问题描述 在达梦数据库中报错字符串截断存在多种情况，报错原因不一。 二、问题解决 2.1 大字段模糊查询  [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="一、问题描述">一、问题描述</h2>



<p>在达梦数据库中报错字符串截断存在多种情况，报错原因不一。</p>



<h2 class="wp-block-heading" id="二、问题解决">二、问题解决</h2>



<h3 class="wp-block-heading" id="2.1-大字段模糊查询">2.1 大字段模糊查询</h3>



<p>当查询条件中字段为TEXT，并进行LIKE模糊查询时报错。</p>



<p>报错原因是因为存在隐式转换，转换时截断而导致报错。</p>



<p><strong>解决方法一</strong></p>



<p>首先，查看一下该大字段长度</p>



<pre class="wp-block-code"><code>SELECT DBMSLOB.GETLENGTH(字段);</code></pre>



<p>再根据长度尝试修改参数<code>CLOB_LIKE_MAX_LEN</code>【静态参数】的值，并重启数据库。</p>



<p>CLOB_LIKE_MAX_LEN：LIKE 语句中 CLOB 类型的最大长度，单位 KB，取值范围 8~102400。</p>



<p>修改SQL</p>



<pre class="wp-block-code"><code>SP_SET_PARA_VALUE(2, 'CLOB_LIKE_MAX_LEN', 20480);
--或者
ALTER SYSTEM SET 'CLOB_LIKE_MAX_LEN' = 20480 SPFILE;</code></pre>



<p><strong>解决方法二</strong></p>



<p>通过包的方法<code>DBMS_LOB.SUBSTR</code>可以避免报错</p>



<pre class="wp-block-code"><code>SELECT 1 WHERE DBMS_LOB.SUBSTR(TEXT_COLUMN) LIKE '%XXX%';</code></pre>



<h3 class="wp-block-heading" id="2.2-WM_CONCAT/LISTAGG-拼接">2.2 WM_CONCAT/LISTAGG 拼接</h3>



<p>当使用WM_CONCAT/LISTAGG进行字符串拼接，拼接太多时报错。</p>



<p>报错原因是因为WM_CONCAT/LISTAGG函数返回类型为VARCHAR类型，所以有截断的可能。</p>



<p><strong>解决方法一</strong></p>



<p>使用LISTAGG2集函数替换WM_CONCAT/LISTAGG函数</p>



<pre class="wp-block-code"><code>SELECT LISTAGG2(NAME, ', ') WITHIN GROUP (ORDER BY NAME) AS "拼接结果" FROM PRODUCTION.PRODUCT;</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>注意<br>1.WM_CONCAT 也可以写成 WMSYS.WM_CONCAT<br>2.WM_CONCAT 和 LISTAGG 返回类型为 VARCHAR<br>3.LISTAGG2 返回类型为 CLOB</p>
</blockquote>



<p><strong>解决方法二</strong></p>



<p>后续版本可以通过修改参数<code>WM_CONCAT_LOB</code>【静态参数】指定<code>WM_CONCAT</code>函数的返回值类型。0：VARCHAR 类型；1：CLOB 类型</p>



<p>修改SQL</p>



<pre class="wp-block-code"><code>SP_SET_PARA_VALUE(2, 'WM_CONCAT_LOB', 1);
--或者
ALTER SYSTEM SET 'WM_CONCAT_LOB' = 1 SPFILE;</code></pre>



<p>静态参数，修改后需重启数据库生效。</p>



<h2 class="wp-block-heading" id="三、补充">三、补充</h2>



<p>如再有其他情况报错：字符串截断。发现后即同步到该文章。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e5%ad%97%e7%ac%a6%e4%b8%b2%e6%88%aa%e6%96%ad/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>达梦数据库SQL报错之多版本操作冲突过多 或 Too many mvcc conflict</title>
		<link>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e5%a4%9a%e7%89%88%e6%9c%ac%e6%93%8d%e4%bd%9c%e5%86%b2%e7%aa%81%e8%bf%87%e5%a4%9a-%e6%88%96-too-many-mvcc-conflict/</link>
					<comments>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e5%a4%9a%e7%89%88%e6%9c%ac%e6%93%8d%e4%bd%9c%e5%86%b2%e7%aa%81%e8%bf%87%e5%a4%9a-%e6%88%96-too-many-mvcc-conflict/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Fri, 06 Dec 2024 02:06:14 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=865</guid>

					<description><![CDATA[一、问题描述 MVCC 多版本并发控制是指维持一个数据的多个版本，在保证事务隔离级的情况下，使得读写操作没有冲 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="一、问题描述">一、问题描述</h2>



<p>MVCC 多版本并发控制是指维持一个数据的多个版本，在保证事务隔离级的情况下，使得读写操作没有冲突，提高了数据库并发读写的性能。<br>因此可以解决在并发读写数据库时，做到在读操作时不用阻塞写操作，写操作也不用阻塞读操作。<br>当对一张表批量修改且并发量较大时，可能会报 MVCC 多版本操作冲突过多的错误。多会话同时修改表数据时报错：Too many mvcc conflict。</p>



<h2 class="wp-block-heading" id="二、问题解决">二、问题解决</h2>



<p>修改数据库<code>dm.ini</code>文件中静态参数<code>MVCC_RETRY_TIMES</code>和<code>BDTA_SIZE</code>，重启数据库。</p>



<div class="wpdt-c row wpDataTableContainerSimpleTable wpDataTables wpDataTablesWrapper
"
    >
        <table id="wpdtSimpleTable-18"
           style="border-collapse:collapse;
                   border-spacing:0px;"
           class="wpdtSimpleTable wpDataTable"
           data-column="3"
           data-rows="3"
           data-wpID="18"
           data-responsive="0"
           data-has-header="0">

                    <tbody>        <tr class="wpdt-cell-row " >
                                <td class="wpdt-cell wpdt-bold wpdt-fs-000016"
                                            data-cell-id="A1"
                    data-col-index="0"
                    data-row-index="0"
                    style=" width:22.846441947566%;                    padding:10px;
                    "
                    >
                                        参数名称                    </td>
                                                <td class="wpdt-cell wpdt-bold wpdt-fs-000016"
                                            data-cell-id="B1"
                    data-col-index="1"
                    data-row-index="0"
                    style=" width:70.536828963795%;                    padding:10px;
                    "
                    >
                                        参数描述及建议                    </td>
                                                <td class="wpdt-cell wpdt-bold wpdt-fs-000016"
                                            data-cell-id="C1"
                    data-col-index="2"
                    data-row-index="0"
                    style=" width:6.6167290886392%;                    padding:10px;
                    "
                    >
                                        属性                    </td>
                                        </tr>
                            <tr class="wpdt-cell-row " >
                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="A2"
                    data-col-index="0"
                    data-row-index="1"
                    style="                    padding:10px;
                    "
                    >
                                        MVCC_RETRY_TIMES                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016 wpdt-align-left"
                                            data-cell-id="B2"
                    data-col-index="1"
                    data-row-index="1"
                    style="                    padding:10px;
                    "
                    >
                                        指定发生 MVCC 冲突时的最大重试次数。有效值范围（0~4294967294） 注：MPP 下此参数无效，发生 MVCC 冲突时将直接报错。                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="C2"
                    data-col-index="2"
                    data-row-index="1"
                    style="                    padding:10px;
                    "
                    >
                                        静态                    </td>
                                        </tr>
                            <tr class="wpdt-cell-row " >
                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="A3"
                    data-col-index="0"
                    data-row-index="2"
                    style="                    padding:10px;
                    "
                    >
                                        BDTA_SIZE                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016 wpdt-align-left"
                                            data-cell-id="B3"
                    data-col-index="1"
                    data-row-index="2"
                    style="                    padding:10px;
                    "
                    >
                                        BDTA 缓存的记录数。有效值范围（1~10000）。                    </td>
                                                <td class="wpdt-cell wpdt-fs-000016"
                                            data-cell-id="C3"
                    data-col-index="2"
                    data-row-index="2"
                    style="                    padding:10px;
                    "
                    >
                                        静态                    </td>
                                        </tr>
                    </table>
</div><style id='wpdt-custom-style-18'>
.wpdt-fs-000016 { font-size: 16px !important;}
</style>




<p>修改<code>MVCC_RETRY_TIMES</code>参数值为 100，重启数据库。</p>



<p>修改SQL</p>



<pre class="wp-block-code"><code>SP_SET_PARA_VALUE(2, 'MVCC_RETRY_TIMES', 100);
--或者
ALTER SYSTEM SET 'MVCC_RETRY_TIMES' = 100 SPFILE;</code></pre>



<p><strong>参数分析</strong></p>



<p>MVCC_RETRY_TIMES 参数越大，发生 MVCC 冲突时的最大重试次数越多，报错可能性越低。<br>BDTA_SIZE 参数越大，批量数据处理大小（BDTA 的大小）越大，报错可能性越大。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>注意<br>在实际业务场景中，同一时刻并发执行修改同一条数据会导致此报错，建议分析确认应用中同一时刻不同事务修改同一条数的业务合理性。达梦中控制并发冲突次数的参数为 <code>MVCC_RETRY_TIMES</code>，默认为 5，该参数可从一定程度上缓解冲突的概率。但该错误信息不是并发操作触发锁机制控制锁等待，而是同一时刻不同的事务修改同一条数据导致，故要综合评估业务逻辑的合理性来进行解决。</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e5%a4%9a%e7%89%88%e6%9c%ac%e6%93%8d%e4%bd%9c%e5%86%b2%e7%aa%81%e8%bf%87%e5%a4%9a-%e6%88%96-too-many-mvcc-conflict/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>达梦数据库SQL报错之试图在blob或者clob列上排序或比较</title>
		<link>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e8%af%95%e5%9b%be%e5%9c%a8blob%e6%88%96%e8%80%85clob%e5%88%97%e4%b8%8a%e6%8e%92%e5%ba%8f%e6%88%96%e6%af%94%e8%be%83/</link>
					<comments>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e8%af%95%e5%9b%be%e5%9c%a8blob%e6%88%96%e8%80%85clob%e5%88%97%e4%b8%8a%e6%8e%92%e5%ba%8f%e6%88%96%e6%af%94%e8%be%83/#respond</comments>
		
		<dc:creator><![CDATA[shine]]></dc:creator>
		<pubDate>Fri, 06 Dec 2024 01:41:48 +0000</pubDate>
				<category><![CDATA[DM]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.lemonary.cn/?p=859</guid>

					<description><![CDATA[一、问题描述 在达梦数据库中对大字段类型的列进行排序或比较时会报错[-2685]：试图在blob或者clob列 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="一、问题描述">一、问题描述</h2>



<p>在达梦数据库中对大字段类型的列进行排序或比较时会报错[-2685]：试图在blob或者clob列上排序或比较</p>



<p>测试数据演示示例：</p>



<p><strong>建表</strong></p>



<pre class="wp-block-code"><code>CREATE TABLE T_BCLOB (ID INT PRIMARY KEY, BL BLOB, CL CLOB);</code></pre>



<p>使用SQLark的数据生成功能，生成测试数据。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="594" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-48-1024x594.png" alt="" class="wp-image-860" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-48-1024x594.png 1024w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-48-300x174.png 300w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-48-768x446.png 768w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-48-1536x892.png 1536w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-48.png 1800w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>使用ORDER BY在大字段列进行排序</p>



<pre class="wp-block-code"><code>SELECT * FROM T_BCLOB ORDER BY BL,CL;</code></pre>



<p>报错[-2685]：试图在blob或者clob列上排序或比较</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="405" height="235" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-49.png" alt="" class="wp-image-861" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-49.png 405w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-49-300x174.png 300w" sizes="auto, (max-width: 405px) 100vw, 405px" /></figure>



<h2 class="wp-block-heading" id="二、问题解决">二、问题解决</h2>



<p>首先，对大字段进行排序本身是一个高资源消耗工作，且实际意义不大，不推荐这样编写SQL。不过，如果大字段长度不是很长，且实有这种需求，可以考虑开启功能性参数<code>ENABLE_BLOB_CMP_FLAG</code>【动态，会话级参数】来解决。</p>



<p><strong>开启方法</strong></p>



<pre class="wp-block-code"><code>SP_SET_PARA_VALUE(1, 'ENABLE_BLOB_CMP_FLAG', 1);
--或者
ALTER SYSTEM SET 'ENABLE_BLOB_CMP_FLAG' = 1 BOTH;</code></pre>



<p><strong>查询验证</strong></p>



<pre class="wp-block-code"><code>--查询参数是否修改成功
SELECT PARA_NAME, PARA_VALUE, FILE_VALUE FROM V$DM_INI WHERE PARA_NAME = 'ENABLE_BLOB_CMP_FLAG';
--测试ORDER BY排序功能
SELECT * FROM T_BCLOB ORDER BY BL,CL;</code></pre>



<p><strong>执行成功</strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="780" height="411" src="https://www.lemonary.cn/wp-content/uploads/2024/12/image-50.png" alt="" class="wp-image-862" srcset="https://www.lemonary.cn/wp-content/uploads/2024/12/image-50.png 780w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-50-300x158.png 300w, https://www.lemonary.cn/wp-content/uploads/2024/12/image-50-768x405.png 768w" sizes="auto, (max-width: 780px) 100vw, 780px" /></figure>



<p>排序成功，问题解决。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lemonary.cn/%e8%be%be%e6%a2%a6%e6%95%b0%e6%8d%ae%e5%ba%93sql%e6%8a%a5%e9%94%99%e4%b9%8b%e8%af%95%e5%9b%be%e5%9c%a8blob%e6%88%96%e8%80%85clob%e5%88%97%e4%b8%8a%e6%8e%92%e5%ba%8f%e6%88%96%e6%af%94%e8%be%83/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
