2017-10-11 47 views
0

首先,我希望这是纯粹用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 

发票号码模式。他们可以在将来改变

请帮我实现这一点。

在此先感谢。

+0

发票号码中是否总是有两个部分,一部分是连续的数字,另一部分是连续的非数字,或者发票号码可能类似于“001INV003FOO1234BAR”? – Binarus

+0

是的,就像'PRCMMU1718/00057','PRCMMU1718/00058','AQW1025','AQW1028','AQW1030','1258POC','1259POC' ... – pmenezes

+0

您是否允许(即您有权限)为该表添加两个额外的列,或创建一个新表? – Binarus

回答

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

现在,您可以使用该列,以获得最小和最大发票号码(不带“模式文本”):

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'作为字符串进行比较,然后将199作为数字进行比较。

如果这是一个问题,然后使用MySQL的CAST将其送入MAXMIN之前,文本将数字转换。但请注意,这有其自己的注意事项:

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

除了转换为数字之外,还可以使用前导零来填充invoice_digits中的值,例如使用MySQL的LPAD函数。这将避免上述问题并按预期对数字进行排序,即使它们包含像/这样的非数字,但您必须事先知道数字串的最大长度。

4)代码很丑!你真的必须从AZ中删除所有可能的字符吗?通过UPDATE语句来获取数字字符串?

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

不幸的是,AFAIK,MySQL仍然没有提供REGEX-REPLACE功能。我没有看到任何机会解决这个问题,除非你用合适的UDF(用户定义函数)扩展MySQL。有一些很酷的家伙认识到这个问题,并将这些功能添加到MySQL中。由于推荐库似乎不鼓励在SO上,只是谷歌的“MySQL正则表达式替换”。

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

为了完整起见,您可以通过执行UPDATE ... SET ... = REPLACE(REPLACE(REPLACE(...)))来避免多个UPDATE语句,并因此应用具有一个语句的所有更新。但是这更加丑陋,容易出错,所以如果你对你的问题非常认真,你真的必须通过REGEX-REPLACE来扩展MySQL。

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

对于解决方案,这是正确的。但我选择这样做是因为它使总体思路清晰易懂。除了将列添加到原始表格之外,您还可以创建一个新表格来存储纯文本/数字(此表格可能是临时表格)。

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

+0

正如你在第2点中提到的那样,这正是我的情况。前缀和后缀可以是字母数字,后跟增量数字。发票模式未在系统中定义。我将不得不寻找其他方式来做到这一点,可能是通过PHP – pmenezes

+0

只要没有规则属于该模式以及实际数字是什么,您就没有机会通过软件解决此问题。如果我的评论是正确的,即使是数字也可能是模式的一部分。因此,例如,如果你有'INV001'和'INV002',那么你甚至不能分辨出模式是'INV00',实际数字是'1'和'2',或者模式是'INV0 '和实际数字是'01'和'02',或者如果模式是'INV'并且实际数字是'001'和'002'。 – Binarus