Oracle函数获取最小值

yqhsw0fo  于 2022-11-22  发布在  Oracle
关注(0)|答案(4)|浏览(224)

我试图创建一个简单的函数,返回传递给该函数的最小值......但当运行时,它不工作,它只是返回相同的值,我传递给函数15,2,3,它应该返回2......有人能帮助我,告诉我为什么它不工作吗?

CREATE OR REPLACE
function
 GET_LEAST_VALUE(in_numbers IN VARCHAR2)
 RETURN VARCHAR2
 IS
 vReturn varchar2(50);
 
   BEGIN
       vReturn := least(in_numbers);
     return vReturn;
   END;
bmp9r5qi

bmp9r5qi1#

正如所评论的,它不能工作,因为你实际上是传递一个字符串给函数。
这里有一个选项-它将该字符串拆分为行; regexp_substr返回一个字符串(再次),所以-首先修剪它(以删除可能的空格),然后应用to_number(否则你会比较字符串,这与比较数字不同)。最后,应用min聚合函数,因为如果你传递了多个 numberleast将不起作用(你会得到too_many_rows错误)。

SQL> CREATE OR REPLACE FUNCTION get_least_value (in_numbers IN VARCHAR2)
  2     RETURN VARCHAR2
  3  IS
  4     vreturn  VARCHAR2 (50);
  5  BEGIN
  6     WITH
  7        temp
  8        AS
  9           (    SELECT TO_NUMBER (TRIM (REGEXP_SUBSTR (in_numbers,
 10                                                       '[^,]+',
 11                                                       1,
 12                                                       LEVEL))) val
 13                  FROM DUAL
 14            CONNECT BY LEVEL <= REGEXP_COUNT (in_numbers, ',') + 1)
 15     SELECT MIN (val)
 16       INTO vreturn
 17       FROM temp;
 18
 19     RETURN vreturn;
 20  END;
 21  /

Function created.

SQL> SELECT get_least_value ('15,2,3') result FROM DUAL;

RESULT
--------------------------------------------------------------------------------
2

SQL>
de90aj5v

de90aj5v2#

CREATE OR REPLACE FUNCTION get_least_value (in_numbers IN VARCHAR2) RETURN NUMBER IS
    w_result NUMBER ;
BEGIN
    SELECT MIN(value) INTO w_result FROM 
    json_table (
        '[' || in_numbers || ']',
        '$[*]'
        columns (
            value NUMBER PATH '$'
        )
    )
    ;
    RETURN w_result ;
END ;
/

SELECT get_least_value('4,1,9,0,-5') from dual ;

-5
oalqel3c

oalqel3c3#

为什么不工作?

当你传入一个字符串时,LEAST在将这个字符串与其他字符串进行比较时找到最小值,所以它返回这个字符串;这正是LEAST想要做的。
但是,它并不是你所期望的那样。

为什么它没有按照我的预期运行?

您希望将'15,2,3'计算为一个数字列表(即LEAST(15,2,3)),但它不是一个数字列表,它是一个字符串文字,在人类看来,它恰好像一个数字列表,但在SQL引擎看来,它实际上只是一个字符串值,并将计算为LEAST('15,2,3')

如何修复?

您需要通过集合(例如,内置的SYS.ODCINUMBERLIST varray集合类型)传入多个值:

CREATE FUNCTION GET_LEAST_VALUE(
  in_numbers IN SYS.ODCINUMBERLIST
) RETURN NUMBER
IS
  v_least NUMBER;
BEGIN
  SELECT MIN(column_value)
  INTO   v_least
  FROM   TABLE(in_numbers);

  RETURN v_least;
END;
/

然后将其命名为:

SELECT get_least_value(SYS.ODCINUMBERLIST(15,2,3))
FROM   DUAL;

BEGIN
  DBMS_OUTPUT.PUT_LINE(get_least_value(SYS.ODCINUMBERLIST(15,2,3)));
END;
/

fiddle
或者,您需要split the string into separate values,然后找到这些值中的最小值。

kokeuurv

kokeuurv4#

它可以用一个用户定义的函数通过分隔符拆分字符串并返回一个元素数组来完成。在这种情况下,简单的plsql函数可以如下所示:

