我有一个表“ Contacts”,其中有一堆行和列,看起来像这样:
ID |FirstName|LastName|Addr1|Addr2 |Phone |...|
1 |Anna |Johnson |123 |Fake street|12345678|...|
2 |Bob |Smith |234 |Foo Ave |23456789|...|
现在,安娜已经搬家了,我想更新她的地址。我的系统输出一个具有Contacts行类型的行,其中PK和update列都有一个值,其他所有内容都为空。
declare
update_row Contacts%rowtype;
begin
update_row := (1,null,null,987,Bar Street,null,null);
我现在想做的是保留Contacts的原始行,然后更新update_row中不为null的值。 我本质上想做的是:
if update_row.FirstName is not null then Contacts.FirstName := update_row.FirstName;
if update_row.LastName is not null then Contacts.LastName := update_row.LastName;
if update_row.Addr1 is not null then Contacts.Addr1 := update_row.Addr1;
if update_row.Addr2 is not null then Contacts.Addr2 := update_row.Addr2;
...
或某种程度的东西,而不必显式检查每个列的值。 是否有类似以下的解决方案?:
merge update_row into Contacts where update_row is not null and ID = 1
答案 0 :(得分:2)
您可以编写一个查询,检查每个输入字段,并仅在给出非空值时才进行替换。
以下对于使用NVL
函数的Oracle(在mysql中,使用COALESCE
,依此类推)起作用。
UPDATE CONTACTS
SET
FIRST_NAME = NVL(?, FIRST_NAME),
LAST_NAME = NVL(?, LAST_NAME),
ADDR1 = NVL(?, ADDR1),
ADDR2 = NVL(?, ADDR2),
PHONE = NVL(?, PHONE)
WHERE ID = ?
答案 1 :(得分:0)
这里是一个概念,您如何动态地通过包来实现它。
CREATE TABLE contact (
id NUMBER,
cust_id NUMBER,
line1 VARCHAR2(100),
line2 VARCHAR2(100)
);
CREATE OR REPLACE PACKAGE pkg_test IS
/*
* Variables for contact table.
*/
gv_contact1 contact%ROWTYPE;
gv_contact2 contact%ROWTYPE;
/* Do overwrite for Contact table
* pi_contact1.ColumnName is overwitten when pi_contact2.ColumnName IS NOT NULL
*
* @param pi_contact1 Contact record which is overwritten
* @param pi_contact2 Contact record from wich data are copied
*/
FUNCTION overwriteContactIfNull(
pi_contact1 contact%ROWTYPE,
pi_contact2 contact%ROWTYPE
)
RETURN contact%ROWTYPE;
END pkg_test;
/
CREATE OR REPLACE PACKAGE BODY pkg_test IS
/* List of columns in a table
*
* @param pi_tableName Table name
*/
FUNCTION columns_(
pi_tableName VARCHAR2
)
RETURN SYS.ODCIVARCHAR2LIST
AS
lv_ColumNames SYS.ODCIVARCHAR2LIST;
begin
SELECT CAST (
MULTISET
(
SELECT c.column_name
FROM user_tab_columns c
WHERE c.table_name = pi_tableName
) AS SYS.ODCIVARCHAR2LIST
)
INTO lv_ColumNames
FROM dual;
RETURN lv_ColumNames;
END columns_;
/* Do overwirte for a specific table
* gv_#TableName1.ColumnName is overwitten when gv_#TableName2.ColumnName IS NOT NULL
*
* @param pi_tableName Table name
*/
PROCEDURE overwriteIfNull(
pi_tableName VARCHAR2
)
AS
lc_bodyTemplate CONSTANT CLOB :=
'BEGIN' || CHR(10) ||
' IF pkg_test.gv_#TableName2.#ColumnName IS NOT NULL THEN' || CHR(10) ||
' pkg_test.gv_#TableName1.#ColumnName := pkg_test.gv_#TableName2.#ColumnName; ' || CHR(10) ||
' END IF;' || CHR(10) ||
'END;';
lv_ColumNames SYS.ODCIVARCHAR2LIST;
lv_body CLOB;
BEGIN
lv_ColumNames := columns_(pi_tableName => 'CONTACT');
FOR i IN 1..lv_ColumNames.COUNT
LOOP
lv_body := lc_bodyTemplate;
lv_body := REPLACE(lv_body, '#TableName' , pi_tableName);
lv_body := REPLACE(lv_body, '#ColumnName', lv_ColumNames(i));
EXECUTE IMMEDIATE lv_body;
END LOOP;
END overwriteIfNull;
/* Do overwrite for Contact table
* pi_contact1.ColumnName is overwitten when pi_contact2.ColumnName IS NOT NULL
*
* @param pi_contact1 Contact record which is overwritten
* @param pi_contact2 Contact record from wich data are copied
*/
FUNCTION overwriteContactIfNull(
pi_contact1 contact%ROWTYPE,
pi_contact2 contact%ROWTYPE
)
RETURN contact%ROWTYPE
AS
BEGIN
gv_contact1 := pi_contact1;
gv_contact2 := pi_contact2;
overwriteIfNull(pi_tableName => 'CONTACT');
RETURN gv_contact1;
END overwriteContactIfNull;
END pkg_test;
/
这是通话示例
DECLARE
lv_contact1 contact%ROWTYPE;
lv_contact2 contact%ROWTYPE;
BEGIN
lv_contact1.line1 := 'Line1';
lv_contact1.line1 := 'Line2';
lv_contact2.line2 := 'NewLine2';
lv_contact1 := pkg_test.overwriteContactIfNull(
pi_contact1 => lv_contact1,
pi_contact2 => lv_contact2
);
dbms_output.put_line('lv_contact1.line1:' || lv_contact1.line1);
dbms_output.put_line('lv_contact1.line2:' || lv_contact1.line2);
END;
/