使用Firebird中的附加文本从字符串列中提取整数值

时间:2017-09-05 13:08:48

标签: firebird firebird2.5

我将BDE查询(Paradox)转换为Firebird(2.5,而不是3.x),并且我有一个非常方便的转换:

select TRIM('      1') as order1, CAST('      1' AS INTEGER) AS order2    --> 1
select TRIM('  1 bis') as order1, CAST('  1 bis' AS INTEGER) AS order2    --> 1

然后按照铸造值进行排序,然后测试值(ORDER order2,order1)为我提供了我需要的结果:

1
1 bis
2 ter
100
101 bis

但是在Firebird CASTing中,一个不正确的整数会引发异常,我没有找到任何方法来提供相同的结果。我想我可以判断一个数字是否存在如下所示,但我无法找到提取它的方法...

TRIM('    1 bis') similar to '[ [:ALPHA:]]*[[:DIGIT:]]+[ [:ALPHA:]]*' 

[编辑]

我必须处理文本在数字之前的情况,所以使用@Arioch'触发器让我运行得很好:

SET TERM ^ ;
CREATE TRIGGER SET_MYTABLE_INTVALUE FOR MYTABLE ACTIVE
BEFORE UPDATE OR INSERT POSITION 0
AS 
DECLARE I INTEGER;
DECLARE S VARCHAR(13);
DECLARE C VARCHAR(1);
DECLARE R VARCHAR(13);
BEGIN 
  IF (NEW.INTVALUE is not null) THEN EXIT;
  S = TRIM( NEW.VALUE );
  R = NULL;
  I = 1;
  WHILE (I <= CHAR_LENGTH(S)) DO
  BEGIN
    C = SUBSTRING( S FROM I FOR 1 );
    IF ((C >= '0') AND (C <= '9')) THEN LEAVE;
    I = I + 1;
  END
  WHILE (I <= CHAR_LENGTH(S)) DO
  BEGIN
    C = SUBSTRING( S FROM I FOR 1 );
    IF (C < '0') THEN LEAVE;
    IF (C > '9') THEN LEAVE;
    IF (C IS NULL) THEN LEAVE;
    IF (R IS NULL) THEN R=C; ELSE R = R || C;
    I = I + 1; 
  END
  NEW.INTVALUE = CAST(R AS INTEGER);
END^
SET TERM ; ^

2 个答案:

答案 0 :(得分:2)

转换这样的表时,必须添加一个特殊的索引整数列来保存提取的整数数据。

注意,这个查询在使用“非常方便的转换”时实际上是相当糟糕的:你应该使用索引列来排序(排序)大量数据,否则你将进入缓慢执行并浪费大量内存/磁盘临时排序表。

因此,您必须添加一个额外的整数索引列,并在查询中使用它。

接下来的问题是如何填充该列。

当您将整个数据库和应用程序从BDE移动到Firebird时,最好是这样做一次。从那时起,在输入新数据行时,您的应用程序会正确地填充varcharinteger列。

转换器应用程序可以进行一次转换。 或者您可以使用可选择的Stored Procedure来重复使用此类和添加列的表格。或者你可以使Execute Block遍历表并更新计算所述整数值的行。

How to SELECT a PROCEDURE in Firebird 2.5

如果您需要保留遗留应用程序,只插入文本列而不是整数列,那么我认为您必须在Firebird中使用BEFORE UPDATE OR INSERT触发器,它将逐字母地解析文本列值并且从中提取整数。然后确保您的应用程序永远不会直接更改该整数列。

请参阅Trigger on Update Firebird

上的触发示例

PSQL语言文档:https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-psql.html

无论您是编写过程还是触发器来填充所述添加的整数索引列,您都必须对字符进行简单循环,将字符串从第一个数字复制到第一个非数字。

https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-functions-scalarfuncs.html#fblangref25-functions-string

https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-psql-coding.html#fblangref25-psql-declare-variable

像这样的东西

CREATE TRIGGER my_trigger FOR my_table 
BEFORE UPDATE OR INSERT
AS 
DECLARE I integer;
DECLARE S VARCHAR(100);
DECLARE C VARCHAR(100);
DECLARE R VARCHAR(100);
BEGIN 
  S = TRIM( NEW.MY_TXT_COLUMN );
  R = NULL;
  I = 1;
  WHILE (i <= CHAR_LENGTH(S)) DO
  BEGIN
    C = SUBSTRING( s FROM i FOR 1 );
    IF (C < '0') THEN LEAVE;
    IF (C > '9') THEN LEAVE;
    IF (C IS NULL) THEN LEAVE;

    IF (R IS NULL) THEN R=C; ELSE R = R || C;
    I = I + 1; 
  END

  NEW.MY_INT_COLUMN = CAST(R AS INTEGER);
END;

在此示例中,您的ORDER order2, order1将成为

SELECT ..... FROM my_table ORDER BY MY_INT_COLUMN, MY_TXT_COLUMN 

此外,您的列实际上似乎包含复合数据:整数索引和可选的文本后缀。如果是这样,那么您所拥有的数据就不会被标准化,并且可以更好地重组该表。

CREATE TABLE my_table (
  ORDER_Int INTEGER NOT NULL,
  ORDER_PostFix VARCHAR(24) CHECK( ORDER_PostFix = TRIM(ORDER_PostFix) ),

  ......

  ORDER_TXT COMPUTED BY (ORDER_INT || COALESCE( ' ' || ORDER_PostFix, '' )),
  PRIMARY KEY (ORDER_Int, ORDER_PostFix )
);

当您将数据从Paradox移动到Firebird时 - 让您的转换器应用程序检查并将这些值(如“1 bis”)拆分为两个新列。

然后你的查询就像

SELECT ORDER_TXT, ...  FROM my_table ORDER BY ORDER_Int, ORDER_PostFix 

答案 1 :(得分:1)

如果你正在使用fb2.5,你可以使用以下内容:

execute block (txt varchar(100) = :txt )
returns (res integer)
as
declare i integer;
begin
   i=1;
   while (i<=char_length(:txt)) do begin
    if (substring(:txt from i for 1) not similar to '[[:DIGIT:]]')
    then txt =replace(:txt,substring(:txt from i for 1),'');
    else i=i+1;
 end
res = :txt;
suspend;

end

在fb3.0中你有更方便的方法来做同样的事情

select
cast(substring(:txt||'#' similar '%#"[[:DIGIT:]]+#"%' escape '#') as integer)
from rdb$database