我们可以将参数传递给 Oracle 中的 JSON_VALUE
can we pass parameter to the JSON_VALUE in Oracle
我有一个从列中获取 json 格式数据的查询。我想通过从列中动态传递 field_name 来获取 json 的数据。
例如
SELECT SUBJECT_MARKS
FROM STUDENT
WHERE STUDENT_ID = 101
结果是:
{
"English": "70",
"Hindi": "80",
"Maths": "90",
"Science": "90",
"Social": "85"
}
如果我想得到特定的学科分数,那么查询是:
SELECT JSON_VALUE(SUBJECT_MARKS,'$.Maths')
FROM STUDENT
WHERE STUDENT_ID = 101
现在的结果是:
90
现在我的要求是动态获取主题标记,同时在查询中将主题名称作为参数提供
SELECT JSON_VALUE(SUBJECT_MARKS,:pSubjectMarks)
FROM STUDENT
WHERE STUDENT_ID = 101
当我们将 :pSubjectMarks
作为 '$.Science'
时执行查询
然后它抛出错误消息
ORA-40454: path expression not a literal
40454. 00000 - "path expression not a literal"
*Cause: The provided path expression was not a literal (a constant).
*Action: Provide a constant path expression.
Error at Line: 29 Column: 45
谁能帮我找到查询的解决方案
提前致谢
从 Oracle 12 开始(当添加了对 JSON 函数的支持时),您可以使用以下任一方法将 CASE
表达式中的所有可能选项列入白名单:
SELECT CASE :pSubjectMarks
WHEN 'English' THEN JSON_VALUE(SUBJECT_MARKS,'$.English')
WHEN 'Hindi' THEN JSON_VALUE(SUBJECT_MARKS,'$.Hindi')
WHEN 'Maths' THEN JSON_VALUE(SUBJECT_MARKS,'$.Maths')
WHEN 'Science' THEN JSON_VALUE(SUBJECT_MARKS,'$.Science')
WHEN 'Social' THEN JSON_VALUE(SUBJECT_MARKS,'$.Social')
END as subject_marks
FROM STUDENT s
或:
SELECT CASE :pSubjectMarks
WHEN 'English' THEN english
WHEN 'Hindi' THEN hindi
WHEN 'Maths' THEN maths
WHEN 'Science' THEN science
WHEN 'Social' THEN social
END as subject_marks
FROM STUDENT s
CROSS APPLY JSON_TABLE(
s.subject_marks,
'$'
COLUMNS (
ENGLISH NUMBER PATH '$.English',
HINDI NUMBER PATH '$.Hindi',
MATHS NUMBER PATH '$.Maths',
SCIENCE NUMBER PATH '$.Science',
SOCIAL NUMBER PATH '$.Social'
)
) j
WHERE STUDENT_ID = 101;
db<>fiddle here
您可以将要从中获取信息的主题构建到 EXECUTE IMMEDIATE
语句中。由于您所有的主题都是简单的字符串,您可以使用 DBMS_ASSERT
程序包来验证 p_subject_name
参数的输入,以防止发生任何 SQL 注入。
以下是有关如何构建程序的示例。
设置
CREATE TABLE students
AS
SELECT 101 AS student_id,
EMPTY_CLOB ()
|| '{ "English": "70", "Hindi": "80", "Maths": "90", "Science": "90", "Social": "85" }' AS subject_marks
FROM DUAL;
DECLARE
PROCEDURE print_subject_score (p_student_id students.student_id%TYPE, p_subject_name VARCHAR2)
IS
l_sql VARCHAR2 (1000);
l_score VARCHAR2 (5);
BEGIN
l_sql :=
'select json_value(subject_marks, ''$.'
|| DBMS_ASSERT.SIMPLE_SQL_NAME (p_subject_name)
|| ''') from students where student_id = :p_student_id';
EXECUTE IMMEDIATE l_sql
INTO l_score
USING p_student_id;
DBMS_OUTPUT.put_line (l_score);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
--Student ID does not exist
NULL;
END;
BEGIN
print_subject_score (p_student_id => 101, p_subject_name => 'English');
print_subject_score (p_student_id => 101, p_subject_name => 'Test');
print_subject_score (p_student_id => 102, p_subject_name => 'Maths');
END;
/
既然你可以访问JSON_VALUE
函数,那么你的Oracle版本至少是12.1。此版本还提供对 SQL 查询中的本地 PL/SQL 声明的访问,您可以在其中完全动态访问 JSON 键。
因此您可以为此使用 JSON_OBJECT_T
数据类型。如您所见,路径作为列值(表达式,而不是文字)传递,您可以使用绑定变量代替表达式。
with function get_mark(
p_marks in varchar2,
p_subj in varchar2
)
return number
as
json JSON_OBJECT_T;
begin
json := JSON_OBJECT_T(p_marks);
return json.get_String(p_subj);
end;
select
t.column_value as subj,
get_mark(SUBJECT_MARKS, t.column_value) as mark
from students
cross join sys.odcivarchar2list(
'English',
'Hindi',
'Maths',
'Science',
'Social',
'something'
) t
where student_id = 101
SUBJ | MARK
:-------- | ---:
English | 70
Hindi | 80
Maths | 90
Science | 90
Social | 85
something | null
db<>fiddle here
我有一个从列中获取 json 格式数据的查询。我想通过从列中动态传递 field_name 来获取 json 的数据。
例如
SELECT SUBJECT_MARKS
FROM STUDENT
WHERE STUDENT_ID = 101
结果是:
{
"English": "70",
"Hindi": "80",
"Maths": "90",
"Science": "90",
"Social": "85"
}
如果我想得到特定的学科分数,那么查询是:
SELECT JSON_VALUE(SUBJECT_MARKS,'$.Maths')
FROM STUDENT
WHERE STUDENT_ID = 101
现在的结果是:
90
现在我的要求是动态获取主题标记,同时在查询中将主题名称作为参数提供
SELECT JSON_VALUE(SUBJECT_MARKS,:pSubjectMarks)
FROM STUDENT
WHERE STUDENT_ID = 101
当我们将 :pSubjectMarks
作为 '$.Science'
时执行查询
然后它抛出错误消息
ORA-40454: path expression not a literal 40454. 00000 - "path expression not a literal" *Cause: The provided path expression was not a literal (a constant). *Action: Provide a constant path expression. Error at Line: 29 Column: 45
谁能帮我找到查询的解决方案 提前致谢
从 Oracle 12 开始(当添加了对 JSON 函数的支持时),您可以使用以下任一方法将 CASE
表达式中的所有可能选项列入白名单:
SELECT CASE :pSubjectMarks
WHEN 'English' THEN JSON_VALUE(SUBJECT_MARKS,'$.English')
WHEN 'Hindi' THEN JSON_VALUE(SUBJECT_MARKS,'$.Hindi')
WHEN 'Maths' THEN JSON_VALUE(SUBJECT_MARKS,'$.Maths')
WHEN 'Science' THEN JSON_VALUE(SUBJECT_MARKS,'$.Science')
WHEN 'Social' THEN JSON_VALUE(SUBJECT_MARKS,'$.Social')
END as subject_marks
FROM STUDENT s
或:
SELECT CASE :pSubjectMarks
WHEN 'English' THEN english
WHEN 'Hindi' THEN hindi
WHEN 'Maths' THEN maths
WHEN 'Science' THEN science
WHEN 'Social' THEN social
END as subject_marks
FROM STUDENT s
CROSS APPLY JSON_TABLE(
s.subject_marks,
'$'
COLUMNS (
ENGLISH NUMBER PATH '$.English',
HINDI NUMBER PATH '$.Hindi',
MATHS NUMBER PATH '$.Maths',
SCIENCE NUMBER PATH '$.Science',
SOCIAL NUMBER PATH '$.Social'
)
) j
WHERE STUDENT_ID = 101;
db<>fiddle here
您可以将要从中获取信息的主题构建到 EXECUTE IMMEDIATE
语句中。由于您所有的主题都是简单的字符串,您可以使用 DBMS_ASSERT
程序包来验证 p_subject_name
参数的输入,以防止发生任何 SQL 注入。
以下是有关如何构建程序的示例。
设置
CREATE TABLE students
AS
SELECT 101 AS student_id,
EMPTY_CLOB ()
|| '{ "English": "70", "Hindi": "80", "Maths": "90", "Science": "90", "Social": "85" }' AS subject_marks
FROM DUAL;
DECLARE
PROCEDURE print_subject_score (p_student_id students.student_id%TYPE, p_subject_name VARCHAR2)
IS
l_sql VARCHAR2 (1000);
l_score VARCHAR2 (5);
BEGIN
l_sql :=
'select json_value(subject_marks, ''$.'
|| DBMS_ASSERT.SIMPLE_SQL_NAME (p_subject_name)
|| ''') from students where student_id = :p_student_id';
EXECUTE IMMEDIATE l_sql
INTO l_score
USING p_student_id;
DBMS_OUTPUT.put_line (l_score);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
--Student ID does not exist
NULL;
END;
BEGIN
print_subject_score (p_student_id => 101, p_subject_name => 'English');
print_subject_score (p_student_id => 101, p_subject_name => 'Test');
print_subject_score (p_student_id => 102, p_subject_name => 'Maths');
END;
/
既然你可以访问JSON_VALUE
函数,那么你的Oracle版本至少是12.1。此版本还提供对 SQL 查询中的本地 PL/SQL 声明的访问,您可以在其中完全动态访问 JSON 键。
因此您可以为此使用 JSON_OBJECT_T
数据类型。如您所见,路径作为列值(表达式,而不是文字)传递,您可以使用绑定变量代替表达式。
with function get_mark( p_marks in varchar2, p_subj in varchar2 ) return number as json JSON_OBJECT_T; begin json := JSON_OBJECT_T(p_marks); return json.get_String(p_subj); end; select t.column_value as subj, get_mark(SUBJECT_MARKS, t.column_value) as mark from students cross join sys.odcivarchar2list( 'English', 'Hindi', 'Maths', 'Science', 'Social', 'something' ) t where student_id = 101
SUBJ | MARK :-------- | ---: English | 70 Hindi | 80 Maths | 90 Science | 90 Social | 85 something | null
db<>fiddle here