一个引号带来的悲剧,字段定义

今天看了一个优化案例觉的挺有代表性,这里记录下来做一个标记,来纪念一下随便的字段定义的问题。

回忆一下,在表的设计中很多人习惯的把表的结构设计成Varchar(64),Varchar(255)之类的,虽然大多数情况只存了5-15个字节.那么我看一下下面这个案例.
查询语句:

SELECT SQL_NO_CACHE channel, COUNT(channel) AS visitors FROM xxx_sources WHERE client_id = 1301 GROUP BY client_id, channel;
该表(client_id,channel)是一个组合索引.
利用explain,看一下执行计划,对于索引使用上看上非常完美

mysql> explain SELECT SQL_NO_CACHE channel, COUNT(channel) AS visitors FROM xxx_sources WHERE client_id = 1301 GROUP BY client_id, channel;
+----+-------------+-------------+-------+--------------------+--------------------+---------+------+----------+--------------------------+
| id | select_type | table       | type  | possible_keys      | key                | key_len | ref  | rows     | Extra                    |
+----+-------------+-------------+-------+--------------------+--------------------+---------+------+----------+--------------------------+
|  1 | SIMPLE      | xxx_sources | index | idx_client_channel | idx_client_channel | 1032    | NULL | 20207319 | Using where; Using index |
+----+-------------+-------------+-------+--------------------+--------------------+---------+------+----------+--------------------------+
1 row in set (0.00 sec)
看一下实际执行:

mysql> SELECT SQL_NO_CACHE channel, COUNT(channel) AS visitors FROM xxx_sources WHERE client_id = 1301 GROUP BY client_id, channel;
+---------+----------+
| channel | visitors |
+---------+----------+
| NULL    |        0 |
+---------+----------+
1 row in set (11.69 sec)
实际执行的情况非常的糟糕.传通的想法,这个执行从索引上执行计划上看非常完美了,好象和MySQL没什么关系了. 在去看一下表的设计会发现client_id也是设计成了
varchar(255).看到这里不防可以使用下面的方法试一下:

mysql> explain SELECT SQL_NO_CACHE channel, COUNT(channel) AS visitors FROM xxx_sources WHERE client_id = '1301' GROUP BY client_id, channel;
+----+-------------+-------------+------+--------------------+--------------------+---------+-------+--------+--------------------------+
| id | select_type | table       | type | possible_keys      | key                | key_len | ref   | rows   | Extra                    |
+----+-------------+-------------+------+--------------------+--------------------+---------+-------+--------+--------------------------+
|  1 | SIMPLE      | xxx_sources | ref  | idx_client_channel | idx_client_channel | 258     | const | 457184 | Using where; Using index |
+----+-------------+-------------+------+--------------------+--------------------+---------+-------+--------+--------------------------+
1 row in set (0.00 sec)
从执行计划上来看,差不多,但实际差多了.具体上来看key_len从1032降到了258,执行计划变成了const基于等于的查找,行数从原来千万级到了十万级了.不算也能明白IO
节省了很多.
再来看实际执行:

mysql> SELECT SQL_NO_CACHE channel, COUNT(channel) AS visitors FROM xxx_sources WHERE client_id = '1301' GROUP BY client_id, channel;
+---------+----------+
| channel | visitors |
+---------+----------+
| NULL    |        0 |
+---------+----------+
1 row in set (0.25 sec)
哇,从11.69秒变成了0.25秒,这是什么概念,优化了多少倍,算一下吧.

看到这里在想什么呢,记住这个案例,嗯,不错,以后还可以加引号优化一下.那为什么不问一下,能不能在优化了,为什么会这样呢?
我们先来看一下第一个问题:
能不能在优化了?
答案是当然可以了.从索引的长度上来看258还是一个非常大的数据,对于client_id这个字段从名字上来看,也只会存数据型的值,那为什么不用的一个int unsigned去存呢,
索引的长度马上会从258降到4。这样不是又节省了很多吗?
接下来看一下第二个问题,为什么会这样呢?
原因有两点,同时基于一个原则,基于成本的优化器。对于client_id在表的定义时定义成了字符型的值,在查询时传入了数值型的值,需要经过一个数值转换,悲剧的开始,最终
导致MySQL选择了一个完成的索引去扫描。

