MySQL语句中有IFNULL , 查询结果返回是乱码 [B@类的结果

问题描述:根据开发反馈由于应用查询MySQL数据库的数据,有部分数值出现乱码,如下图所示。整条sql在数据库中查询没有出现这种乱码情况。

在数据库中查询乱码两个字段是如下的结果:



开始进行分析问题是什么原因造成乱码问题:

1、首先数据库查询结果没有问题,那么从应用端开始查。

找到相应的代码处:

   String txyj = map.get("txyj") == null ? "" : map.get("txyj").toString();

   String cxyj = map.get("cxyj") == null ? "" : map.get("cxyj").toString();

写法是最后将以字符串返回到前端页面。

那么我们这个表相应的字段设置的数据类型是什么呢?

以下是该功能查询数据库的sql:

CASE
WHEN a.indexcalno < '201711' THEN
	ifnull(
		(
			SELECT
				sum(fyc)
			FROM
				table1
			WHERE
				agentcode = A.agentcode
			AND wageno = A.indexcalno
			AND riskcode IN (
				SELECT
					CODE
				FROM
					table2
				WHERE
					codetype = 'riskTypeGroup'
			)
			AND riskchnl = '12'
		),
		'0'
	)
ELSE
	ifnull(a.W89, '0')
END txyj,
 CASE
WHEN a.indexcalno < '201711' THEN
	ifnull(
		(
			SELECT
				sum(fyc)
			FROM
				table1
			WHERE
				agentcode = A.agentcode
			AND wageno = A.indexcalno
			AND riskcode IN (
				SELECT
					CODE
				FROM
					table2
				WHERE
					codetype = 'riskTypeProperty'
			)
			AND riskchnl = '12'
		),
		'0'
	)
ELSE
	ifnull(a.W90, '0')
END cxyj,


该sql是使用ifnull函数来进行判断空值,如果是非空则返回前一个expr1。txyj与cxyj都是返回的前一个值。那么前面的子查询最后计算的是sum(fyc)。

`FYC` decimal(12,2) DEFAULT NULL

该字段设置的是decimal。因而结果应该也是decimal。从decimal转换成string输出就是乱码了。但是其他列也是decimal设定的数据类型,那么怎么没有问题呢。

从网上找到了一个解决方法,将ifull后面加个*1.测试确实可以成功。

http://www.jfinal.com/feedback/575

那么加*1也就是将ifnull的结果转换成呗。那么我将ifnull的后面'0'去掉单引号是否也是可以的,测试也是成功的。那到底是什么原因导致这种字符类型转换呢?是sum()求和导致的,还是ifnull()函数导致的?

查询官方文档发现了主要问题所在:

1.先看是不是sum()的问题所在

https://dev.mysql.com/doc/refman/5.6/en/group-by-functions.html

For numeric arguments, the variance and standard deviation functions return a DOUBLE value. The SUM() and AVG() functions return a DECIMAL value for exact-value arguments (integer or DECIMAL), and a DOUBLE value for approximate-value arguments (FLOAT or DOUBLE).

这里说的挺明确的,对于数值参数,方差和标准偏差函数返回一个DOUBLE类型。SUM()和AVG()函数对于精确值参数(integer或DECIMAL)的返回一个DECIMAL,以及近似值参数(float或double)的返回DOUBLE。

The SUM() and AVG() aggregate functions do not work with temporal values. (They convert the values to numbers, losing everything after the first nonnumeric character.) To work around this problem, convert to numeric units, perform the aggregate operation, and convert back to a temporal value. Examples:

SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(time_col))) FROM tbl_name;
SELECT FROM_DAYS(SUM(TO_DAYS(date_col))) FROM tbl_name;

对于时间类型的无法用SUM()和AVG()来直接计算,当遇到非数值字符的时候,后面的值函数就认不到了。所以要将时间转换为数值来计算就可以了。

那么我们是否直接将结果转换为char不就可以了。

2.将结果decimal类型转换为char类型

将子查询中加入convert函数,应用测试问题也得到了解决。

convert(sum(fyc),char)

而跟开发测试确认,如果不用sum()函数,只查一行数据。加上limit 1。也是没有问题的。

那么为什么会需要转换呢?什么原因造成这种字符乱码还是没找到根本原因。

3.查询官方文档,究其根本

还有个函数ifnull还没有查,所以在官方文档中查询ifnull的解释:

https://dev.mysql.com/doc/refman/5.6/en/control-flow-functions.html

有段如下内容:


什么是VARBINARY?

https://dev.mysql.com/doc/refman/5.6/en/binary-varbinary.html

The BINARY and VARBINARY types are similar to CHAR and VARCHAR, except that they contain binary strings rather than nonbinary strings. That is, they contain byte strings rather than character strings. This means they have the binary character set and collation, and comparison and sorting are based on the numeric values of the bytes in the values.

BINARY(二进制)和VARBINARY(可变二进制),就是CHAR和VARCHAR是类型的关系。他们都是二进制字符串而不是包含非二进制字符串。也就是说,它们包含字节串而不是字符串。这意味着它们具有二进制字符集和排序规则,比较和排序是基于字节的数值。

The permissible maximum length is the same for BINARY and VARBINARY as it is for CHAR and VARCHAR, except that the length for BINARY and VARBINARY is a length in bytes rather than in characters.

BINARY和VARBINARY的允许最大长度与CHAR和VARCHAR相同,BINARY和VARBINARY是字节的长度,而不是字符的长度。

那么就按照官方文档把结果查询结果,CTAS创建一张新表看看字段数据类型是什么:

create TABLE tmp
SELECT
 CASE
WHEN a.indexcalno < '201711' THEN
	ifnull(
		(
			SELECT
				sum(fyc)
			FROM
				table1
			WHERE
				agentcode = A.agentcode
			AND wageno = A.indexcalno
			AND riskcode IN (
				SELECT
					CODE
				FROM
					table2
				WHERE
					codetype = 'riskTypeGroup'
			)
			AND riskchnl = '12'
		),
		'0'
	)
ELSE
	ifnull(a.W89, '0')
END txyj,


 CASE
WHEN a.indexcalno < '201711' THEN
	ifnull(
		(
			SELECT
				convert(sum(fyc),char)
			FROM
				table3
			WHERE
				agentcode = A.agentcode
			AND wageno = A.indexcalno
			AND riskcode IN (
				SELECT
					CODE
				FROM
					table4
				WHERE
					codetype = 'riskTypeProperty'
			)
			AND riskchnl = '12'
		),
		'0'
	)
ELSE
	ifnull(a.W90, '0')
END cxyj

FROM
	table5 A,
	table6 C
WHERE
	a.branchattr = c.branchattr
AND a.branchtype = c.branchtype
AND a.indexcalno = c.indexcalno
AND EXISTS (
	SELECT
		1
	FROM
		table7 q
	WHERE
		q.indexcalno = a.indexcalno
	AND q.agentgroup = a.agentgroup
)
AND a.state = '1'
AND a.agentcode = '100000302'
AND a.indexcalno = '201711'
AND a.state = '1'

tmp表的类型如下:

txyj是varbinary,而经过convert的cxyj是varchar.

其实前面在ifnull()函数后面加*1,和将'0'去掉单引号。最后经过create table测试,字段类型都是varchar类型。

总结:

应用从数据库中查询的结果需要字符类型的数据,如果是VARBINARY数据类型的数据,数据库查询会正常返回相应的字符结果,但java应用却无法转换这种字节串数据。


猜你喜欢

转载自blog.csdn.net/u012987186/article/details/80924666