首先,我想指出我几乎尝试过所有事情。我从最近8个小时开始尝试按顺序列出我的列表,并且我已经应用了这里找到的十几个解决方案。
以下是SQL Fiddle的示例数据。我发现a page能够按正确的顺序对我的列表进行排序,即:
1
2
2.B3
5
9
10 A-1
10 A-3
10 B-4
10 B-5
11
12
B3-43
B3-44
B3 - 48
B3 - 49
Basztowa 3
Basztowa 4
Basztowa 5
Basztowa 7
Basztowa 9
D.1
D.2
D.10
D.11
D.12
Kabaty ul. Pod lipą 4
但我无法使用MySQL重现这一点。
我会感激任何帮助,因为我没有更多的想法。我考虑使用PHP来对我的列表进行排序,但据我所知,DBMS针对这个操作孩子进行了优化,所以如果可能的话我想避免使用PHP来实现这一点。
@UPDATE
感谢@Jakumi我创建了两个帮助我解决问题的功能。
您需要创建一个列,以便以排序友好格式存储您的值( zeropadded_name ),在更新时创建触发器,并在名称时插入以填充 zeropadded_name 改变就是这样!现在只需通过 zeropadded_name 订购并享受!
帮助程序功能
regex_replace
- 它的任务是通过删除所有非字母数字字符来帮助我们消毒价值。lpad_numbers
- 填充字符串中的每个数字。这有点难看,因为我不太了解MySQL的功能,但嘿,它的工作原理非常快。示例:
SELECT lpad_numbers(regex_replace('[^a-zA-Z0-9]', ' ', 'B3 - A-5'));
#B0003A0005
DROP FUNCTION IF EXISTS regex_replace;
CREATE FUNCTION `regex_replace`(
pattern VARCHAR(1000)
CHARSET utf8
COLLATE utf8_polish_ci,
replacement VARCHAR(1000)
CHARSET utf8
COLLATE utf8_polish_ci,
original VARCHAR(1000)
CHARSET utf8
COLLATE utf8_polish_ci
) RETURNS varchar(1000) CHARSET utf8
DETERMINISTIC
BEGIN
DECLARE temp VARCHAR(1000)
CHARSET utf8
COLLATE utf8_polish_ci;
DECLARE ch VARCHAR(1)
CHARSET utf8
COLLATE utf8_polish_ci;
DECLARE i INT;
SET i = 1;
SET temp = '';
IF original REGEXP pattern
THEN
loop_label: LOOP
IF i > CHAR_LENGTH(original)
THEN
LEAVE loop_label;
END IF;
SET ch = SUBSTRING(original, i, 1);
IF NOT ch REGEXP pattern
THEN
SET temp = CONCAT(temp, ch);
ELSE
SET temp = CONCAT(temp, replacement);
END IF;
SET i = i + 1;
END LOOP;
ELSE
SET temp = original;
END IF;
RETURN temp;
END;
DROP FUNCTION IF EXISTS lpad_numbers;
CREATE FUNCTION `lpad_numbers`(str VARCHAR(256)) RETURNS varchar(256) CHARSET utf8 COLLATE utf8_polish_ci
BEGIN
DECLARE i, len SMALLINT DEFAULT 1;
DECLARE ret VARCHAR(256) DEFAULT '';
DECLARE num VARCHAR(256) DEFAULT '';
DECLARE c CHAR(1);
IF str IS NULL
THEN
RETURN "";
END IF;
SET len = CHAR_LENGTH(str);
REPEAT
BEGIN
SET c = MID(str, i, 1);
IF c BETWEEN '0' AND '9'
THEN
SET num = c;
SET i = i + 1;
REPEAT
BEGIN
SET c = MID(str, i, 1);
SET num = CONCAT(num, c);
SET i = i + 1;
END;
UNTIL c NOT BETWEEN '0' AND '9' END REPEAT;
SET ret = CONCAT(ret, LPAD(num, 4, '0'));
ELSE
SET ret = CONCAT(ret, c);
SET i = i + 1;
END IF;
END;
UNTIL i > len END REPEAT;
RETURN ret;
END;
答案 0 :(得分:2)
从技术上讲,mysql排序机制可以正常工作,但是你的字符串是以错误的方式格式化的。您的数据的基础结构类似于以下内容(为了便于与示例关联而保留Original
列):
alpha1 num1 alpha2 num2 ... Original
1 1
2 2
2 B 3 2.B3
5 5
9 9
10 A 1 10 A-1
10 A 3 10 A-3
10 B 4 10 B-4
10 B 5 10 B-5
11 11
12 12
B 3 43 B3-43
B 3 44 B3-44
B 3 48 B3 - 48
B 3 49 B3 - 49
Basztowa 3 Basztowa 3
Basztowa 4 Basztowa 4
Basztowa 5 Basztowa 5
Basztowa 7 Basztowa 7
Basztowa 9 Basztowa 9
D 1 D.1
D 2 D.2
D 10 D.10
D 11 D.11
D 12 D.12
如果您现在使用ORDER BY alpha1, num1, alpha2, num2
对它们进行排序,它们将按您的需要进行排序。但是已经“格式化”的版本(Original
列)无法轻易排序,因为应按字母顺序排序的部分和应按数字排序的部分混合在一起。
有一个不那么广泛的替代方案只需要一个额外的列,你假设没有任何数字超过让我们说10000并且你现在可以用零填充版本替换每个数字(不是数字!),所以{{1} } 10 A-1
(显然是0010A0001
和0010
以及A
,但我不认为这是在{{{} { 1}}陈述。
但是对于这个例子,zeropadded版本(假设:每个数字<10000):
0001
这可以通过ORDER BY
对您的愿望进行排序。
所以最后,您可能需要在php中排序或创建更多列,以帮助您通过重新格式化/清理/拆分输入进行排序。
<强>更新强>
zeropadding背后的主要思想是数字的自然格式与计算机中的格式不同。在计算机中,数字 2实际上是数字序列 0..0002(所以包括前导零)类似10(0..0010)。当计算机比较数字时,它将从左到右,直到找到不同的数字:
Original Zeropadded
1 0001
2 0002
2.B3 0002B0003
5 0005
9 0009
10 A-1 0010A0001
10 A-3 0010A0003
10 B-4 0010B0004
10 B-5 0010B0005
11 0011
12 0012
B3-43 B00030043
B3-44 B00030043
B3 - 48 B00030048
B3 - 49 B00030049
Basztowa 3 Baztowa0003
Basztowa 4 Baztowa0004
Basztowa 5 Baztowa0005
Basztowa 7 Baztowa0007
Basztowa 9 Baztowa0009
D.1 D0001
D.2 D0002
D.10 D0010
D.11 D0011
D.12 D0012
然后它将确定哪个数字更大或更小。在这种情况下,0 < 1,因此2&lt; 10.(当然计算机使用二进制,但这不会改变这个想法。)
现在,字符串在技术上是字符序列。字符串比较工作略有不同。当比较两个字符串时,它们不会(左)填充,因此每个字符串的第一个字符实际上是第一个字符而不是填充(例如空格)。从技术上讲,字符串 ORDER BY zeropadded
是一系列字符0...0002
0...0010
======!. (the ! marks the point where the first digit is different)
,A10
和A
。并且由于使用了字符串比较,它比1
“更小”,因为字符串比较不会将数字看作数字而是作为字符(即数字):
0
因为A2
&lt; A10
A2
=! (the ! marks the point where the first character is different)
为字符,1
&lt; 2
。现在为了避免这个问题,我们强制字符串中的数字格式与数字比较中的数字格式相同,方法是将数字填充到相同的长度,根据place value对齐数字:< / p>
A10
现在,它实际上与您在数值比较中所期望的相同。但是,您必须对数字的最大长度做出一些假设,以便您可以适当地选择填充。没有这个假设,你就会遇到问题。
剩下的唯一(逻辑)点:当比较的字符串具有字母字符,而另一个字符串具有数字时,填充变化是什么?答案是:没什么。我们不会将数字更改为字母,并且数字小于字母,因此在这种情况下所有内容都保持相同的顺序。
zeropadding的效果是:我们通过根据数字字符对齐数字字符来调整字符串中的“数字”比较,使其与实数比较相似。
答案 1 :(得分:-2)
SELECT name FROM realestate ORDER BY name ASC;
这应该用字母数字数据对您的列表进行排序......我没有看到问题。
编辑:好的,我还是不知道我是否真的明白这个问题的目标是什么(比赛是针对什么?),但是我可以提交这个“扭曲”的查询(我希望我永远不会使用)在我的职业生涯中):SELECT name FROM realestate
ORDER BY IF(SUBSTRING(name, 1, 2) REGEXP '[A-Z]', 100000, CAST(name AS UNSIGNED)) ASC,
SUBSTRING(name, 1, 2) ASC,
CAST(SUBSTRING(name FROM LOCATE('.', name)+1) AS UNSIGNED) ASC,
REPLACE(name, ' ', '') ASC;
也许有人可以找到一种更简单的方法,因为我承认我的答案有点复杂。但是,Kamil和Jakumi解决方案更加棘手和复杂。