在PHP或MySQL中排序十进制数字

3

我正在开发一款账簿应用程序。我的主要问题是我的客户有一个类似于这样的会计科目代码:

1.1.1, 
1.1.2 
...... 
1.1.10, 
1.1.11,
.........

使用 PHP 或 MySQL,我只能将它们排序到

1.1.1, 
1.1.10, 
1.1.11,
1.1.2, 
.......

任何关于如何排序,使得1.1.10在1.1.9之后的帮助都将不胜感激。提前致谢。

你有多少条记录?(我很好奇你是否能将它们全部存储在内存中以便对它们进行排序) - Seth McClaine
目前,我正在使用MySQL查询中的order by。每个记录有8个字段,总共不到400条记录。但是由于1.1.10在1.1.2之前出现,所以COA的顺序很糟糕。 - Daniel Adinugroho
6个回答

3

这不太美观,但它可以工作:

ORDER
   BY SUBSTRING_INDEX(CONCAT( col ,'.'),'.',1) + 0
    , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',2),'.',-1) + 0
    , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',3),'.',-1) + 0
    , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',4),'.',-1) + 0
    , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',5),'.',-1) + 0
    , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',6),'.',-1) + 0
    , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',7),'.',-1) + 0

为了测试这些表达式,你可以在SELECT语句中使用它们,并验证它们提取了正确的组件,并且它们被正确排序:

SELECT col
     , SUBSTRING_INDEX(CONCAT( col ,'.'),'.',1) + 0 AS p1
     , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',2),'.',-1) + 0 AS p2
     , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',3),'.',-1) + 0 AS p3
     , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',4),'.',-1) + 0 AS p4
     , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',5),'.',-1) + 0 AS p5
     , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',6),'.',-1) + 0 AS p6
     , SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT( col ,'.'),'.',7),'.',-1) + 0 AS p7
  FROM mytable 
 ORDER BY 2,3,4,5,6,7,8

不必解释其原理,我将着重介绍一些重要的“技巧”

  • 在列末尾添加一个“.”,这样可以避免多次返回最后一个位置

  • 使用SUBSTRING_INDEX来检索直到第n个“.”的部分

  • 使用SUBSTRING_INDEX来检索该部分的尾随部分(从后往前读,到达首个点)

  • 添加零,将字符串转换为数字值


抱歉提出一个旧的答案,但我想知道为什么你要将十进制数CONCATcol的末尾。内部的SUBSTRING_INDEX将获取到第X个小数点之前(不包括该小数点)的子字符串,而外部的SUBSTRING_INDEX将返回从右边开始计数的第一个小数点右侧(不包括该小数点)的所有内容。这应该只会给你一个数字值。所以我不明白在连接小数时的意义所在。 - Cave Johnson
我添加了一个尾随的点字符,因为SUBSTRING_INDEX有些古怪。举个例子, SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('1.2','.',4),'.',-1) 返回值为 2。(将 4 替换为任意大于等于 2 的整数,结果也是 2)。如果我们连接一个尾随的点字符 1.2.,那么我们将得到一个空字符串。我考虑按照不同数量点排序的情况,并希望 1.3 排在 1.3.1 之前。如果 1.3 没有额外的点,则 ORDER BY 表达式将评估为“1”,“3”,“3”,“3”,“3” ... 这将导致 1.31.3.1 之后排序。 - spencer7593
通过添加一个尾随的点字符,所以我们操作'1.3.',order by表达式返回'1','3','0','0','0'。这就是我想要的:当这些组件缺失时,返回p3、p4、p5等零。这可能并不是严格必要的。但在一般情况下...当我们想要排序'3'和'3.1.4.2'时...我希望将3处理为3.0.0.0。通过运行SELECT来查看它的实际效果。(我的解决方案更通用,适用于不仅仅是OP案例中的示例,具有完全相同数量的点。OP在另一个答案的后续评论中有更多的点。) - spencer7593
啊,好的,谢谢你的解释。这很有道理。我刚刚才注意到你已经在你的回答中解释过了。抱歉让你再次解释。 - Cave Johnson
@KodosJohnson:没问题;答案只是简单地提到了添加额外的点字符,没有解释为什么要这样做。对于某些用例,例如列中的每个值都具有相同数量的点,添加点不会产生任何区别。它只会在排序具有不同点数的值时产生差异。 - spencer7593

2

将值分配给数组并使用natsort()自然地排序。

$foo = array ('1.1.1', '1.1.2', '1.1.10', '1.1.11');
natsort ($foo);
print_r ($foo);

/*
Array
(
    [0] => 1.1.1
    [1] => 1.1.2
    [2] => 1.1.10
    [3] => 1.1.11
)
*/

?>


2

你需要提取小数点之间的数字并将它们视为数字。使用SUBSTRING_INDEX函数提取数字,再使用CAST函数将其转换为数字类型:

SELECT *
FROM myAccounts
ORDER BY
  CAST(SUBSTRING_INDEX(account_number, '.', 1) AS UNSIGNED),
  CAST(SUBSTRING_INDEX(account_number, '.', -2) AS UNSIGNED),
  CAST(SUBSTRING_INDEX(account_number, '.', -1) AS UNSIGNED)

2
SELECT col FROM mytable ORDER BY REPLACE(col, ".", "")+0

REPLACE 去除 . 并且 MySQL 会自动使用数学运算转换字符串。您可以尝试使用 REPLACE(col, ".", "")*1


0
如果排序字段是name,则使用以下的ORDER BY子句。
ORDER BY length(name), name

为了让事情变得更加复杂,它也可以像这样,2.5.1.1、2.5.2.1......2.5.10.1、2.5.10.2、2.5.11.1......人类在排序方面没有问题,但是应用程序呢?我们能做到吗?我的朋友建议增加一个名为“顺序”的字段来定义账户的顺序。但是维护这个顺序将会非常麻烦...... - Daniel Adinugroho
那么您最好的选择是引入一个排序字段。 - DevZer0

0

我曾经遇到过类似的问题,我是这样解决的:

SELECT .... , CAST(SUBSTRING([column_name],1) AS UNSIGNED) AS myNum,  CAST(SUBSTRING([column_name],3) AS UNSIGNED) AS myDec
FROM ...
WHERE ...
ORDER BY myNum, myDec

在你的情况下,也许你需要更深入的了解。

希望这能帮助你获得一些见解。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接