我将首先展示一个总体思路,如何解决这个问题,并提供一些难看但易于理解的代码。然后我会解释这些问题以及如何补救。
第1步:推导分组标准
对于第一步,我认为你有正确的(特权)在表中创建一个额外的列。让我们将其命名为invoice_text
。现在,总体思路是删除发票号码中的所有数字,以便只保留“文本模式”。然后我们可以按文字模式分组。
假设您已经创建柱上面提到的,你可以做到以下几点:
UPDATE Invoices SET invoice_text = REPLACE(invoice_number, '0', '');
UPDATE Invoices SET invoice_text = REPLACE(invoice_text, '1', '');
UPDATE Invoices SET invoice_text = REPLACE(invoice_text, '2', '');
...
UPDATE Invoices SET invoice_text = REPLACE(invoice_text, '9', '');
已经这样做了,你将有没有数字的纯文本模式在invoice_text
,可以利用它来进行分组后:
SELECT COUNT(invoice_number) AS total_invoices FROM Invoices
GROUP BY invoice_text
这很好,但它还没有你想要的。它不显示每个组的第一个和最后一个发票号码。
步骤2:导出第一和最后一个发票每组
对于这个步骤,创建您的表中多了一个列。我们将其命名为invoice_digits
。正如其名称所暗示的那样,它只是为了在没有“模式文本”的情况下仅使用纯粹的发票号码。
假设你有一个栏,你可以做到以下几点:
UPDATE Invoices SET invoice_digits = REPLACE(invoice_number, 'A', '');
UPDATE Invoices SET invoice_digits = REPLACE(invoice_digits, 'B', '');
UPDATE Invoices SET invoice_digits = REPLACE(invoice_digits, 'C', '');
...
UPDATE Invoices SET invoice_digits = REPLACE(invoice_digits, 'Z', '');
现在,您可以使用该列,以获得最小和最大发票号码(不带“模式文本”):
SELECT
MIN(invoice_digits) AS from_invoice_no,
MAX(invoice_digits) AS to_invoice_no,
COUNT(invoice_number) AS total_invoices
FROM Invoices
GROUP BY invoice_text
问题以及如何解决这些问题
1)根据你r问题,您想获得最小和最大完整发票号码文本。上面的解决方案将只显示最小和最大发票号码文本,但不包含文本部分,即仅限数字。
我们可以通过做进一步的JOIN
来弥补这一点,但是因为我可以很好地想象你不会坚持这样做:-),并且由于它不会使总体思路更清晰,所以我将离开这个给你。如果您有兴趣,请告诉我们。
2)可能很难决定什么数字(即什么实际发票数)是。例如,如果您有发票号码,例如INV001
,INV002
,这将是没有问题的,但是如果您有INV001/001
,INV001/002
,INV002/003
等等呢?在这个例子中,我的代码将产生001001
,001002
,002003
作为实际的发票号码,并用它来决定最小和最大数字是什么。
这可能不是你想要做的那种情况。解决这个问题的唯一方法就是彻底思考你应该考虑一个数字和什么不是,并相应地调整我的代码。
3)我的代码当前使用字符串比较来获取最小和最大发票号。这可能会产生其他结果,而不是将数值与数字进行比较如果您想知道这是什么意思:将'19'
与'9'
作为字符串进行比较,然后将19
与9
作为数字进行比较。
如果这是一个问题,然后使用MySQL的CAST
将其送入MAX
或MIN
之前,文本将数字转换。但请注意,这有其自己的注意事项:
如果您的发票号码很长,且数字太大以至于它们不适合MySQL的数字数据类型,则此方法将失败。如果您将/
这样的字符定义为数字(由于2中描述的问题),它也会失败,因为MySQL无法将其转换为数字。
除了转换为数字之外,还可以使用前导零来填充invoice_digits
中的值,例如使用MySQL的LPAD
函数。这将避免上述问题并按预期对数字进行排序,即使它们包含像/
这样的非数字,但您必须事先知道数字串的最大长度。
4)代码很丑!你真的必须从A
到Z
中删除所有可能的字符吗?通过UPDATE
语句来获取数字字符串?
实际上,情况更糟。我只是假设您的发票中只有“文字字符”A
到Z
。但是可以有Unicode定义的任何字符:俄语或中文字符,换句话说,特殊字符:数以千计的不同字符。
不幸的是,AFAIK,MySQL仍然没有提供REGEX-REPLACE功能。我没有看到任何机会解决这个问题,除非你用合适的UDF(用户定义函数)扩展MySQL。有一些很酷的家伙认识到这个问题,并将这些功能添加到MySQL中。由于推荐库似乎不鼓励在SO上,只是谷歌的“MySQL正则表达式替换”。
当以这种方式扩展MySQL时,您可以用一个单一的替换从发票号码中删除数字/文本的丑陋的UPDATE
语句(使用REGEX,可以替换所有数字或全部非数字立刻)。
为了完整起见,您可以通过执行UPDATE ... SET ... = REPLACE(REPLACE(REPLACE(...)))
来避免多个UPDATE
语句,并因此应用具有一个语句的所有更新。但是这更加丑陋,容易出错,所以如果你对你的问题非常认真,你真的必须通过REGEX-REPLACE来扩展MySQL。
5)该解决方案只有在您有权在表中创建新列时才有效。
对于解决方案,这是正确的。但我选择这样做是因为它使总体思路清晰易懂。除了将列添加到原始表格之外,您还可以创建一个新表格来存储纯文本/数字(此表格可能是临时表格)。
此外,由于MySQL支持按计算值进行分组,因此根本不需要额外的列/表。你应该自己决定什么是最好的方法。
发票号码中是否总是有两个部分,一部分是连续的数字,另一部分是连续的非数字,或者发票号码可能类似于“001INV003FOO1234BAR”? – Binarus
是的,就像'PRCMMU1718/00057','PRCMMU1718/00058','AQW1025','AQW1028','AQW1030','1258POC','1259POC' ... – pmenezes
您是否允许(即您有权限)为该表添加两个额外的列,或创建一个新表? – Binarus