更新列(如果不为null)

时间:2018-12-09 15:24:42

标签: sql oracle plsql

我有一个表“ 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

2 个答案:

答案 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;
/
相关问题