【问题标题】:How to Chain Member Functions in a PL/SQL Object Type如何在 PL/SQL 对象类型中链接成员函数
【发布时间】:2018-09-15 09:40:14
【问题描述】:

对于我想要完成的工作来说,这可能是一个糟糕的用例,但我已经阅读了几十页,我无法确定这在 Oracle 中是否可行。

我想完成类似于另一个 stackoverflow 问题的事情:How to chain calls in a pl/sql object type of functions returning SELF

在我的代码中,我有一个名为 parseName 的类型,它带有一个构造函数和 3 个成员函数:getFirstName、getMiddleName、getLastName。我想要第四个成员函数:cleanString,它删除不在 A-Z 或 a-z 之间的所有字符。

调用示例:

SELECT parseName('John123 Doe').getFirstName().cleanString()
FROM dual;

SELECT parseName('John123 Doe').getFirstName()
FROM dual;

这是我目前所写的类型。

CREATE OR REPLACE TYPE parseName AS OBJECT
(
    g_name         VARCHAR2(255),
    g_parts        NUMBER,
    g_first_name   VARCHAR2(255),
    g_middle_name  VARCHAR2(1),
    g_last_name    VARCHAR2(255),
    -- constructor function
    CONSTRUCTOR FUNCTION parseName
      (p_name IN VARCHAR2)
      RETURN self AS result,
    -- member functions
    MEMBER FUNCTION getFirstName  RETURN VARCHAR2,
    MEMBER FUNCTION getMiddleName RETURN VARCHAR2,
    MEMBER FUNCTION getLastName   RETURN VARCHAR2
);
/
SHOW ERRORS
/

CREATE OR REPLACE TYPE BODY parseName IS

  -- populateValues
  CONSTRUCTOR FUNCTION parseName
    (p_name IN VARCHAR2)
  RETURN self AS result
  IS
    -- other variables
    v_name    VARCHAR2(255);
    v_length  NUMBER;
    v_parts   NUMBER;
    v_instr   NUMBER;
  BEGIN

    -- save off input
    v_name := TRIM(p_name);

    -- check
    IF v_name IS NULL THEN
      self.g_first_name  := 'Unknown';
      self.g_middle_name := ' ';
      self.g_last_name   := 'Unknown';
      RETURN;
    END IF;

    -- otherwise, fill our global
    self.g_name := v_name;

    -- exit
    RETURN;

  END;

  /* getFirstName */
  /* --------------------------------------- */
  MEMBER FUNCTION getFirstName
    RETURN VARCHAR2
  IS
    v_parts NUMBER;
  BEGIN

    -- did we get a null on construct?
    IF self.g_first_name IS NOT NULL THEN
      RETURN self.g_first_name;
    END IF;

    -- how many spaces do we have?
    v_parts := LENGTH(self.g_name) - LENGTH(REPLACE(self.g_name,' ',''));

    -- if 0 spaces, return the name
    IF v_parts = 0 THEN
      RETURN self.g_name;
    -- else, return everything up to the space
    ELSE
      RETURN TRIM(SUBSTR(self.g_name,1, INSTR(self.g_name,' ',1) ));
    END IF;

  END getFirstName;

  /* getMiddleName */
  /* --------------------------------------- */
  MEMBER FUNCTION getMiddleName
    RETURN VARCHAR2
  IS
    v_parts  NUMBER;
    v_instr2 NUMBER;
    v_instr1 NUMBER;
  BEGIN

    -- did we get a null on construct?
    IF self.g_middle_name IS NOT NULL THEN
      RETURN NULL;
    END IF;

    -- how many spaces do we have?
    v_parts := LENGTH(self.g_name) - LENGTH(REPLACE(self.g_name,' ',''));

    -- if we have zero spaces, we only have a first name, return null
    IF v_parts = 0 THEN
      RETURN NULL;
    -- don't do middle if we only have 1 space
    ELSIF v_parts = 1 THEN
      RETURN NULL;
    -- else, we've got more than one, so grab between space 1 and 2
    ELSE
      v_instr2 := INSTR(self.g_name,' ',1,2);
      v_instr1 := INSTR(self.g_name,' ',1,1);
      RETURN TRIM( SUBSTR(self.g_name, v_instr1, (v_instr2-v_instr1) ));
    END IF;

  END getMiddleName;

  /* getLastName */
  /* --------------------------------------- */
  MEMBER FUNCTION getLastName
    RETURN VARCHAR2
  IS
    v_parts  NUMBER;
  BEGIN

    -- did we get a null on construct?
    IF self.g_last_name IS NOT NULL THEN
      RETURN self.g_last_name;
    END IF;

    -- how many spaces do we have?
    v_parts := LENGTH(self.g_name) - LENGTH(REPLACE(self.g_name,' ',''));

    -- if we have zero spaces, we only have a first name, return 'Unknown'
    IF v_parts = 0 THEN
      RETURN 'Unknown';
    -- if have 1 space, the space on is the last name
    ELSIF v_parts = 1 THEN
      RETURN TRIM( SUBSTR(self.g_name, INSTR(self.g_name,' ',1,1), LENGTH(self.g_name)) );
    -- else, we've got more than one, go from 2 to end
    ELSE
      RETURN TRIM( SUBSTR(self.g_name, INSTR(self.g_name,' ',1,2), LENGTH(self.g_name)) );
    END IF;

  END getLastName;


