1. 程式人生 > >MySQL語句中有IFNULL , 查詢結果返回是亂碼 [[email protected]類的結果

MySQL語句中有IFNULL , 查詢結果返回是亂碼 [[email protected]類的結果

問題描述:根據開發反饋由於應用查詢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.測試確實可以成功。

那麼加*1也就是將ifnull的結果轉換成唄。那麼我將ifnull的後面'0'去掉單引號是否也是可以的,測試也是成功的。那到底是什麼原因導致這種字元型別轉換呢?是sum()求和導致的,還是ifnull()函式導致的?

查詢官方文件發現了主要問題所在:

1.先看是不是sum()的問題所在

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的解釋:

有段如下內容:


什麼是VARBINARY?

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應用卻無法轉換這種位元組串資料。