ДЛЯ ВСЕХ, ИМЕЮЩИХ ТАКУЮ ПРОБЛЕМУ, Я СМОЖЕН РЕШИТЬ ЭТО, УДАЛИВ '_' В ИМЯ ПАРАМЕТРА VARCHAR2.
Мне нужна помощь в этом, я застрял здесь слишком долго. Я уже пробовал несколько вещей. Был бы признателен за любое устранение неполадок.
У меня есть таблица, в которой строки представляют собой типы ключевых показателей эффективности, а строки — это цели на нескольких целевых уровнях.
Теперь я создаю функцию, которой требуются два параметра: необработанный показатель и тип ключевого показателя эффективности. Например: 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 внутри, где будут использоваться два входных параметра. Я ожидаю, что будет возвращено значение, но оно всегда равно нулю.
В вашей таблице отсутствует столбец target_125
, а в функции отсутствует оператор END;
. Если эти два слова опечатки, то ваша функция работает рабочий пример . Если вы получаете значения NULL
, пожалуйста, отредактируйте свой вопрос, включив в него минимально воспроизводимый пример с утверждениями CREATE TABLE
и INSERT
для соответствующих таблиц, чтобы мы могли воспроизвести проблему, потому что на данный момент мы не можем воспроизвести проблема.
Рекомендуется добавлять префикс к параметрам функции (например, p_raw_score). Это делает ваш код более читабельным и предотвращает нежелательные результаты, если имена столбцов совпадают с именами параметров.
@LasseV.Karlsen да, так и есть.
@MT0 Я добавил туда утверждения. Кроме того, отсутствуют target_125 и End; заявление - это просто опечатки. Спасибо, попробовал по вашей ссылке, получилось так, как я ожидал. Но не могу заставить его работать на моей базе данных. :(
@KoenLostrie, спасибо, это имеет смысл.
@anon_stat_123 Для чего на самом деле используется оператор CREATE TABLE
TARGETS_TABLE
(а не тот, который вы скопировали из моей скрипки)? Ответ ниже расскажет вам, почему вы получаете значения NULL
, и если вы всегда получаете NULL
, то я предполагаю, что столбец KPI_TYPE
— это что-то вроде CHAR(10)
, а не VARCHAR2(10)
(но это также описано в ответе ниже).
@MT0, ох, ладно. Я отредактировал свой вопрос выше. Судя по всему, столбец KPI из таблицы целей уже находится в VARCHAR2, что аналогично параметру KPI_TYPE.
@anon_stat_123 Тогда нужно проверить данные. Если в таблице есть kpi a
и вы ищете KPI A
, то совпадения не будет. Аналогично для KPI A
(с конечными пробелами) и KPI A
.
@MT0, я вижу, я вижу. Спасибо, я последовал вашему совету и сделал TARGETS_TABLE.KPI = KPI_TYPE --> Upper(trim(TARGETS_TABLE.KPI)) = Upper(trim(KPI_TYPE)). Все еще возвращает ноль.
Каков результат вызова функции из PL/SQL с использованием DECLARE v_kpi NUMBER; BEGIN v_kpi := KPI(100, 'KPI A'); END;
(при условии, что KPI A
является допустимым значением поиска)?
Кроме того, уверены ли вы, что: подключаетесь к правильной базе данных; подключение под правильным пользователем; вызов правильной функции; вызов функции, принадлежащей правильному пользователю; использование правильной таблицы TARGETS_TABLE
, принадлежащей нужному пользователю; и т. д.? Извините, но иногда бывают такие простые вещи, как будто вы спрашиваете не о том. Если да, то SELECT owner, object_name, object_type, status FROM ALL_OBJECTS WHERE object_name IN ('KPI', 'TARGETS_TABLE');
отображается только одна KPI
функция и одна таблица, и обе они действительны?
@MT0, да, я действительно понимаю, что иногда именно такие мелочи нас сбивают с толку -- Спасибо. Однако да, я уверен, что все это правильно. Мне удалось создать аналогичную функцию, только для нее требуется только один параметр — raw_score. Я просто установил kpi_type как константу, выполнив WHERE TARGETS_TABLE.KPI = 'KPI B'. Эта функция работает правильно, как и ожидалось. Проблема почему-то возникает, когда мне требуются два параметра — raw_score и kpi_type.
Если вы посмотрите на скрипку, можете ли вы сделать что-то подобное и сравнить результат, когда вы DUMP()
значение аргумента функции и значение в таблице? Если они не совпадают, вы можете посмотреть, какие дополнительные символы есть в аргументе или таблице. Если использование DUMP()
не показывает разницы между ними и ничего больше из предложенных не работает, значит, у меня заканчиваются идеи, почему эта функция не будет работать.
@MT0, перепробовал все, что вы предложили, но функция по-прежнему не работает. почти сдался, но решил убрать '_' в KPI_TYPE (имя параметра), и функция наконец-то заработала. Я не знаю, почему KPI_TYPE не работает, но это была проблема с самого начала. Спасибо, чувак, за предложенные вами методы устранения неполадок, они действительно помогли мне исключить другие возможные проблемы в моем функциональном скрипте.
Значение 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;
Выходы:
Но выполнение того же вызова функции в 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;
Выведет:
Но:
SELECT KPI(100, 'KPI B ') FROM DUAL;
Выходы:
Потому что вы дополнили 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 */
Дает ли оператор выбора какое-либо значение, если он выполняется вне функции?