最近,我重新发现了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
值?
答案 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
类型中删除值。最好使用常规查找表。
我预计,如果像这样更改数据模型,编写所需的函数会容易得多。
在少数情况下,非原子数据类型可能是一个强大的工具,但如果不加选择地使用它们,则会引起很多痛苦。