创建一个接受多个数据类型的equals(...)函数?

发布于 2025-02-12 18:43:26 字数 1430 浏览 1 评论 0 原文

作为一个实验,我有兴趣编写一个自定义等于(..)函数,该功能模仿是[不是]与某些数据库中存在的功能不同(但是不是Oracle)。

null-ware比较: is \ [not \]独特来自

...提供了一个比较操作员,将两个无效值视为 相同。

Custom 等于(...)函数可能会这样工作:

  1. 将两个表达式传递给可以相互比较的函数。
  2. 该函数将使用一些逻辑(TBD),如果两个值相同,则将返回 true ,如果两个值不同,则 false
    • 两个零值将被视为相同。
    • 可以将nulls与非效果进行比较。例如,'a'vs. null 将返回 equals = false ,未知。

作为新手,这似乎是一个有趣的主意。

话虽如此,我不知道我如何创建一个能够接受我可能会投入的所有不同数据类型的函数。

通常,我会写下这样的函数:

with function equals(v_text varchar2) return varchar2 --'SAME' or 'DIFFERENT'

但这仅适用于单个数据类型,而不是多个数据类型。


问题:

有没有办法创建一个接受多个数据类型的函数? (考虑到等于(...)函数>函数想法)

我想Oracle没有创建这样的内置函数可能是一个完全很好的理由。我只是想通过实验学习&问问题。


相关:

As an experiment, I'm interested in writing a custom equals(..) function that would mimic the is [not] distinct from functionality that exists in some databases (but not Oracle).

NULL-Aware Comparison: is \[not\] distinct from

...provides a comparison operator that treats two null values as
the same.

The custom equals(...) function would possibly work like this:

  1. Pass two expressions to the function that would be compared against each other.
  2. The function would use some logic (TBD) that would return true if the two values are the same, and false if the two values are different.
    • Two nulls would be treated as being the same.
    • Nulls could be compared to non-nulls. For example, 'A' vs. null would return equals = false, not unknown.

As a novice, that seems like an interesting idea.

With that said, I don't know how I'd create a function that would be capable accepting all the different datatypes that I might throw at it.

Normally, I'd write a function like this:

with function equals(v_text varchar2) return varchar2 --'SAME' or 'DIFFERENT'

But that only works for a single datatype, not multiple datatypes.


Question:

Is there a way to create a function that accepts multiple datatypes? (with the equals(...) function idea in mind)

I imagine there might be a perfectly good reason why Oracle hasn't created a built-in function like that. I'm just trying to learn-by-experimenting & asking questions.


Related:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尝蛊 2025-02-19 18:43:26

您可以使用自定义聚合函数来汇总所有从共享超级类型继承的用户定义类型。

适应我在这里的答案

如果您有类型:

CREATE TYPE parent_t IS OBJECT(
  a NUMBER
) NOT FINAL;

CREATE TYPE child_t UNDER parent_t(
  b NUMBER
);

并且您想将它们汇总到唯一列表中:

CREATE OR REPLACE TYPE parent_tab IS TABLE OF parent_t;
/

然后您可以声明用户定义的聚合类型:

CREATE OR REPLACE TYPE ParentsTableUnion AS OBJECT(
  list parent_tab,

  STATIC FUNCTION is_equal(
    v_left  IN parent_t,
    v_right IN parent_t
  ) RETURN BOOLEAN,

  STATIC PROCEDURE merge(
    v_cur IN OUT parent_tab,
    v_new IN     parent_tab
  ),

  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT ParentsTableUnion
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT ParentsTableUnion,
    value       IN     parent_t
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT ParentsTableUnion,
    returnValue    OUT parent_tab,
    flags       IN     NUMBER
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT ParentsTableUnion,
    ctx         IN OUT ParentsTableUnion
  ) RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY ParentsTableUnion
