Всегда возвращает значение Null: использование оператора Select при создании функции в Oracle SQL Developer

ДЛЯ ВСЕХ, ИМЕЮЩИХ ТАКУЮ ПРОБЛЕМУ, Я СМОЖЕН РЕШИТЬ ЭТО, УДАЛИВ '_' В ИМЯ ПАРАМЕТРА VARCHAR2.

Мне нужна помощь в этом, я застрял здесь слишком долго. Я уже пробовал несколько вещей. Был бы признателен за любое устранение неполадок.

У меня есть таблица, в которой строки представляют собой типы ключевых показателей эффективности, а строки — это цели на нескольких целевых уровнях.

КПЭ цель_50 цель_100 target_150 КПЭ А 5 10 20 КПЭ Б 10 30 50

Теперь я создаю функцию, которой требуются два параметра: необработанный показатель и тип ключевого показателя эффективности. Например: KPI(6,'KPI A') вернет 50 %. KPI(10,'KPI 1') вернет 100%. KPI(9,'KPI B') вернет 0% KPI(100,'KPI B') вернет 150%.

Это мой сценарий:

create or replace FUNCTION KPI(RAW_SCORE in NUMBER, KPI_TYPE in VARCHAR2)

RETURN NUMBER AS

ACTUAL_SCORE NUMBER;

BEGIN

    select case when RAW_SCORE >= target_150 then 1.50
                when RAW_SCORE >= target_100 then 1.00
                when RAW_SCORE >= target_50 then 0.50
            else 0
            end into ACTUAL_SCORE
    from TARGETS_TABLE
    where TARGETS_TABLE.KPI = KPI_TYPE;
RETURN ACTUAL_SCORE;

Однако это всегда возвращает NULL. Я попытался запустить тот же оператор SELECT и вручную ввести raw_score и kpi_type. Он возвращает ожидаемое значение. Но функция всегда возвращает значение null, что бы я ни вводил.

Вот скрипт для таблицы целей:

  CREATE TABLE "TARGETS_TABLE" 
   (    "KPI" VARCHAR2(300 BYTE),  
    "TARGET_50" NUMBER(38,14), 
    "TARGET_100" NUMBER(38,14), 
    "TARGET_150" NUMBER(38,17)
   ) SEGMENT CREATION IMMEDIATE 
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 81920 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "DATA" ;

Я был бы признателен за любые идеи и способы устранения неполадок, которые я мог бы попробовать. Большое спасибо.

Я попытался создать пользовательскую функцию с оператором SELECT внутри, где будут использоваться два входных параметра. Я ожидаю, что будет возвращено значение, но оно всегда равно нулю.

Дает ли оператор выбора какое-либо значение, если он выполняется вне функции?

Lasse V. Karlsen 14.06.2024 10:44

В вашей таблице отсутствует столбец target_125, а в функции отсутствует оператор END;. Если эти два слова опечатки, то ваша функция работает рабочий пример . Если вы получаете значения NULL, пожалуйста, отредактируйте свой вопрос, включив в него минимально воспроизводимый пример с утверждениями CREATE TABLE и INSERT для соответствующих таблиц, чтобы мы могли воспроизвести проблему, потому что на данный момент мы не можем воспроизвести проблема.

MT0 14.06.2024 10:49

Рекомендуется добавлять префикс к параметрам функции (например, p_raw_score). Это делает ваш код более читабельным и предотвращает нежелательные результаты, если имена столбцов совпадают с именами параметров.

Koen Lostrie 14.06.2024 10:57

@LasseV.Karlsen да, так и есть.

anon_stat_123 14.06.2024 11:17

