MySQL发票号码按计数范围排列

时间:2017-10-11 07:46:09

标签: mysql

首先,我希望这完全是用MySQL查询完成的。

我有一系列发票号码

invoice_number
INV001
INV002
INV003
INV004
INV005
001
002
003
006
007
009
010
INVOICE333
INVOICE334
INVOICE335
INVOICE337
INVOICE338
INVOICE339
001INV
002INV
005INV
009INV

我想输出类似的东西

from_invoice_no    to_invoice_no    total_invoices
INV001             INV005           5
001                010              7
INVOICE333         INVOICE339       6
001INV             009INV           4

无法修复发票编号图案。他们将来可以改变

请帮助我实现这一目标。

提前致谢。

1 个答案:

答案 0 :(得分:0)

我将首先展示如何解决此问题并提供一些丑陋但易于理解的代码。然后,我将解释问题是什么以及如何纠正它们。

第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', '');

现在,您可以使用该列获取最小和最大发票号(不包含#34;模式文本"):

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)根据您的问题,您希望获得最小和最大完整发票号码文本。上述解决方案仅显示没有文本部分的最小和最大发票编号文本,即仅显示数字。

我们可以通过进一步JOIN来解决这个问题,但是因为我很好地想象你不会坚持这个:-),因为它不会成为一般的想法更清楚,我把这个留给你。如果您有兴趣,请告诉我们。

2)可能很难确定一个数字(即实际发票数字)是什么。例如,如果您有INV001INV002等发票号码,这将没有问题,但如果您有INV001/001INV001/002INV002/003和等等?在此示例中,我的代码会将001001001002002003作为实际发票编号,并使用它来确定最小和最大数字。

在这种情况下,这可能不是您想要做的。解决这个问题的唯一方法就是你要彻底考虑你应该考虑什么,不应该考虑什么,以及相应地调整我的代码。

3)我的代码目前使用字符串比较来获取最小和最大发票号。除了将值作为数字进行比较之外,这可能产生其他结果如果您想知道这意味着什么:将'19''9'比较为字符串,并将199作为数字进行比较。

如果这是一个问题,请使用MySQL CAST将文本转换为数字,然后再将其转移到MAXMIN。但请注意,这有其自己的警告:

如果您的发票号很长且数字太多,以至于它们不适合MySQL的数字数据类型,则此方法将失败。如果您将/之类的字符定义为数字(由于2中描述的问题),它也将失败,因为MySQL无法将其转换为数字。

您也可以使用MySQL invoice_digits函数填充LPAD中带有前导零的值,而不是转换为数字。这样可以避免上述问题并按预期对数字进行排序,即使它们包含非数字/,但您必须事先知道数字字符串的最大长度。

4)代码很难看!您是否真的需要通过执行A语句逐个删除ZUPDATE中所有可能的字符来获取数字字符串?

实际上,情况更糟。我只是假设你只有#34;文本字符"发票中AZ。但是Unicode可以定义任何字符:俄语或中文字符,特殊字符,换句话说:数千个不同的字符。

不幸的是,AFAIK,MySQL仍然没有提供REGEX-REPLACE功能。除非您使用适当的UDF(用户定义的函数)扩展MySQL,否则我没有看到任何机会解决此问题。有一些很酷的人已经认识到这个问题,并已将这些功能添加到MySQL。由于推荐图书馆似乎不鼓励SO,只需google for" mysql regex replace"。

当以这种方式扩展MySQL时,您可以替换丑陋的UPDATE语句,这些语句将发票号中的数字/文本删除一个(使用REGEX,您可以替换所有数字或全部一次非数字。)

为了完整起见,您可以通过执行UPDATE来避免许多UPDATE ... SET ... = REPLACE(REPLACE(REPLACE(...)))语句,从而将所有更新应用于一个语句。但这更加丑陋且容易出错,所以如果你认真对待你的问题,那么你真的必须通过REGEX-REPLACE扩展MySQL。

5)只有您有权在表格中创建新列时,该解决方案才有效。

对于解决方案,这是正确的。但我选择这样做只是因为它使一般的想法清晰易懂。您也可以创建一个新表来存储纯文本/数字(此表可能是临时表),而不是将列添加到原始表中。

此外,由于MySQL支持按计算值进行分组,因此您根本不需要额外的列/表。你应该自己决定最好的方法。