列出所有Postgres枚举数组值,并将其与记录中所应用的值组合在一起,显示是否已应用状态

时间:2019-02-17 18:37:31

标签: arrays postgresql enums compositetype

最近,我重新发现了Postgres用户定义的类型和数组,这让我感到Postgres早在该术语流行之前就已经是NoSQL(不仅是SQL)。

需要选择一个枚举值列数组以显示所有枚举值和一个标记,如果枚举数组列包含枚举值或排序不正确(首先按列应用枚举值排序,然后按枚举值顺序(如果未将枚举值包含在列中)

DDL示例为:

-- available website settings enum:
CREATE TYPE website_menu_extras_type AS ENUM ( 'logo', 'emails', 'locations', 'phones', 'search' );

-- this type represents if website setting is enabled or not (the setting is treated as enabled if it's included to a website's column `menu_extras` of type `website_menu_extras_type[]`):
CREATE TYPE website_menu_extras_with_defaults AS ( menu_extra website_menu_extras_type, is_enabled BOOLEAN );

-- website table which contains applied `website_menu_extras_type` values as array:
CREATE TABLE website ( ID serial PRIMARY KEY, menu_extras website_menu_extras_type [] );

-- insert some values:
INSERT INTO website ( menu_extras )
VALUES
    ( ARRAY [ 'logo', 'emails' ]:: website_menu_extras_type [] );

-- select `menu_extras` as applied values 
-- and `menu_extras_with_defaults` which is an array 
-- of website_menu_extras_with_defaults having 
-- all values of `website_menu_extras_type` enum
-- and `is_enabled` set to true if `menu_extras` includes enum value
-- and `is_enabled` set to false if `menu_extras` does not include enum value
-- `menu_extras_with_defaults` should be sorted by `menu_extras` order 
-- and then by `website_menu_extras_type` enum order if `menu_extras` didn't include the enum value
SELECT
    id, menu_extras, menu_extras as menu_extras_with_defaults
FROM
    website;

可能会有大量的网站记录,将来大部分都会读取,并且设置枚举将在将来扩展,因此,将设置“包含”到网站记录中看起来比使用多对多更好的解决方案桌子。

我已经开始使用UDF(例如,它不会像预期的那样工作),

  CREATE FUNCTION website_menu_extras_with_defaults ( website website ) RETURNS website_menu_extras_with_defaults[] AS $$ 
  WITH
    all_enum_values
    AS
    (
      SELECT UNNEST
        (
        enum_range ( NULL
     :: website_menu_extras_type )) AS val 
    ),
    all_enum_values1 AS
  (
    SELECT UNNEST
        (
        enum_range ( NULL
  :: website_menu_extras_type )) AS val 
    )
  -- select * from x1
  SELECT

    array[( val, FALSE ), (val, TRUE)]
  :: website_menu_extras_with_defaults []
FROM
    all_enum_values
-- where val in (website).menu_extras
    $$ LANGUAGE SQL STABLE;

我无法按预期进行工作。

在这种情况下,您如何获得正确的menu_extras_with_defaults值?

1 个答案:

答案 0 :(得分:1)

unnest(...) WITH ORDINALITY是您的朋友在这里:

CREATE OR REPLACE FUNCTION website_menu_extras_with_defaults(website website)
   RETURNS website_menu_extras_with_defaults[]
   LANGUAGE sql AS
$$SELECT array_agg(
            ROW(et.e, me.num IS NOT NULL)::website_menu_extras_with_defaults
               ORDER BY me.num, et.num
         )
FROM unnest(enum_range(NULL::website_menu_extras_type)) WITH ORDINALITY AS et(e, num)
   LEFT JOIN unnest(website.menu_extras) WITH ORDINALITY AS me(e, num)
      USING (e)$$;

让我在这里添加一两个建议。

即使PostgreSQL支持复合数据类型和数组,您也不应在任何地方开始使用它们。

这里不需要使用数组和复合类型。 website可能也只有id,而menu_extras应该是引用website的第二张表。

此外,除非确定值的范围不会改变,否则不要使用enum类型。例如,您不能从enum类型中删除值。最好使用常规查找表。

我预计,如果像这样更改数据模型,编写所需的函数会容易得多。

在少数情况下,非原子数据类型可能是一个强大的工具,但如果不加选择地使用它们,则会引起很多痛苦。