从这个案例上,我们需要注意什么呢?
合理的选择数据类型,基本工太重要了,就这叫赢在起跑线,一切都不能随便了,别把一个表定义成了降了主建外其它全是Varchar(255)。对数据库的double/float这种字
段做索引时一定要小心。

待思考:
为什么加一个引号后索引长度执行计划变成了258,为什么是258呢,不是别的呢。
因为加引号后,mysql不要做数据的转换了。

本文系转载
http://www.mysqlsupport.cn/
<think>我们有一个明确的任务:在Oracle数据库中,使用REPLACE函数将字段中的双引号替换为空。根据用户的问题,他想要替换掉字段中的双引号("),也就是将双引号替换成空字符串(即删除双引号)。在Oracle中,REPLACE函数的语法是:REPLACE(字符串,要替换的子字符串,替换后的子字符串)因此,我们可以这样写:REPLACE(column_name,'"','')注意:双引号在字符串中需要用单引号括起来,即'"'表示一个引号字符。另外,我们参考了站内引用:引用[1]中展示了REPLACE函数的使用:将字符串中的某个字符替换为空(即删除)。例如:REPLACE('helloword','e')的结果是'hlloword'。引用[2]和引用[3]虽然提到了替换和转换,但主要与日期格式和pivot函数有关,与当前问题关系不大,所以我们主要参考引用[1]。因此,我们可以构造如下的SQL语句:假设表名为table_name,字段名为column_name,则:UPDATEtable_nameSETcolumn_name=REPLACE(column_name,'"','');或者,如果我们只是想查询而不更新,可以使用:SELECTREPLACE(column_name,'"','')FROMtable_name;注意:双引号在Oracle字符串中就是普通字符,直接使用即可。但是要注意,在SQL语句中字符串常量是用单引号括起来的,所以双引号字符表示为'"'。此外,用户可能需要注意,如果字段中同时存在单引号和双引号,并且想要替换双引号,那么上面的方法没有问题。如果字段中可能包含其他特殊字符,或者双引号有转义的需求,但在这个问题中,我们只需要替换双引号,所以直接使用即可。但是,如果用户想要替换的是两个连续的双引号(比如在CSV中双引号转义为两个双引号)?但用户没有说明,所以按照替换单个双引号处理。所以,总结回答如下:</think>在 Oracle 数据库中,使用 `REPLACE` 函数替换字段中的双引号为空的语法如下: ```sql -- 查询时替换 SELECT REPLACE(column_name, '"', '') AS cleaned_column FROM your_table; -- 更新数据(永久替换) UPDATE your_table SET column_name = REPLACE(column_name, '"', ''); ``` ### 关键说明: 1. **函数语法** `REPLACE(source_string, search_string, replacement_string)` - 将 `source_string` 中的所有 `search_string` 替换为 `replacement_string` - 替换双引号时:`search_string` 需用单引号包裹双引号 `'"'` - 替换为空:`replacement_string` 设为 `''`(空字符串) 2. **双引号处理** - 双引号 `"` 是普通字符,无需转义(区别于单引号) - 示例:`REPLACE('He"llo"', '"', '')` → 返回 `Hello` 3. **注意事项** - 若字段包含 **单引号**,需用两个单引号转义(`''`),但双引号无需此操作 - 更新数据前建议备份(`UPDATE` 会永久修改数据) ### 示例场景 假设表 `product` 有字段 `remark`: | id | remark | |----|-----------------| | 1 | "质量优等品" | | 2 | 尺寸:10"x20" | 执行更新: ```sql UPDATE product SET remark = REPLACE(remark, '"', ''); ``` 结果: | id | remark | |----|---------------| | 1 | 质量优等品 | | 2 | 尺寸:10x20 | ### 其他场景扩展 若需同时替换 **多种符号**(如双引号+单引号): ```sql SELECT REPLACE(REPLACE(column, '"', ''), '''', '') FROM table; -- 单引号需用两个单引号表示 ``` [^1]: Oracle 的 `REPLACE` 函数可直接处理特殊符号,无需转义双引号。 [^2]: 字符替换时需区分单引号与双引号的处理差异。 [^3]: 函数嵌套可实现多符号批量替换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值