END;
/
SHOW ERRORS
/

.

感谢您提供的任何建议。

【问题讨论】:

  • .getFirstName() 返回一个字符串(并且您不能在字符串上定义成员函数) - 它需要返回一个 parseName 对象才能具有链接请求的功能(这样就不会获得名)。为什么不能只把cleanString()当作普通函数,而做cleanString( parseName('John123 Doe').getFirstName() )
  • 我绝对可以。这可能就是我最终要走的路线。只是展示我的好奇心。 :) 我确实尝试过创建两个版本的 getFirstName,一个返回 self,一个返回 VARCHAR2,但 Oracle 也不喜欢这样。我收到了错误:ORA-22806: not an object or REF
  • 你绝对可以在 PL/SQL 中链接这样的调用(MT0 已经在他的回答中展示了它)。关键是您必须始终遵循数据类型,并且不要尝试在错误的数据类型上调用函数(任何类型的 - 无论是“标准”或用户定义的,类型的成员或其他)。在您的问题中,另一个观察结果是 MEMBER 函数只能在它所属的类型上定义;否则它不是“成员”函数。

标签: oracle function plsql types member


【解决方案1】:

我想要第四个成员函数:cleanString,它会删除所有不在 A-Z 或 a-z 之间的字符。

调用示例:

SELECT parseName('John123 Doe').getFirstName().cleanString()
FROM dual;

当您调用.getFirstName() 时,期望它返回人的名字(毕竟这是成员函数的名称所说的),并且该名称是VARCHAR2 数据类型。 VARCHAR2 是一种原始数据类型,不是可以(以某种方式)扩展为具有 .cleanString() 成员函数的对象。

您可以定义cleanString() 函数:

CREATE FUNCTION cleanString( value VARCHAR2 ) RETURN VARCHAR2
IS
BEGIN
  RETURN REGEXP_REPLACE( value, '[^[:alpha:]]+' );
END cleanString;
/

然后调用:

 SELECT cleanString( parseName('John123 Doe').getFirstName() )
 FROM   DUAL;

或者您可以创建一个成员函数,在返回名称之前清除它们:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE OR REPLACE TYPE parseName AS OBJECT
(
    g_name         VARCHAR2(255),
    g_first_name   VARCHAR2(255),
    g_middle_name  VARCHAR2(1),
    g_last_name    VARCHAR2(255),
    -- constructor function
    CONSTRUCTOR FUNCTION parseName(p_name IN VARCHAR2) RETURN self AS result,
    -- member functions
    MEMBER FUNCTION cleanNames    RETURN parseName,
    MEMBER FUNCTION getFirstName  RETURN VARCHAR2,
    MEMBER FUNCTION getMiddleName RETURN VARCHAR2,
    MEMBER FUNCTION getLastName   RETURN VARCHAR2
);
/

CREATE OR REPLACE TYPE BODY parseName IS
  -- populateValues
  CONSTRUCTOR FUNCTION parseName(p_name IN VARCHAR2) RETURN self AS result
  IS
  BEGIN
    g_name := TRIM(p_name);
    g_first_name  := REGEXP_SUBSTR( g_name, '^(\S+)\s+((\S*?)\s+)?(.*)$', 1, 1, NULL, 1 );
    g_middle_name := REGEXP_SUBSTR( g_name, '^(\S+)\s+((\S*?)\s+)?(.*)$', 1, 1, NULL, 3 );
    g_last_name   := REGEXP_SUBSTR( g_name, '^(\S+)\s+((\S*?)\s+)?(.*)$', 1, 1, NULL, 4 );
    RETURN;
  END;

  MEMBER FUNCTION cleanNames RETURN parseName
  IS
    v_name parseName := SELF;
  BEGIN
    v_name.g_first_name  := cleanString( SELF.g_first_name );
    v_name.g_middle_name := cleanString( SELF.g_middle_name );
    v_name.g_last_name   := cleanString( SELF.g_last_name );
    RETURN v_name;
  END;

  MEMBER FUNCTION getFirstName RETURN VARCHAR2
  IS
  BEGIN
    RETURN g_first_name;
  END getFirstName;

  MEMBER FUNCTION getMiddleName RETURN VARCHAR2
  IS
  BEGIN
    RETURN g_middle_name;
  END getMiddleName;

  MEMBER FUNCTION getLastName RETURN VARCHAR2
  IS
  BEGIN
    RETURN g_last_name;
  END getLastName;
END;
/

查询 1

SELECT parseName('John123 Doe').getFirstName(),
       parseName('John123 Doe').cleanNames().getFirstName()
FROM DUAL

Results

| PARSENAME('JOHN123DOE').GETFIRSTNAME() | PARSENAME('JOHN123DOE').CLEANNAMES().GETFIRSTNAME() |
|----------------------------------------|-----------------------------------------------------|
|                                John123 |                                                John |

【讨论】:

  • 后者(一个在拆分之前清理输入的函数 - 并返回与其输入相同的数据类型)似乎非常接近 OP 的要求。
  • 效果很好!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-19
  • 1970-01-01
  • 2020-08-21
  • 2010-11-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多