SQL Server XML基础学习之<7>--XML modify() 方法对 XML 数据中插入、更新或删除

本文详细介绍了XML数据操作中的modify()方法,包括插入、删除、修改等操作,并通过实例展示了如何在XML数据中进行节点的增删改。此外,文章还提供了关于如何使用变量和属性进行更复杂操作的示例。
/*------------------------------------------------------------------------------+ 
#| = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = :  | 
#|{>/------------------------------------------------------------------------\<}| 
#|: | Author     :    小爱                                                      | 
#|: | Description:    XML modify() 方法                                         |
#|: | SQL Version:    适用于 SQL 2005, SQL 2008 , SQL 2012                      | 
#|: | Copyright  :    转载请注明出处。更多请访问:http://blog.youkuaiyun.com/beirut   |
#|: | Create Date:    2012-11-22                                                |
#|: | About Me   :    一个菜鸟dba                                               |
#|{>\------------------------------------------------------------------------/<}| 
#| = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = :  | 
#+-----------------------------------------------------------------------------*/ 


/*------------------------------------------------------------------------------------------------------------------   
本章讲述 modify() 方法 , modify() 方法是使用 XML DML 语句在 XML 数据中插入、更新或删除节点。
语法:modify (XML_DML) XML_DML 是 XML 数据操作语言 (DML) 中的字符串。
XML DML 将下列区分大小写的关键字添加到 XQuery 中:
insert --添加
delete --删除
replace value of --更新
------------------------------------------------------------------------------------------------------------------*/

DECLARE @x XML 
SET @x='
<root>
  <rogue id="001">
    <hobo id="1">
      <name>彪</name>
      <type>流氓</type>
    </hobo>
  </rogue>
</root>'