@MT0 Я добавил туда утверждения. Кроме того, отсутствуют target_125 и End; заявление - это просто опечатки. Спасибо, попробовал по вашей ссылке, получилось так, как я ожидал. Но не могу заставить его работать на моей базе данных. :(

anon_stat_123 14.06.2024 11:22

@KoenLostrie, спасибо, это имеет смысл.

anon_stat_123 14.06.2024 11:22

@anon_stat_123 Для чего на самом деле используется оператор CREATE TABLETARGETS_TABLE (а не тот, который вы скопировали из моей скрипки)? Ответ ниже расскажет вам, почему вы получаете значения NULL, и если вы всегда получаете NULL, то я предполагаю, что столбец KPI_TYPE — это что-то вроде CHAR(10), а не VARCHAR2(10) (но это также описано в ответе ниже).

MT0 14.06.2024 11:29

@MT0, ох, ладно. Я отредактировал свой вопрос выше. Судя по всему, столбец KPI из таблицы целей уже находится в VARCHAR2, что аналогично параметру KPI_TYPE.

anon_stat_123 14.06.2024 11:42

@anon_stat_123 Тогда нужно проверить данные. Если в таблице есть kpi a и вы ищете KPI A, то совпадения не будет. Аналогично для KPI A (с конечными пробелами) и KPI A.

MT0 14.06.2024 11:46

@MT0, я вижу, я вижу. Спасибо, я последовал вашему совету и сделал TARGETS_TABLE.KPI = KPI_TYPE --> Upper(trim(TARGETS_TABLE.KPI)) = Upper(trim(KPI_TYPE)). Все еще возвращает ноль.

anon_stat_123 14.06.2024 12:12

Каков результат вызова функции из PL/SQL с использованием DECLARE v_kpi NUMBER; BEGIN v_kpi := KPI(100, 'KPI A'); END; (при условии, что KPI A является допустимым значением поиска)?

MT0 14.06.2024 13:41

Кроме того, уверены ли вы, что: подключаетесь к правильной базе данных; подключение под правильным пользователем; вызов правильной функции; вызов функции, принадлежащей правильному пользователю; использование правильной таблицы TARGETS_TABLE, принадлежащей нужному пользователю; и т. д.? Извините, но иногда бывают такие простые вещи, как будто вы спрашиваете не о том. Если да, то SELECT owner, object_name, object_type, status FROM ALL_OBJECTS WHERE object_name IN ('KPI', 'TARGETS_TABLE'); отображается только одна KPI функция и одна таблица, и обе они действительны?

MT0 14.06.2024 13:45

@MT0, да, я действительно понимаю, что иногда именно такие мелочи нас сбивают с толку -- Спасибо. Однако да, я уверен, что все это правильно. Мне удалось создать аналогичную функцию, только для нее требуется только один параметр — raw_score. Я просто установил kpi_type как константу, выполнив WHERE TARGETS_TABLE.KPI = 'KPI B'. Эта функция работает правильно, как и ожидалось. Проблема почему-то возникает, когда мне требуются два параметра — raw_score и kpi_type.

anon_stat_123 16.06.2024 17:12

Если вы посмотрите на скрипку, можете ли вы сделать что-то подобное и сравнить результат, когда вы DUMP() значение аргумента функции и значение в таблице? Если они не совпадают, вы можете посмотреть, какие дополнительные символы есть в аргументе или таблице. Если использование DUMP() не показывает разницы между ними и ничего больше из предложенных не работает, значит, у меня заканчиваются идеи, почему эта функция не будет работать.

MT0 16.06.2024 21:05

@MT0, перепробовал все, что вы предложили, но функция по-прежнему не работает. почти сдался, но решил убрать '_' в KPI_TYPE (имя параметра), и функция наконец-то заработала. Я не знаю, почему KPI_TYPE не работает, но это была проблема с самого начала. Спасибо, чувак, за предложенные вами методы устранения неполадок, они действительно помогли мне исключить другие возможные проблемы в моем функциональном скрипте.

anon_stat_123 18.06.2024 11:59
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
15
113
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Значение KPI_TYPE, которое вы передаете функции, не существует в таблице TARGETS_TABLE, и функция вызывает исключение NO DATA FOUND, когда пытается использовать SELECT ... INTO ..., а выбранное значение соответствует нулю.

SQL-запрос получает исключение NO DATA FOUND и понимает, что возвращаемое значение должно быть NULL, молча потребляет исключение и заменяет NULL на его место.

Например, если у вас есть данные:

CREATE TABLE targets_table (KPI, target_50, target_100, target_125, target_150) AS
SELECT 'KPI A',  5, 10, 15, 20 FROM DUAL UNION ALL
SELECT 'KPI B', 10, 30, 40, 50 FROM DUAL;

Затем:

SELECT KPI(100, 'KPI C') FROM DUAL;

Выходы:

КПЭ(100,'КПИК') нулевой

Но выполнение того же вызова функции в PL/SQL (а не в SQL):

DECLARE
  v_kpi NUMBER;
BEGIN
  v_kpi := KPI(100, 'KPI C');
END;
/

Показывает основное исключение:

ORA-01403: no data found
ORA-06512: at "FIDDLE_PTZQEOEAQSCSHGUJATEJ.KPI", line 9
ORA-06512: at line 4

Решение состоит в том, чтобы передать значение KPI_TYPE функции, которая действительно существует в таблице.


Однако, если вы создали таблицу, используя тип данных CHAR для столбцов KPI:

CREATE TABLE targets_table (
  KPI        CHAR(10),
  target_50  NUMBER,
  target_100 NUMBER,
  target_125 NUMBER,
  target_150 NUMBER
);
INSERT INTO targets_table (KPI, target_50, target_100, target_125, target_150)
SELECT 'KPI A',  5, 10, 15, 20 FROM DUAL UNION ALL
SELECT 'KPI B', 10, 30, 40, 50 FROM DUAL;

Затем:

SELECT KPI(100, 'KPI B') FROM DUAL;

Выведет:

КПЭ(100,'КПИБ') нулевой

Но:

SELECT KPI(100, 'KPI B     ') FROM DUAL;

Выходы:

КПЭ(100,'КПИБ') 1,5

Потому что вы дополнили KPI до правильной длины столбца.

В этом случае лучшим решением было бы преобразовать столбец KPI из CHAR(10) в VARCHAR2(10), поскольку вы используете строки переменной, а не фиксированной длины. Если вы не можете этого сделать, задайте для аргумента KPI_TYPE тот же тип, что и у столбца (что будет неявно дополнять более короткие строки до нужной длины). Однако предпочтительнее использовать столбец VARCHAR2 (хотя вы можете использовать оба):

CREATE OR REPLACE FUNCTION KPI(
  RAW_SCORE IN TARGETS_TABLE.TARGET_50%TYPE,
  KPI_TYPE  IN TARGETS_TABLE.KPI%TYPE
)
RETURN NUMBER
AS
  ACTUAL_SCORE NUMBER;
BEGIN
  select case when RAW_SCORE >= target_150 then 1.50
              when RAW_SCORE >= target_125 then 1.25
              when RAW_SCORE >= target_100 then 1.00
              when RAW_SCORE >= target_50 then 0.50
         else 0
         end
  into   ACTUAL_SCORE
  from   TARGETS_TABLE
  where  TARGETS_TABLE.KPI = KPI_TYPE;

  RETURN ACTUAL_SCORE;
END;
/

В качестве альтернативы, если вам нужна точно такая же функциональность в запросе (без функции - даже для репликации поведения для исключений NO DATA FOUND и TOO MANY ROWS, если KPI_TYPE соответствует нулю или нескольким строкам соответственно), вы можете использовать коррелированный подзапрос:

-- sample test data
WITH data (raw_score, kpi_type) AS (
  SELECT 10, 'KPI A' FROM DUAL UNION ALL
  SELECT 10, 'KPI B' FROM DUAL UNION ALL
  SELECT 10, 'KPI C' FROM DUAL
)
-- query starts here
SELECT raw_score,
       kpi_type,
       (
         select case
                when d.RAW_SCORE >= t.target_150 then 1.50
                when d.RAW_SCORE >= t.target_125 then 1.25
                when d.RAW_SCORE >= t.target_100 then 1.00
                when d.RAW_SCORE >= t.target_50  then 0.50
                else 0
                end
         from   TARGETS_TABLE t
         where  t.KPI = d.KPI_TYPE
       ) AS kpi
FROM   data d

рабочий пример

Возможно, вам вообще не нужна эта функция. Предполагая, что есть некоторые данные о тестировании оценок и что функция предназначена для вызова как часть некоторого списка выбора, возможно, вы могли бы рассмотреть возможность сделать это без функции...

WITH   --  S a m p l e    D a t a :
    targets_table (KPI, TARGET_50, TARGET_100, TARGET_150) AS
        ( Select 'KPI A',  5, 10, 20 From Dual UNion All
          Select 'KPI B', 10, 30, 50 From Dual
        ),

... ниже приведен cte (test_scores), содержащий ROW_SCORE и KPI_TYPE, представляющие возможный интерес. Если вы планируете вызывать функцию (передавать ей параметры) с помощью sql - рассмотрите код ниже этого cte как возможное решение...

--  dummy data as a sample
    test_scores (ID, RAW_SCORE, KPI_TYPE) AS
        ( Select 1,   6, 'KPI A' From Dual Union All 
          Select 2,  10, 'KPI 1' From Dual Union All 
          Select 3,  10, 'KPI A' From Dual Union All 
          Select 4,   9, 'KPI B' From Dual Union All
          Select 5, 100, 'KPI B' From Dual
        )

... код для получения баллов (1 или более)

--      S Q L :
Select    ts.ID, ts.RAW_SCORE, ts.KPI_TYPE,
          Case When tt.KPI Is Null Then Null
               When ts.RAW_SCORE >= TARGET_150 Then 1.50
               When ts.RAW_SCORE >= TARGET_100 Then 1.00
               When ts.RAW_SCORE >= TARGET_50  Then 0.50
          Else 0
          End "ACTUAL_SCORE",
          Case When tt.KPI Is Null Then 'No such KPI_TYPE in targets_table' End "NOTE"
From      test_scores ts  
Left Join targets_table tt ON(tt.KPI = ts.KPI_TYPE)
--Where tt.KPI = 'some KPI'    <<<---- when you need to filter the resultset
Order By  ts.ID
/*      R e s u l t :
ID  RAW_SCORE  KPI_TYPE  ACTUAL_SCORE  NOTE
--  ---------  --------  ------------  -----------------------------------
 1          6  KPI A              0.5   
 2         10  KPI 1                   No such KPI_TYPE in targets_table
 3         10  KPI A                1   
 4          9  KPI B                0   
 5        100  KPI B              1.5                                       */

Другие вопросы по теме