从带有附加文本的字符串列中提取整数值
Extract integer value from string column with additional text
我正在将 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 中,转换不正确的整数会引发异常,我没有找到任何方法来提供相同的结果。我想我可以通过下面的内容判断数字是否存在,但我找不到提取它的方法。
TRIM(' 1 bis') similar to '[ [:ALPHA:]]*[[:DIGIT:]]+[ [:ALPHA:]]*'
[编辑]
我不得不处理文本在数字之前的情况,所以使用@Arioch'The 的触发器,我得到了这个 运行 很棒:
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 ; ^
转换这样的 table,您必须添加一个特殊的索引整数列来保存提取的整数数据。
注意,这个使用 "very convenient conversion" 的查询实际上是相当糟糕的:你应该使用索引列来排序(排序)大量数据,否则你将执行缓慢并浪费很多 memory/disk临时排序tables.
因此您必须添加一个额外的整数索引列并在查询中使用它。
下一个问题是如何填充该列。
当您将整个数据库和应用程序从 BDE 移动到 Firebird 时,最好只执行一次。从那时起,让您的应用程序在输入新数据行时正确填充 varchar
和 integer
列。
然后,您的转换器应用程序可以完成一次转换。
或者你可以使用 selectable Stored Procedure
来重复 table 这样的和添加的列。或者您可以使 Execute Block
遍历 table 并更新其行以计算所述整数值。
How to SELECT a PROCEDURE in Firebird 2.5
如果您需要保留遗留应用程序,即只插入文本列而不插入整数列,那么我认为您必须在 Firebird 中使用 BEFORE UPDATE OR INSERT
触发器,它将解析文本列值字母字母并从中提取整数。然后确保您的应用程序永远不会直接更改该整数列。
请参阅
中的触发器示例
无论您是编写过程还是触发器来填充上述添加的整数索引列,您都必须对字符进行简单循环,将字符串从第一个数字复制到第一个非数字。
类似的东西
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
此外,您的列似乎实际上包含一个复合数据:一个整数索引和一个可选的文本后缀。如果是这样,那么您拥有的数据未标准化,table 最好进行重组。
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
如果您使用的是 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
--assuming that the field is varchar(15))
select cast(field as integer) from table;
在 firebird 2.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 中,转换不正确的整数会引发异常,我没有找到任何方法来提供相同的结果。我想我可以通过下面的内容判断数字是否存在,但我找不到提取它的方法。
TRIM(' 1 bis') similar to '[ [:ALPHA:]]*[[:DIGIT:]]+[ [:ALPHA:]]*'
[编辑]
我不得不处理文本在数字之前的情况,所以使用@Arioch'The 的触发器,我得到了这个 运行 很棒:
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 ; ^
转换这样的 table,您必须添加一个特殊的索引整数列来保存提取的整数数据。
注意,这个使用 "very convenient conversion" 的查询实际上是相当糟糕的:你应该使用索引列来排序(排序)大量数据,否则你将执行缓慢并浪费很多 memory/disk临时排序tables.
因此您必须添加一个额外的整数索引列并在查询中使用它。
下一个问题是如何填充该列。
当您将整个数据库和应用程序从 BDE 移动到 Firebird 时,最好只执行一次。从那时起,让您的应用程序在输入新数据行时正确填充 varchar
和 integer
列。
然后,您的转换器应用程序可以完成一次转换。
或者你可以使用 selectable Stored Procedure
来重复 table 这样的和添加的列。或者您可以使 Execute Block
遍历 table 并更新其行以计算所述整数值。
How to SELECT a PROCEDURE in Firebird 2.5
如果您需要保留遗留应用程序,即只插入文本列而不插入整数列,那么我认为您必须在 Firebird 中使用 BEFORE UPDATE OR INSERT
触发器,它将解析文本列值字母字母并从中提取整数。然后确保您的应用程序永远不会直接更改该整数列。
请参阅
无论您是编写过程还是触发器来填充上述添加的整数索引列,您都必须对字符进行简单循环,将字符串从第一个数字复制到第一个非数字。
类似的东西
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
此外,您的列似乎实际上包含一个复合数据:一个整数索引和一个可选的文本后缀。如果是这样,那么您拥有的数据未标准化,table 最好进行重组。
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
如果您使用的是 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
--assuming that the field is varchar(15))
select cast(field as integer) from table;
在 firebird 2.5 版中工作。