IS
  STATIC FUNCTION is_equal(
    v_left  IN parent_t,
    v_right IN parent_t
  ) RETURN BOOLEAN
  IS
  BEGIN
    IF v_left.a = v_right.a
    OR (v_left.a IS NULL AND v_right.a IS NULL)
    THEN
      IF    v_left  IS NOT OF (child_t)
      AND   v_right IS NOT OF (child_t)
      THEN
        RETURN TRUE;
      ELSIF v_left  IS OF (child_t)
      AND   v_right IS OF (child_t)
      AND   (  TREAT(v_left AS child_t).b = TREAT(v_right AS child_t).b
            OR (TREAT(v_left AS child_t).b IS NULL AND TREAT(v_right AS child_t).b IS NULL)
            )
      THEN
        RETURN TRUE;
      END IF;      
    END IF;
    RETURN FALSE;
  END;
  
  STATIC PROCEDURE merge(
    v_cur IN OUT parent_tab,
    v_new IN     parent_tab
  )
  IS
    v_cnt PLS_INTEGER := v_cur.COUNT;
  BEGIN
    <<next_value>>
    FOR i IN 1 .. v_new.COUNT LOOP
      FOR j IN 1 .. v_cnt LOOP
        IF ParentsTableUnion.is_equal(v_new(i), v_cur(j))
        THEN
          CONTINUE next_value;
        END IF;
      END LOOP;
      v_cur.EXTEND(1);
      v_cur(v_cur.COUNT) := v_new(i);
    END LOOP;
  END;
  
  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT ParentsTableUnion
  ) RETURN NUMBER
  IS
  BEGIN
    ctx := ParentsTableUnion( NULL );
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT ParentsTableUnion,
    value       IN     parent_t
  ) RETURN NUMBER
  IS
  BEGIN
    IF value IS NULL THEN
      NULL;
    ELSIF self.list IS NULL THEN
      self.list := parent_tab(value);
    ELSE
      ParentsTableUnion.merge(self.list, parent_tab(value));
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT ParentsTableUnion,
    returnValue    OUT parent_tab,
    flags       IN     NUMBER
  ) RETURN NUMBER
  IS
  BEGIN
    returnValue := self.list;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT ParentsTableUnion,
    ctx         IN OUT ParentsTableUnion
  ) RETURN NUMBER
  IS
    v_cnt PLS_INTEGER;
  BEGIN
    IF self.list IS NULL THEN
      self.list := ctx.list;
    ELSIF ctx.list IS NULL THEN
      NULL;
    ELSE
      ParentsTableUnion.merge(self.list, ctx.list);
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;
END;
/

然后,您可以将其包装在用户定义的聚合函数中:

CREATE FUNCTION PARENT_T_UNION( list parent_t )
RETURN parent_tab
PARALLEL_ENABLE AGGREGATE USING ParentsTableUnion;
/

然后,如果您使用以下方式调用它,

WITH data (value) AS (
  SELECT parent_t(1)  FROM DUAL UNION ALL
  SELECT parent_t(2)  FROM DUAL UNION ALL
  SELECT parent_t(1)  FROM DUAL UNION ALL
  SELECT parent_t(3)  FROM DUAL UNION ALL
  SELECT child_t(1,1) FROM DUAL UNION ALL
  SELECT child_t(1,2) FROM DUAL UNION ALL
  SELECT child_t(2,1) FROM DUAL UNION ALL
  SELECT child_t(1,1) FROM DUAL
)
SELECT u.a,
       TREAT(VALUE(u) AS child_t).b AS b
FROM   TABLE( ( SELECT PARENT_T_UNION(value) FROM data ) ) u

则输出为:

a b
1 null
2 null
3 null
1 1
1 2
2 1

db&lt;&gt; fiddle noreflowl noreferrer“

You can use a custom aggregation function to aggregate user-defined types that all inherit from a shared super-type.

Adapting my answer here:

If you have the types:

CREATE TYPE parent_t IS OBJECT(
  a NUMBER
) NOT FINAL;

CREATE TYPE child_t UNDER parent_t(
  b NUMBER
);

and you want to aggregate them into a unique list into the type:

CREATE OR REPLACE TYPE parent_tab IS TABLE OF parent_t;
/

Then you can declare the user-defined aggregation type:

CREATE OR REPLACE TYPE ParentsTableUnion AS OBJECT(
  list parent_tab,

  STATIC FUNCTION is_equal(
    v_left  IN parent_t,
    v_right IN parent_t
  ) RETURN BOOLEAN,

  STATIC PROCEDURE merge(
    v_cur IN OUT parent_tab,
    v_new IN     parent_tab
  ),

  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT ParentsTableUnion
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT ParentsTableUnion,
    value       IN     parent_t
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT ParentsTableUnion,
    returnValue    OUT parent_tab,
    flags       IN     NUMBER
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT ParentsTableUnion,
    ctx         IN OUT ParentsTableUnion
  ) RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY ParentsTableUnion
IS
  STATIC FUNCTION is_equal(
    v_left  IN parent_t,
    v_right IN parent_t
  ) RETURN BOOLEAN
  IS
  BEGIN
    IF v_left.a = v_right.a
    OR (v_left.a IS NULL AND v_right.a IS NULL)
    THEN
      IF    v_left  IS NOT OF (child_t)
      AND   v_right IS NOT OF (child_t)
      THEN
        RETURN TRUE;
      ELSIF v_left  IS OF (child_t)
      AND   v_right IS OF (child_t)
      AND   (  TREAT(v_left AS child_t).b = TREAT(v_right AS child_t).b
            OR (TREAT(v_left AS child_t).b IS NULL AND TREAT(v_right AS child_t).b IS NULL)
            )
      THEN
        RETURN TRUE;
      END IF;      
    END IF;
    RETURN FALSE;
  END;
  
  STATIC PROCEDURE merge(
    v_cur IN OUT parent_tab,
    v_new IN     parent_tab
  )
  IS
    v_cnt PLS_INTEGER := v_cur.COUNT;
  BEGIN
    <<next_value>>
    FOR i IN 1 .. v_new.COUNT LOOP
      FOR j IN 1 .. v_cnt LOOP
        IF ParentsTableUnion.is_equal(v_new(i), v_cur(j))
        THEN
          CONTINUE next_value;
        END IF;
      END LOOP;
      v_cur.EXTEND(1);
      v_cur(v_cur.COUNT) := v_new(i);
    END LOOP;
  END;
  
  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT ParentsTableUnion
  ) RETURN NUMBER
  IS
  BEGIN
    ctx := ParentsTableUnion( NULL );
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT ParentsTableUnion,
    value       IN     parent_t
  ) RETURN NUMBER
  IS
  BEGIN
    IF value IS NULL THEN
      NULL;
    ELSIF self.list IS NULL THEN
      self.list := parent_tab(value);
    ELSE
      ParentsTableUnion.merge(self.list, parent_tab(value));
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT ParentsTableUnion,
    returnValue    OUT parent_tab,
    flags       IN     NUMBER
  ) RETURN NUMBER
  IS
  BEGIN
    returnValue := self.list;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT ParentsTableUnion,
    ctx         IN OUT ParentsTableUnion
  ) RETURN NUMBER
  IS
    v_cnt PLS_INTEGER;
  BEGIN
    IF self.list IS NULL THEN
      self.list := ctx.list;
    ELSIF ctx.list IS NULL THEN
      NULL;
    ELSE
      ParentsTableUnion.merge(self.list, ctx.list);
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;
END;
/

Then you can wrap it in a user-defined aggregation function:

CREATE FUNCTION PARENT_T_UNION( list parent_t )
RETURN parent_tab
PARALLEL_ENABLE AGGREGATE USING ParentsTableUnion;
/

Then, if you call it using:

WITH data (value) AS (
  SELECT parent_t(1)  FROM DUAL UNION ALL
  SELECT parent_t(2)  FROM DUAL UNION ALL
  SELECT parent_t(1)  FROM DUAL UNION ALL
  SELECT parent_t(3)  FROM DUAL UNION ALL
  SELECT child_t(1,1) FROM DUAL UNION ALL
  SELECT child_t(1,2) FROM DUAL UNION ALL
  SELECT child_t(2,1) FROM DUAL UNION ALL
  SELECT child_t(1,1) FROM DUAL
)
SELECT u.a,
       TREAT(VALUE(u) AS child_t).b AS b
FROM   TABLE( ( SELECT PARENT_T_UNION(value) FROM data ) ) u

Then the output is:

A B
1 null
2 null
3 null
1 1
1 2
2 1

db<>fiddle here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文