create or replace Function Get_Least(P_LIST VARCHAR2, P_DELIMITER VARCHAR2 := ',') Return NUMBER AS
BEGIN
    Declare
        elements      STRING_ARRAY := STRING_ARRAY();
        least_element Number(12) := 999999999999;
    Begin
        elements := split(P_LIST , P_DELIMITER);
        For i In 1..elements.count Loop
            If To_Number(elements(i)) < least_element Then
                least_element := To_Number(elements(i));
            End If;
        End Loop;
        RETURN least_element;
    End;
END get_Least;

下面是用户定义的STRING_ARRAY类型和SPLIT()函数:

create or replace TYPE STRING_ARRAY AS  
VARRAY(4000) OF VARCHAR2(1000);
/

create or replace FUNCTION split ( csvString  IN  VarChar2, delimiter  IN  VarChar2 := ',') 
                  RETURN STRING_ARRAY AS 
BEGIN
    Declare
        elements     STRING_ARRAY := STRING_ARRAY();
        CURSOR c IS
            SELECT  COL1
            FROM    (   SELECT INDX, MY_STR1, COL1_ELEMENTS, COL1
                  FROM  (   SELECT 0 "INDX", COL1 "MY_STR1", COL1_ELEMENTS, COL1
                          FROM(
                                SELECT
                                  REPLACE(COL1, delimiter || ' ', delimiter) "COL1",    
                                  Trim(Length(Replace(COL1, delimiter || ' ', delimiter))) - Trim(Length(Translate(REPLACE(COL1, delimiter || ' ', delimiter), 'A' || delimiter, 'A'))) + 1 "COL1_ELEMENTS"
                                FROM (SELECT csvString "COL1" FROM DUAL)        
                              )
                      )
                  MODEL     
                      DIMENSION BY(0 as INDX)
                      MEASURES(COL1, COL1_ELEMENTS, CAST('a' as VarChar2(4000)) as MY_STR1)
                      RULES ITERATE (1000)      --UNTIL (ITERATION_NUMBER <= COL1_ELEMENTS[ITERATION_NUMBER + 1]) -- If you don't know the number of elements this should be bigger then you aproximation. Othewrwise it will split given number of elements
                      (
                        COL1_ELEMENTS[ITERATION_NUMBER + 1] = COL1_ELEMENTS[0],
                        MY_STR1[0] = COL1[CV()],
                        MY_STR1[ITERATION_NUMBER + 1] = SubStr(MY_STR1[ITERATION_NUMBER], InStr(MY_STR1[ITERATION_NUMBER], delimiter, 1) + Length(delimiter)),
                        COL1[ITERATION_NUMBER + 1] = SubStr(MY_STR1[ITERATION_NUMBER], 1, CASE WHEN InStr(MY_STR1[ITERATION_NUMBER], delimiter) <> 0 THEN InStr(MY_STR1[ITERATION_NUMBER], delimiter)-1 ELSE Length(MY_STR1[ITERATION_NUMBER]) END)
                      )
                )
            WHERE INDX > 0 And INDX <= COL1_ELEMENTS;  -- And COL1 Is Not Null;
        cStr    VarChar2(1000);
        i       Number := 1;
    Begin 
        If c%ISOPEN Then CLOSE c; End If;
        OPEN c;
        LOOP
            FETCH c Into cStr;
            EXIT WHEN c%NOTFOUND;
            elements.extend;
            elements(i) := cStr;
            i := i + 1;
        END LOOP;
        CLOSE c;
        RETURN elements;
    End;
END split;
/

使用包含逗号分隔的数字列表的字符串进行测试调用

SET SERVEROUTPUT ON
Declare
    least_one    NUMBER(3);
    Numbers_List VARCHAR2(100) := '123, 46, 756, 3, 918, 4';
Begin
    least_one := GET_LEAST(Numbers_List);
    DBMS_OUTPUT.PUT_LINE(least_one);
End;
--  
--  Result:
--
--  anonymous block completed
--  3

SPLIT()函数可以用任何分隔符(包括用于分隔句子中单词的空格字符)来分隔任何字符串。它也适用于没有正则表达式的数据库版本。
示例:-相同的数字由“xo”分隔-相同的结果

SET SERVEROUTPUT ON
Declare
    least_one    NUMBER(3);
    Numbers_List VARCHAR2(100) := '123xo46xo756xo3xo918xo4';
Begin
    least_one := GET_LEAST(Numbers_List, 'xo');
    DBMS_OUTPUT.PUT_LINE(least_one);
End;
--  
--  Result:
--
--  anonymous block completed
--  3

此致

相关问题