我有大量的 MariaDB 10.6 表,其中有几个
decimal(P,D)
列和数百万行。这些列的精度(P
)和规模(D
)曾经被设置为不切实际的高水平,只是容纳每个可能的输入,例如decimal(24,10)
。
现在我想根据这些表中的实际数据确定所需的
P
和 D
值。
我能想到的最好的办法是
SELECT CEIL(LOG10(MAX(col))) AS maxI,
MAX(CEIL(LOG10(CAST(REVERSE(SUBSTR(CAST(col % 1 AS VARCHAR(12)), 3)) AS INT)))) AS maxD
FROM tbl;
其中
col
是数据类型为 decimal(24,10)
的列,maxI
是最大整数部分,maxP
是小数点后有效位数的最大数量(因此 maxP = maxI + maxD
)。
虽然它确实有效,但速度慢得不足为奇,我想知道是否有更有效的方法来实现这一目标。
处理精度数字时不要使用双精度函数(如 LOG10),以避免警告和舍入错误。
计算小数点前的最大位数非常简单,因为这可以由优化器处理而无需触及索引。对于无符号小数,您可以使用
MAX()
,对于有符号小数,您必须使用 MIN()
和 MAX()
:
select @maxI1:=LENGTH(TRUNCATE(MIN(col1),0)), @maxI2:=LENGTH(TRUNCATE(MAX(col1),0)) FROM tbl;
小数位数最多的有记录
MAX(col1 - TRUNCATE(col1,0))
。
所以第二条语句是:
SELECT IF(@maxI1 > @maxI2, @maxI1, @maxI2) as maxI,
LENGTH(MAX(col1 - TRUNCATE(col1, 0))) -2 as maxD
FROM tbl;
速度对比(7 Mio 行):
SELECT CEIL(LOG10(MAX(col1))) AS maxI, MAX(CEIL(LOG10(CAST(REVERSE(SUBSTR(CAST(col1 % 1 AS VARCHAR(12)), 3)) AS INT)))) AS maxD FROM tbl;
+------+------+
| maxI | maxD |
+------+------+
| 13 | 10 |
+------+------+
1 row in set, 40964 warnings (6.166 sec)
与
SELECT @maxI1:=LENGTH(TRUNCATE(MIN(col1),0)) as maxI1, @maxI2:=LENGTH(TRUNCATE(MAX(col1),0)) as maxI2 FROM tbl;
+-------+-------+
| maxI1 | maxI2 |
+-------+-------+
| 5 | 13 |
+-------+-------+
1 row in set (0.000 sec)
SELECT IF(@maxI1 > @maxI2, LENGTH(@maxI1), LENGTH(@maxI2)) as maxI, LENGTH(MAX(col1 - truncate(col1, 0))) -2 as maxD FROM tbl;
+------+------+
| maxI | maxD |
+------+------+
| 13 | 10 |
+------+------+
1 row in set (4.168 sec)