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 が返されます。"unknown" のように数字が存在しない場合は、0 が返されます。

上記で行ったことは、たとえば Regex.Split を使用して簡単に実行できますが、数値の前に空の要素を持つ文字列配列を返すため、これは使用しませんでした。

SQL クエリでこの数値抽出を行う方法はありますか? 何らかの文字列操作を使用して、最初の数字以外のすべてを削除することはできますか? ただし、関数を作成することは許可されていないため、可能であれば、これらすべてを 1 つのクエリで実行する必要があります。ポインタはありますか?

4

2 に答える 2

1

あなたのレベルは 1 桁または少なくとも既知のセットに制限されていますか? はいの場合、ケースステートメントが機能する可能性があります。有効な値の知識に基づいて「ALPHA 2C 211」の正しい結果を得るには、一致パターンをいじる必要があります。これは、約 89K 行のテーブルの varchar(256) フィールドではかなり高速でした。2.2 ミルについては不明:

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 に答える