MySql数据库varchar和java字符串length()长度tips

Java 中字符串以 Unicode 方式编码的, 其长度通常计算的是字符数,一个中文也算一个字符:

    public static void main(String[] args) {
        String fileName = "中文English,12345";//中文、英文、标点、数字都算一个字符
        int len = fileName.length();
        System.out.println("len="+ len);
    }

运行结果:

     len=15

MySql中 varchar(n)类型字段,n所表示的也是字符数,并非字节数,所以字段可以插入<=n个字符。一个字符占用多少

字节取决于所使用的字符编码,通常我使用UTF-8,Mysql中一个UTF-8编码的字符占3字节。例如:

CREATE TABLE `group_space` (
  `GROUP_ID` decimal(16,0) NOT NULL,
  `USER_FILE_ID` decimal(18,0) DEFAULT NULL,
  `USER_ID` decimal(20,0) DEFAULT NULL,
  `GROUP_SIZE` decimal(10,0) DEFAULT NULL,
  `CREATE_TIME` timestamp NULL DEFAULT NULL,
  `REVIEW_STATUS` decimal(1,0) DEFAULT NULL,
  `GROUP_NAME` varchar(5) DEFAULT NULL,
  `LAST_OPTIME` timestamp NULL DEFAULT NULL,
  `PUBLIC_LEVEL` decimal(1,0) DEFAULT NULL,
  `ENTER_LEVEL` decimal(1,0) DEFAULT NULL,
  `GROUP_NUMBER` varchar(20) DEFAULT NULL,
  `GROUP_MODEL` decimal(3,0) DEFAULT NULL,
  `GROUP_STATUS` decimal(1,0) DEFAULT NULL,
  `UNI_CONTACT_ID` decimal(20,0) DEFAULT NULL,
  PRIMARY KEY (`GROUP_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

其中GROUP_NAME字段,我定义了个较小的长度方便测试,长度为5,即可以插入5个字符,utf8编码最长存入

15字节。插入数据,给该GROUP_NAME字段赋值5个中文’我是中国人‘,sql如下:


INSERT INTO `group_space` (`GROUP_ID`, `USER_FILE_ID`, `USER_ID`, `GROUP_SIZE`, `CREATE_TIME`, `REVIEW_STATUS`, `GROUP_NAME`, 
`LAST_OPTIME`, `PUBLIC_LEVEL`, `ENTER_LEVEL`, `GROUP_NUMBER`, `GROUP_MODEL`, `GROUP_STATUS`, `UNI_CONTACT_ID`) 
VALUES ('113012502705', '413602500002', '8241', NULL, '2015-09-11 16:24:40', '1', 
'我是中国人', '2015-09-11 16:24:40', '2', '2', '113012502701', '200', '1', NULL);


执行无异常,结果:



同样的insert语句,GROUP_NAME增加一个字符’我是中国人a‘,赋值更换主键后再次执行,抛出异常:

[SQL]INSERT INTO `group_space` (`GROUP_ID`, `USER_FILE_ID`, `USER_ID`, `GROUP_SIZE`, `CREATE_TIME`, `REVIEW_STATUS`, `GROUP_NAME`, 
`LAST_OPTIME`, `PUBLIC_LEVEL`, `ENTER_LEVEL`, `GROUP_NUMBER`, `GROUP_MODEL`, `GROUP_STATUS`, `UNI_CONTACT_ID`) 
VALUES ('113012502706', '413602500002', '8241', NULL, '2015-09-11 16:24:40', '1', 
'我是中国人a', '2015-09-11 16:24:40', '2', '2', '113012502701', '200', '1', NULL);
[Err] 1406 - Data too long for column 'GROUP_NAME' at row 1


当服务出现类似因为字段长度不够的问题时,一般首先想到的是进行字段扩容。但是我负责的系统MySql是个分布式的集群,且数据量在亿级以上,而且出现较长字符串的场景只是偶尔外部原因导致,所以并没有采用字段扩容的方式解决该问题。直接在DAO层做了一个字符串截取,对超过数据库限制的GROUP_NAME字段做截取处理,保留前后一部分字符,去除中间部分,用“...”代替。字符串截取代码:

    public static void main(String[] args) {
        //fileName 字段进行截断(前7字符+...+后10字符)
        String fileName = "MySql数据库varchar长度tips-zhujinq.html";
            int maxLen = 20;
                if (StringUtils.isNotBlank(fileName) && fileName.length() > maxLen) {
                    int len = fileName.length();
                    fileName = fileName.substring(0, (maxLen/2)-3) + "..."+ fileName.substring(len-(maxLen/2), len);
                }
        
        System.out.println("fileName=" + fileName+", newLen=" + fileName.length());
    }


执行结果:

fileName=MySql数据...ujinq.html, newLen=20

最好的方式还是字段扩容。
### 创建酷狗T500榜单爬虫 为了创建一个能够抓取酷歌T500榜单页面的网络爬虫,可以按照如下方法构建程序结构: #### 使用库介绍 - `csv` 库用于将收集的数据写入CSV文件中[^1]。 - `time` 模块用来控制请求之间的延迟时间,防止过频访问给服务器带来压力[^1]。 - `requests` 是发起HTTP请求的核心工具,通过它可以直接获取网页HTML源码。 - `BeautifulSoup` 来自于`bs4`包,专门处理HTML文档解析工作,方便提取所需信息。 - `os` 提供操作系统级别的接口函数,在此主要用于管理本地文件路径以及创建必要的目录结构。 - `pymysql` 负责建立与MySQL数据库间的通信桥梁,并执行SQL语句完成数据持久化操作。 #### 获取目标URL列表 首先需要准备好要爬取的目标链接地址。对于酷狗音乐Top500来说,通常会有一个固定的API端点或者是分页形式展示全部条目。这里假设存在一个基础URL模板,可以通过改变其中某些参数(比如offset偏移量)来遍历整个排行榜。 ```python import requests base_url = "https://www.kugou.com/yy/rank/home/{page}-8888.html?from=rank" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', } for page in range(1, 26): # 假设共有25页 url = base_url.format(page=page) response = requests.get(url=url, headers=headers) if response.status_code != 200: continue html_content = response.text parse_html(html_content) # 解析html内容的方法定义见下文 ``` #### 页面解析逻辑 利用`BeautifulSoup` 对返回的HTML字符串进行分析,定位到包含歌曲名称、歌手姓名等字段的具体标签位置,并将其转换成易于理解的形式。 ```python from bs4 import BeautifulSoup def parse_html(html_str): soup = BeautifulSoup(html_str, features="lxml") song_list = [] items = soup.select('.pc_temp_songlist .pc_temp_item') for item in items: rank = int(item.find('span', class_='pc_temp_num').get_text(strip=True)) title_and_artist = item.find_all('a')[1].get_text().split('-') name = '-'.join(title_and_artist[:-1].strip() duration_tag = item.find('span', class_='pc_temp_tips_r') minutes, seconds = map(int, duration_tag['title'].split(':')) length_in_seconds = minutes * 60 + seconds record = {'Rank': rank, 'Name': name, 'Artist': artist, 'LengthInSeconds': length_in_seconds} save_to_database(record) # 存储至数据库的操作将在后面描述 write_into_file(f"{name},{artist},{length_in_seconds}\n") # 将记录追加到文本文件中 ``` #### 数据存储方式 ##### 文本文件(TXT) 每当成功解析一条有效记录之后,就可以调用下面这个简单的辅助函数把它附加到指定的日志文件里去。 ```python import os.path as osp output_dir = './data' if not osp.exists(output_dir): os.makedirs(output_dir) def write_into_file(line): file_path = osp.join(output_dir, 'kugou_top_500.txt') with open(file_path, mode='a+', encoding='utf-8') as f: f.write(line) ``` ##### CSV 文件 除了纯文本之外,还可以考虑采用更加通用化的表格格式——即逗号分割值(CSV),以便后续导入Excel或其他电子表格软件做进一步加工处理。 ```python import csv def store_as_csv(records): output_file = osp.join(output_dir, 'kugou_top_500.csv') fieldnames = ['Rank', 'Name', 'Artist', 'LengthInSeconds'] exists = osp.isfile(output_file) with open(output_file, newline='', mode=('r+' if exists else 'w'), encoding='utf-8') as csvfile: writer = csv.DictWriter(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL, fieldnames=fieldnames) if not exists: writer.writeheader() # 只有当文件不存在时才写表头 for row_dict in records: writer.writerow(row_dict) ``` ##### MySQL 数据库存储 最后一种方案就是借助关系型数据库管理系统(RDBMS)如MySQL来进行高效检索查询。这一步骤涉及到先配置好相应的连接池对象,再逐行插入新纪录。 ```python import pymysql.cursors db_config = dict( host='localhost', user='root', password='', database='music_rankings', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor ) def init_db(): connection = pymysql.connect(**db_config) try: create_table_query = """ CREATE TABLE IF NOT EXISTS kugou_songs ( id INT AUTO_INCREMENT PRIMARY KEY, rank SMALLINT UNSIGNED NOT NULL UNIQUE, song_name VARCHAR(255), singer_name VARCHAR(255), duration TINYINT UNSIGNED); """ with connection.cursor() as cursor: cursor.execute(create_table_query) connection.commit() finally: connection.close() init_db() def save_to_database(song_info): insert_sql = """INSERT INTO kugou_songs(rank,song_name,singer_name,duration) VALUES (%(Rank)s,%(Name)s,%(Artist)s,FLOOR(%(LengthInSeconds)s / 60)) ON DUPLICATE KEY UPDATE song_name=%(Name)s,singer_name=%(Artist)s,duration=FLOOR(%(LengthInSeconds)s / 60);""" conn = None try: conn = pymysql.connect(**db_config) with conn.cursor() as cur: affected_rows = cur.execute(insert_sql, args=song_info) print(f'Inserted {affected_rows} rows.') conn.commit() except Exception as e: raise RuntimeError(str(e)) finally: if conn is not None and conn.open: conn.close() ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值