1

我在 IBM Informix 数据库中有一个表,其中有一个数据类型为 CHAR(15) 的列“级别”。如果我对该列执行 SELECT DISTINCT,前 5 个结果是:

  • 未知
  • 菜鸟
  • 1级
  • 2A级
  • 2B级

我的意图是编写一个查询,该查询将按该列中数字的升序对结果进行排序。我已经通过 VB.NET 代码实现了它,但想知道我是否可以在查询中实现它。

' Results is a generic list of a class with properties corresponding to column names
' I am using IDataReader to go through the queried rows and load the data to 'results'
results = results.OrderBy(Of Integer)(Function(p) Utilities.ExtractNumber(p.Level))

这就是 ExtractNumber 方法的样子:

Public Shared Function ExtractNumber(ByVal expr As String) As Integer
    Dim number As Integer = 0
    Dim character As Char
    Dim startPos As Integer = -1
    Dim endPos As Integer = -1

    For pos = 0 to expr.Length - 1
        character = expr(pos)

        If Char.IsDigit(character) And startPos = -1 Then
            startPos = pos
        Else If Not Char.IsDigit(character) And startPos > -1 Then
            endPos = pos
            Integer.TryParse(expr.Substring(startPos, endPos - startPos), number)

            Exit For
        End If
    Next

    'Number extends till end of string
    If startPos > -1 And endPos = -1 Then
        Integer.TryParse(expr.Substring(startPos), number)
    EndIf
End Function

我的代码所做的是它为该列中的每个值查找字符串中第一次出现的数字。如果字符串中有多个数字(例如 ALPHA 1C 211",它将返回 1,这是第一个数字。如果不存在数字,例如在“未知”中,它将只返回 0。

我上面所做的可以使用 Regex.Split 轻松完成,但我没有使用它,因为它返回一个字符串数组,其中包含数字前的空元素。

有没有办法可以在 SQL 查询中提取这个数字?也许使用某种字符串操作来摆脱除第一个数字之外的所有内容?不过,我不允许编写函数,所以如果可能的话,我必须在一个查询中完成所有这些操作。任何指针?

4

2 回答 2

1

您的级别是否仅限于一个数字或至少一个已知的集合?如果是,case 语句可能会起作用。根据您对有效值的了解,您必须使用匹配模式以获得“ALPHA 2C 211”的正确结果。这在具有大约 89K 行的表中的 varchar(256) 字段上相当快。不确定大约 220 万:

SELECT
   CASE
   WHEN pr_last_name matches("*1*")
      THEN 1
   WHEN pr_last_name matches("*2*")
      THEN 2
   WHEN pr_last_name matches("*3*")
      THEN 3
   WHEN pr_last_name matches("*4*")
      THEN 3
   WHEN pr_last_name matches("*5*")
      THEN 4
   WHEN pr_last_name matches("*5*")
      THEN 5
   WHEN pr_last_name matches("*6*")
      THEN 6
   ELSE
      0
   END,
count(*)
FROM person
group by 1
order by 1

样本输出:

0   88255
1   469
2   231
3   193
4   53
6   37
于 2013-04-02T20:51:13.817 回答
0

正如我最初提到的,虽然你不能使用用户定义的函数,但你不能解决这个问题。这根本不可行。

如果你可以使用一个函数,那么试试这个大小:

CREATE FUNCTION find_first_number(str VARCHAR(32), def INTEGER DEFAULT 0)
    RETURNING INTEGER AS number
    WITH(NOT VARIANT);

    DEFINE i    INTEGER;
    DEFINE j    INTEGER;
    DEFINE l    INTEGER;
    DEFINE c    CHAR(1);
    DEFINE nstr VARCHAR(32);

    IF str IS NULL THEN
        RETURN def;
    END IF;
    LET l = LENGTH(str);
    FOR i = 0 TO l
        LET c = SUBSTR(str, i, 1);
        IF c >= "0" AND c <= "9" THEN
            LET nstr = c;
            FOR j = i + 1 TO l
                LET c = SUBSTR(str, j, 1);
                IF c >= "0" AND c <= "9" THEN
                    LET nstr = nstr || c;
                ELSE
                    RETURN nstr;    -- String of digits in middle of string
                END IF;
            END FOR;
            RETURN nstr;    -- String of digits at end of string
        END IF;
    END FOR;

    RETURN def;

END FUNCTION;

WITH(NOT VARIANT)子句告诉优化器对于相同的输入,函数总是产生相同的输出(因此输出对于给定的输入是不变的)。

测试代码:

CREATE TEMP TABLE ffn_test
(
    str     VARCHAR(32),
    def     INTEGER,
    num     INTEGER
);

INSERT INTO ffn_test VALUES("UNKNOWN", 0, 0);
INSERT INTO ffn_test VALUES("ROOKIE", -1, -1);
INSERT INTO ffn_test VALUES("LEVEL 1", 0, 1);
INSERT INTO ffn_test VALUES("LEVEL 2A", 0, 2);
INSERT INTO ffn_test VALUES("LEVEL 2B", 0, 2);
INSERT INTO ffn_test VALUES("LEVEL 20", 0, 20);
INSERT INTO ffn_test VALUES("LEVEL 999", 0, 999);
INSERT INTO ffn_test VALUES("LEVEL 3.0", 0, 3);
INSERT INTO ffn_test VALUES(NULL, 0, 0);

SELECT str, def, num,
       find_first_number(str, def) AS result,
       CASE WHEN num = find_first_number(str, def) THEN "PASS" ELSE "FAIL" END pass_fail
  FROM ffn_test;

测试输出:

UNKNOWN     0           0           0           PASS
ROOKIE      -1          -1          -1          PASS
LEVEL 1     0           1           1           PASS
LEVEL 2A    0           2           2           PASS
LEVEL 2B    0           2           2           PASS
LEVEL 20    0           20          20          PASS
LEVEL 999   0           999         999         PASS
LEVEL 3.0   0           3           3           PASS
            0           0           0           PASS
于 2013-04-02T21:29:55.717 回答