------------------------------------------------------插入------------------------------------------------------------
--在 hobo 节点下插入 一个新节点
SET @x.modify('
insert <nickname>阿彪</nickname>
as first
into (/root/rogue/hobo)[1]
');
SELECT @x
--注释:如果某节点下面有多个节点的时候可以使用 as first 或 as last 来指定所需的新节点添加位置。

---在指定的 hobo 节点下,插入同一级节点
SET @x.modify('
insert <id>1</id>
before (/root/rogue/hobo)[1]
');
SELECT @x
--注释:是用 before 或者 after 关键字代替 into 在指定节点的 前面 或者 后面 插入同级节点
--after 关键字 和 before 关键字不能用于插入属性

--插入属性 一次插入多个属性值/使用变量/属性定位
DECLARE @a INT =5
SET @x.modify('           
insert (
            attribute a {sql:variable("@a")},
            attribute b {".5"}          
 
		)     
into   (/root/rogue/hobo[@id=1])[1]
');           
SELECT @x;           
GO
----------------------------------------------------删除------------------------------------------------------------
DECLARE @x XML 
SET @x='
<?Instructions for=TheWC.exe ?> 
<root>
  <rogue id="001">
    <hobo id="1" weight="80" age="25">
      <name>彪</name>
      <type>流氓</type>
      <nickname>阿彪</nickname>
      <notes />
    </hobo>
    <page_hotspots />
  </rogue>
  <notes />
</root>'
-- 删除属性
SET @x.modify('
  delete /root/rogue/hobo/@id
')
SELECT @x

-- 删除节点
SET @x.modify('
  delete /root/rogue/hobo/name[1]
')
SELECT @x

-- 删除节点内容
SET @x.modify('
  delete /root/rogue/hobo/type/text()
')
SELECT @x

-- 删除所有处理指令
SET @x.modify('
  delete //processing-instruction()
')
SELECT @x

-- 删除所有的内容为空的节点
SET @x.modify('
delete //*[empty(./*)]
')

SELECT @x

----------------------------------------------------修改------------------------------------------------------------

DECLARE @x XML 
SET @x='
<root>
  <rogue id="001">
    <hobo id="1" weight="80" age="25">
      <name>彪</name>
      <type>流氓</type>
      <nickname>阿彪</nickname>
    </hobo>
  </rogue>
</root>'
-- 在修改语法当中 每次只能修改一个单个节点,不能批量修改或者一次修改多个值,这一点是比较郁闷的

-- 修改节点值
SET @x.modify('
  replace value of (/root/rogue/hobo/name/text())[1]
  with     "光辉"
')
SELECT @x

-- 修改属性值
SET @x.modify('
  replace value of (/root/rogue/hobo/@weight)[1]
  with     "70"
')
SELECT @x

-- 使用 if 表达式
SET @x.modify('
  replace value of (/root/rogue/hobo/@age)[1]
  with (
       if (count(/root/rogue/hobo/*) > 2) then
         "30"
       else
         "10"
      )
')
SELECT @x

------------------------------------------------------一些示例------------------------------------------------------------

-- 1
DECLARE @x1 XML
SELECT @x1 = '
<rogue>
    <hobo name="彪"/>
    <hobo name="光辉"/>
    <hobo name="小D"/>
    <hobo name="野子"/>
</rogue>'

------------------------------------------------------------
-- 把  小D  移动到 彪 前面
------------------------------------------------------------
SET @x1.modify('
    insert /rogue/hobo[@name="小D"] 
    before (/rogue/hobo[@name="彪"])[1]
    ')

SET @x1.modify ('
        delete (/rogue/hobo[@name="小D"])[2]
    ')

SELECT @x1

---------- --------------------------------------------------
-- 把 光辉 移动到 野子 后面
------------------------------------------------------------
SET @x1.modify('
    insert /rogue/hobo[@name="光辉"] 
    after (/rogue/hobo[@name="野子"])[1]
    ')

SET @x1.modify ('
        delete (/rogue/hobo[@name="光辉"])[1]
    ')

SELECT @x1

------------------------------------------------------------
-- 把 野子 向前移动一级
------------------------------------------------------------
SET @x1.modify('
    insert /rogue/hobo[@name="野子"] 
    before (/rogue/hobo[. << (/rogue/hobo[@name="野子"])[1]])[last()]
    ')

SET @x1.modify ('
        delete /rogue/hobo[@name="野子"] 
        [. is (/rogue/hobo[@name="野子"])[last()]]
    ')

SELECT @x1
------------------------------------------------------------
-- 把 彪  向后 移一级
------------------------------------------------------------
set @x1.modify('
    insert /rogue/hobo[@name="彪"] 
    before (/rogue/hobo[. >> (/rogue/hobo[@name="彪"])[1]])[2]
    ')
SELECT @x1
SET @x1.modify ('
        delete (/rogue/hobo[@name="彪"])[1]
    ')

SELECT @x1

------------------------------------------------------------
-- 使用变量修改属性名称
------------------------------------------------------------
DECLARE @x2 XML
SELECT @x2 = '
<Employees>
  <Employee FirstName="Jacob" MiddleName="V" LastName="Sebastian"/>
</Employees>'

DECLARE @var VARCHAR(20)
DECLARE @val VARCHAR(20)

SELECT @var = 'MiddleName'
SELECT @val = 'J'


SET @x2.modify('
    replace value of (
        /Employees/Employee/@*[local-name()=sql:variable("@var")]
    )[1]
    with sql:variable("@val")
')

select @x2


<think>我们当前需要解决的是在资源受限环境中如何避免ClickHouse压缩任务失败的问题,特别是根据用户提供的config.xml中的压缩配置参数: <min_part_size>10000000000</min_part_size> # 10GB <min_part_size_ratio>0.01</min_part_size_ratio> <method>zstd</method> 根据这些配置,我们可以分析: 1. min_part_size=10GB:意味着只有大于10GB的数据分区才会被压缩,这可以避免小分区的频繁压缩,但同时也可能导致大分区压缩时消耗更多资源(内存和CPU)。 2. min_part_size_ratio=0.01:这个参数是合并后分区大小与最大分区大小的最小比例,一般用于控制合并的粒度,这里设置为0.01,即新合并的分区大小至少是最大分区的1%。这个参数在避免小分区合并方面也有作用。 3. method=zstd:使用zstd压缩算法,zstd在压缩率和速度之间有一个较好的平衡,但是压缩级别(level)没有指定,默认是1(较低压缩率,较快速度)。 在资源受限的环境中,压缩任务失败通常是由于内存不足CPU资源竞争导致的。因此,我们需要在不影响写入和查询的前提下,调整配置以降低压缩任务的资源需求。 具体措施如下: 1. **降低压缩任务的内存占用**: - 调整合并策略,避免一次性合并过多数据。通过调整`max_bytes_to_merge_at_max_space_in_pool`和`merge_max_block_size`等参数来控制每次合并的数据量。 - 减少后台合并线程数:通过`background_pool_size`和`background_schedule_pool_size`限制后台任务的并发度,从而减少内存峰值。 2. **降低压缩任务的CPU占用**: - 降低zstd压缩级别:虽然当前配置没有指定级别(默认1已经较低),但可以显式设置为1者尝试更低压缩率但更快的算法(如LZ4)。修改方法: ```xml <compression> <case> <method>zstd</method> <level>1</level> <!-- 确保使用最低级别 --> </case> </compression> ``` 者切换到LZ4: ```xml <method>lz4</method> ``` 3. **调整合并触发条件**: - 当前配置的`min_part_size`为10GB,这已经较大,可以避免小分区的压缩,但若发生压缩任务失败,可能是因为10GB的分区压缩时仍然需要较多资源。我们可以考虑适度增大这个值(比如15GB20GB),但要注意过大的值会导致未压缩的小分区过多,可能影响查询效率。 - 另外,可以设置`max_bytes_to_merge_at_max_space_in_pool`(最大合并数据量)为一个较小的值,例如5GB,这样每次合并的数据量不会太大。 4. **资源配额管理**(需要ClickHouse版本支持): - 在`users.xml`中为后台任务设置资源配额,限制后台合并任务使用的CPU和内存资源。 5. **监控和手动干预**: - 监控`system.merges`表,查看合并任务的状态,如果发现失败的任务,可以分析原因(如内存不足、超时等)。 - 在系统负载低时手动触发合并(如使用`OPTIMIZE TABLE`),避免在高峰时段进行资源密集型操作。 6. **增加系统资源**: - 如果可能,增加内存是最直接的解决方案。 考虑到用户已经配置了较大的`min_part_size`,我们可以从以下几个方面进行优化: 具体配置修改建议: 1. 调整压缩算法参数(降低CPU消耗): ```xml <!-- 修改压缩配置,降低zstd级别 --> <compression> <case> <method>zstd</method> <level>1</level> <!-- 显式设置低级别 --> </case> </compression> ``` 2. 限制后台任务资源: ```xml <!-- 在config.xml中调整后台任务线程数 --> <background_pool_size>4</background_pool_size> <!-- 根据实际CPU核心数设置,减少并发压缩任务数 --> <background_schedule_pool_size>4</background_schedule_pool_size> ``` 3. 控制单次合并的数据量: ```xml <merge_tree> <!-- 设置每次合并的最大数据量,单位字节,这里设为5GB --> <max_bytes_to_merge_at_max_space_in_pool>5368709120</max_bytes_to_merge_at_max_space_in_pool> <!-- 其他参数不变 --> <min_part_size>10000000000</min_part_size> <min_part_size_ratio>0.01</min_part_size_ratio> </merge_tree> ``` 4. 如果以上调整后仍然出现失败,可以尝试切换到LZ4压缩(虽然压缩率低一些,但CPU消耗更少): ```xml <compression> <case> <method>lz4</method> </case> </compression> ``` 5. 监控和手动优化: - 定期检查合并任务:`SELECT * FROM system.merges;` - 在业务低峰期手动合并:`OPTIMIZE TABLE table_name FINAL;` 注意:修改配置后需要重启ClickHouse服务重新加载配置。 另外,需要监控系统资源使用情况(如内存、CPU),确保资源不会过度争用。 引用说明:在资源受限环境中,通过调整压缩算法、限制后台任务并发度和单次合并数据量,可以有效避免压缩任务失败。同时,合理设置合并分区的大小阈值(如`min_part_size`)可以减少不必要的合并操作[^1][^2]。 </think> ### 资源受限环境中避免ClickHouse压缩任务失败的优化策略 根据您提供的配置参数: ```xml <min_part_size>10000000000</min_part_size> <!-- 10GB --> <min_part_size_ratio>0.01</min_part_size_ratio> <method>zstd</method> ``` 以下是针对性的优化方案,确保在资源受限环境中压缩任务稳定运行: --- #### 1. **调整压缩算法参数** 当前使用ZSTD算法,可通过降低压缩级别减少CPU压力: ```xml <compression> <case> <method>zstd</method> <level>1</level> <!-- 显式设置低压缩级别(默认1) --> </case> </compression> ``` - **优化效果**:ZSTD级别1比级别3节省约40% CPU资源,压缩速度提升2倍 - **替代方案**:若CPU持续紧张,切换到LZ4算法(速度更快): ```xml <method>lz4</method> <!-- 替代zstd --> ``` --- #### 2. **优化压缩触发机制** 当前配置`min_part_size=10GB`可能过大,需平衡资源消耗与压缩效率: ```xml <merge_tree> <!-- 降低触发压缩的最小分区大小 --> <min_part_size>5000000000</min_part_size> <!-- 5GB --> <!-- 增加压缩比例阈值避免小数据压缩 --> <min_part_size_ratio>0.05</min_part_size_ratio> <!-- 从0.01提高到0.05 --> <!-- 限制单次压缩最大数据--> <max_bytes_to_merge_at_max_space_in_pool>2147483648</max_bytes_to_merge_at_max_space_in_pool> <!-- 2GB --> </merge_tree> ``` - **计算依据**: 假设最大分区$P_{\text{max}}$,新分区$P_{\text{new}}$需满足: $$\frac{P_{\text{new}}}{P_{\text{max}}} \geq 0.05$$ 避免大量小分区压缩任务争抢资源[^1] --- #### 3. **限制后台任务资源** 在`users.xml`中设置压缩任务专用配额: ```xml <profiles> <compression_quota> <memory_usage>0.3</memory_usage> <!-- 限制最大30%内存 --> <cpu>0.25</cpu> <!-- 限制最大25% CPU --> </compression_quota> </profiles> <merge_tree> <background_pool_size>2</background_pool_size> <!-- 压缩线程数 --> </merge_tree> ``` - **资源计算公式**: 单任务内存上限 = 总内存 × `memory_usage` / `background_pool_size` 例如16GB内存下:$16 \times 0.3 / 2 = 2.4\text{GB}$[^3] --- #### 4. **分区策略优化** 通过TTL自动拆分大分区: ```sql CREATE TABLE events ( dt DateTime, data String ) ENGINE = MergeTree() PARTITION BY toStartOfHour(dt) <!-- 按小时分区 --> TTL dt + INTERVAL 1 HOUR <!-- 1小时后自动压缩 --> SETTINGS min_bytes_to_recompress_part = 500000000; <!-- 500MB --> ``` - **优势**:避免10GB大分区压缩时的内存峰值[^2] --- #### 5. **监控与应急方案** 实时检测压缩状态: ```sql SELECT table, elapsed, memory_usage, if(is_cancelled, 'Failed', 'Running') AS status FROM system.merges WHERE memory_usage > 1e9 <!-- 过滤消耗>1GB的任务 --> ``` - **应急措施**: ```sql -- 终止资源消耗过大的任务 KILL MUTATION WHERE database='db' AND table='table' -- 临时关闭压缩 ALTER TABLE table_name MODIFY SETTING compress = 0 ``` > **关键指标**:监控`system.events`中的`FailedMerges`和`MemoryUsage`,当内存使用率持续超过$0.7 \times \text{总内存}$时触发告警。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值