如何为 return 约束的列名创建一个函数?
How can I make a function to return a constraint's column name(s)?
我正在尝试在 Oracle 中创建一个 pl/SQL 函数,该函数 return 是受给定约束影响的列的列名。例如,给定以下 table:
CREATE TABLE Products
( Mfr CHAR(3) NOT NULL
,Product CHAR(5) NOT NULL
,Description VARCHAR2(100) DEFAULT 'N/A'
,Price NUMBER(5,2)
,QtyOnHand NUMBER(5) DEFAULT 0
,CONSTRAINT ProductsPK
PRIMARY KEY(Mfr, Product)
,CONSTRAINT UniqueDescription
UNIQUE (Description)
,CONSTRAINT UniqueProduct
UNIQUE (Product)
,CONSTRAINT CheckPrice
CHECK (Price BETWEEN .01 AND 87.98)
,CONSTRAINT CheckQuantity
CHECK (QtyOnHand >= 0)
);
我想将 2 个值传递给函数,例如 Table 名称(在本例中为 'Products')和约束的名称(在本例中为 ProductsPK 注意: 该函数应该能够处理任何给定的约束)。鉴于这些细节,它应该 return 受影响的列名称。
目前我的功能如下
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION GET_CONSTRAINT_COLUMNS(
iTableName IN Varchar2,
iConstraintName IN Varchar2
)
RETURN varchar2
AS
CURSOR Extract_KEY IS
SELECT INDEX_NAME,a.constraint_type
FROM USER_CONSTRAINTS a
WHERE TABLE_NAME = iTableName;
--ORDER BY Column_ID;
CurrentRow Extract_KEY%ROWTYPE;
--Declaring Variables
wKeys VARCHAR2(50);
BEGIN
For CurrentRow IN Extract_Key LOOP
IF currentRow.Constraint_type = 'P' THEN
SELECT COLUMN_NAME
INTO wkeys
FROM user_cons_columns
WHERE TABLE_NAME = iTableName
AND constraint_name = iconstraintname;
ELSIF currentRow.Constraint_Type = 'U' THEN
SELECT COLUMN_NAME
INTO wkeys
FROM user_cons_columns
WHERE constraint_name = iconstraintname
AND table_name = iTableName;
END IF;
END LOOP;
RETURN wKeys;
END;
/
上述函数中的 select 语句单独运行良好。但是当函数本身通过下面的匿名块执行时:
SET SERVEROUTPUT ON;
DECLARE
wconsumable varchar2(24);
BEGIN
wConsumable := GET_CONSTRAINT_COLUMNS('PRODUCTS', 'PRODUCTSPK');
DBMS_OUTPUT.PUT_LINE(wConsumable);
END;
/
我收到以下错误:
Error starting at line : 47 in command -
DECLARE
wconsumable varchar2(24);
BEGIN
wConsumable := GET_CONSTRAINT_COLUMNS('PRODUCTS', 'PRODUCTSPK');
DBMS_OUTPUT.PUT_LINE(wConsumable);
END;
Error report -
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "T325.GET_CONSTRAINT_COLUMNS", line 21
ORA-06512: at line 4
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
附加信息:
主键 select 语句(begin 之后的第一个语句)在上面的函数中 returns 下面的 IF 运行 INDIVIDUALLY
MFR
PRODUCT
第二个select语句(关于唯一键)IF 运行 INDIVIDUALLY 像这样:
SELECT COLUMN_NAME FROM user_cons_columns WHERE constraint_name = 'UNIQUEPRODUCT' AND table_name = 'PRODUCTS';
Returns这个:
Product
如您所见,某些约束可能 return 1 或 2 列。无论列数如何,我都希望它们都存储在变量中并 returned 到调用函数的过程。
我怀疑我的问题源于我使用的光标,但我不能确定。我应该如何解决这个问题?
您可以使用 SYS_REFCURSOR
而不是标准内部数据类型,例如 VARCHAR2
,以便能够 return 多行。带有 INTO 子句的 SELECT 语句不能 return 多行,并且当前错误消息 (ORA-01422
) hurls.
因此,先创建一个包含SYS_REFCURSOR
的存储函数:
SQL> CREATE OR REPLACE FUNCTION GET_CONSTRAINT_COLUMNS(iTableName IN VARCHAR2,
iConstraintName IN VARCHAR2)
RETURN SYS_REFCURSOR AS
wkeys SYS_REFCURSOR;
v_sql VARCHAR2(32767);
BEGIN
v_sql := 'SELECT column_name
FROM user_cons_columns
WHERE constraint_name = :ic
AND table_name = :it';
OPEN wkeys FOR v_sql USING iconstraintname, iTableName;
RETURN wkeys;
END;
/
然后从 SQL 开发者控制台调用
SQL> DECLARE
wConsumable SYS_REFCURSOR;
BEGIN
:wConsumable := GET_CONSTRAINT_COLUMNS('PRODUCTS', 'PRODUCTSPK');
END;
/
SQL> PRINT wConsumable ;
- 第一个SQL(为
CURSOR Extract_KEY
准备)是多余的;
LOOP
中的两个 SELECT
语句没有区别,
顺便说一句,使用当前案例不需要 LOOP
;
- 可以使用命令
PRINT
代替DBMS_OUTPUT.PUT_LINE
order to return 结果 SYS_REFCURSOR
.
我真的不知道您将使用这些功能的用例。 @Barbaros Özhan 提供的答案非常好,您可以将输出用作输入或对其进行任何类型的操作(如果有)
但如果它仅用于显示目的,我想提供另一种使用 listagg
的替代方法,它将列与用户定义的分隔符结合起来,并为您提供所有列的连接字符串。
在我放置代码之前,我想指出您不需要像当前代码中那样的任何 cursor
和 if-else
,因为您将 constraint_name
作为参数和使用 user_*
视图和约束在 schema
中是唯一的。即使我们不需要 table_name
,但我们可以保留它以供展示。
所以我最后修改的代码是,
CREATE OR REPLACE FUNCTION GET_CONSTRAINT_COLUMNS(
iTableName IN Varchar2,
iConstraintName IN Varchar2
)
RETURN varchar2
AS
--Declaring Variables
wKeys VARCHAR2(4000);
BEGIN
SELECT listagg(column_name,' and ') within group (order by position)
INTO wkeys
FROM user_cons_columns
WHERE TABLE_NAME = iTableName
AND constraint_name = iconstraintname;
RETURN wKeys;
END;
/
------测试------
DECLARE
wconsumable varchar2(24);
BEGIN
DBMS_OUTPUT.PUT_LINE('');
wConsumable := GET_CONSTRAINT_COLUMNS
('PRODUCTS', 'PRODUCTSPK');
DBMS_OUTPUT.PUT_LINE(wConsumable);
END;
/
dbms_output:
MFR and PRODUCT
我正在尝试在 Oracle 中创建一个 pl/SQL 函数,该函数 return 是受给定约束影响的列的列名。例如,给定以下 table:
CREATE TABLE Products
( Mfr CHAR(3) NOT NULL
,Product CHAR(5) NOT NULL
,Description VARCHAR2(100) DEFAULT 'N/A'
,Price NUMBER(5,2)
,QtyOnHand NUMBER(5) DEFAULT 0
,CONSTRAINT ProductsPK
PRIMARY KEY(Mfr, Product)
,CONSTRAINT UniqueDescription
UNIQUE (Description)
,CONSTRAINT UniqueProduct
UNIQUE (Product)
,CONSTRAINT CheckPrice
CHECK (Price BETWEEN .01 AND 87.98)
,CONSTRAINT CheckQuantity
CHECK (QtyOnHand >= 0)
);
我想将 2 个值传递给函数,例如 Table 名称(在本例中为 'Products')和约束的名称(在本例中为 ProductsPK 注意: 该函数应该能够处理任何给定的约束)。鉴于这些细节,它应该 return 受影响的列名称。
目前我的功能如下
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION GET_CONSTRAINT_COLUMNS(
iTableName IN Varchar2,
iConstraintName IN Varchar2
)
RETURN varchar2
AS
CURSOR Extract_KEY IS
SELECT INDEX_NAME,a.constraint_type
FROM USER_CONSTRAINTS a
WHERE TABLE_NAME = iTableName;
--ORDER BY Column_ID;
CurrentRow Extract_KEY%ROWTYPE;
--Declaring Variables
wKeys VARCHAR2(50);
BEGIN
For CurrentRow IN Extract_Key LOOP
IF currentRow.Constraint_type = 'P' THEN
SELECT COLUMN_NAME
INTO wkeys
FROM user_cons_columns
WHERE TABLE_NAME = iTableName
AND constraint_name = iconstraintname;
ELSIF currentRow.Constraint_Type = 'U' THEN
SELECT COLUMN_NAME
INTO wkeys
FROM user_cons_columns
WHERE constraint_name = iconstraintname
AND table_name = iTableName;
END IF;
END LOOP;
RETURN wKeys;
END;
/
上述函数中的 select 语句单独运行良好。但是当函数本身通过下面的匿名块执行时:
SET SERVEROUTPUT ON;
DECLARE
wconsumable varchar2(24);
BEGIN
wConsumable := GET_CONSTRAINT_COLUMNS('PRODUCTS', 'PRODUCTSPK');
DBMS_OUTPUT.PUT_LINE(wConsumable);
END;
/
我收到以下错误:
Error starting at line : 47 in command -
DECLARE
wconsumable varchar2(24);
BEGIN
wConsumable := GET_CONSTRAINT_COLUMNS('PRODUCTS', 'PRODUCTSPK');
DBMS_OUTPUT.PUT_LINE(wConsumable);
END;
Error report -
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "T325.GET_CONSTRAINT_COLUMNS", line 21
ORA-06512: at line 4
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
附加信息:
主键 select 语句(begin 之后的第一个语句)在上面的函数中 returns 下面的 IF 运行 INDIVIDUALLY
MFR
PRODUCT
第二个select语句(关于唯一键)IF 运行 INDIVIDUALLY 像这样:
SELECT COLUMN_NAME FROM user_cons_columns WHERE constraint_name = 'UNIQUEPRODUCT' AND table_name = 'PRODUCTS';
Returns这个:
Product
如您所见,某些约束可能 return 1 或 2 列。无论列数如何,我都希望它们都存储在变量中并 returned 到调用函数的过程。
我怀疑我的问题源于我使用的光标,但我不能确定。我应该如何解决这个问题?
您可以使用 SYS_REFCURSOR
而不是标准内部数据类型,例如 VARCHAR2
,以便能够 return 多行。带有 INTO 子句的 SELECT 语句不能 return 多行,并且当前错误消息 (ORA-01422
) hurls.
因此,先创建一个包含SYS_REFCURSOR
的存储函数:
SQL> CREATE OR REPLACE FUNCTION GET_CONSTRAINT_COLUMNS(iTableName IN VARCHAR2,
iConstraintName IN VARCHAR2)
RETURN SYS_REFCURSOR AS
wkeys SYS_REFCURSOR;
v_sql VARCHAR2(32767);
BEGIN
v_sql := 'SELECT column_name
FROM user_cons_columns
WHERE constraint_name = :ic
AND table_name = :it';
OPEN wkeys FOR v_sql USING iconstraintname, iTableName;
RETURN wkeys;
END;
/
然后从 SQL 开发者控制台调用
SQL> DECLARE
wConsumable SYS_REFCURSOR;
BEGIN
:wConsumable := GET_CONSTRAINT_COLUMNS('PRODUCTS', 'PRODUCTSPK');
END;
/
SQL> PRINT wConsumable ;
- 第一个SQL(为
CURSOR Extract_KEY
准备)是多余的; LOOP
中的两个SELECT
语句没有区别, 顺便说一句,使用当前案例不需要LOOP
;- 可以使用命令
PRINT
代替DBMS_OUTPUT.PUT_LINE
order to return 结果SYS_REFCURSOR
.
我真的不知道您将使用这些功能的用例。 @Barbaros Özhan 提供的答案非常好,您可以将输出用作输入或对其进行任何类型的操作(如果有)
但如果它仅用于显示目的,我想提供另一种使用 listagg
的替代方法,它将列与用户定义的分隔符结合起来,并为您提供所有列的连接字符串。
在我放置代码之前,我想指出您不需要像当前代码中那样的任何 cursor
和 if-else
,因为您将 constraint_name
作为参数和使用 user_*
视图和约束在 schema
中是唯一的。即使我们不需要 table_name
,但我们可以保留它以供展示。
所以我最后修改的代码是,
CREATE OR REPLACE FUNCTION GET_CONSTRAINT_COLUMNS(
iTableName IN Varchar2,
iConstraintName IN Varchar2
)
RETURN varchar2
AS
--Declaring Variables
wKeys VARCHAR2(4000);
BEGIN
SELECT listagg(column_name,' and ') within group (order by position)
INTO wkeys
FROM user_cons_columns
WHERE TABLE_NAME = iTableName
AND constraint_name = iconstraintname;
RETURN wKeys;
END;
/
------测试------
DECLARE
wconsumable varchar2(24);
BEGIN
DBMS_OUTPUT.PUT_LINE('');
wConsumable := GET_CONSTRAINT_COLUMNS
('PRODUCTS', 'PRODUCTSPK');
DBMS_OUTPUT.PUT_LINE(wConsumable);
END;
/
dbms_output:
MFR and PRODUCT