【问题标题】:How to add an unique constraint (ignoring special characters) on a text column in postgres?如何在 postgres 的文本列上添加唯一约束(忽略特殊字符)?
【发布时间】:2020-01-13 22:23:55
【问题描述】:

如何在 Postgres 的文本列上添加唯一约束(忽略特殊字符)?

CREATE TABLE my_table(
    SomeTextColumn citext
CONSTRAINT person_u_1 UNIQUE (SomeTextColumn)
);

在上表中,我试图添加一个唯一性约束,通过忽略传入数据中的特殊字符来寻找唯一性

For example:
1. HelloWorld --> Gets inserted successfully
2. Hello World --> Should fail with duplicate constraint
2. Hello%$^&*W^%orld --> Should fail with duplicate constraint

【问题讨论】:

    标签: sql regex postgresql unique-constraint aws-serverless


    【解决方案1】:

    您可以创建一个实现检查的unique 索引:

    create unique index t_txt_unique on t(regexp_replace(txt, '\W', '', 'g'));
    

    正则表达式从字符串中删除所有非单词字符,只保留字母数字字符和取消标记的_。您可以根据需要调整字符类。

    Demo on DB Fiddle

    create table t (id int, txt citext);
    create unique index t_txt_unique on t(regexp_replace(txt, '\W', '', 'g'));
    
    insert into t values(1, 'HelloWorld');
    -- ok
    
    insert into t values(1, 'Hello World');
    -- ERROR:  duplicate key value violates unique constraint "t_txt_unique"
    -- DETAIL:  Key (regexp_replace(txt, '\W'::text, ''::text, 'g'::text))=(HelloWorld) already exists.
    
    insert into t values(1, 'Hello%$^&*W^%orld');
    -- ERROR:  duplicate key value violates unique constraint "t_txt_unique"
    -- DETAIL:  Key (regexp_replace(txt, '\W'::text, ''::text, 'g'::text))=(HelloWorld) already exists.
    
    insert into t values(1, 'Hello Mars');
    -- ok
    

    【讨论】:

    • 太棒了!谢谢!
    【解决方案2】:

    这个问题比较老,但我认为一些额外的注释可能有用......

    • TEXT 的唯一性总是存在问题,因为文本区分大小写(不仅在 PostgreSQL 中)。
      您可能会得到重复,因为“HelloWorld”与“HELLOWORLD”不同。因此,如果您在数据库的文本字段上创建 UNIQUE INDEX,您可能需要添加 UPPER 函数:
      CREATE UNIQUE INDEX t_txt_unique ON t(REGEXP_REPLACE(UPPER(txt), '\W', '', 'g'));
    • 您可能希望使用以下 REGEXP_REPLACE 选项仅保留字符 0-9 和 A-Z(删除变音符号):
      CREATE UNIQUE INDEX t_txt_unique ON t(REGEXP_REPLACE(UPPER(txt), '[^0-9A-Z]', '\1', 'g'));
    • 如果您想将变音符号保留为一般字符(Ü --> U、Ä --> A 等),您可以输入 UNACCENT 函数(https://www.postgresql.org/docs/current/unaccent.html - 这是您需要添加到 PostgreSQL 的扩展,但是我还可以帮助您搜索某些内容 - 请记住,只有超级用户或其他管理员用户可能能够/允许添加扩展):
      CREATE EXTENSION IF NOT EXISTS unaccent WITH SCHEMA public;
      CREATE UNIQUE INDEX t_txt_unique ON t(REGEXP_REPLACE(UPPER(UNACCENT(txt)), '[^0-9A-Z]', '\1', 'g'));

    SQL 代码

    -- -- 检查 REGEXP_REPLACE 以删除不需要的字符。 -- 测试字符串:'^°!"§$%&/()=?`+#äöüÜÖÄ',.-_:;@ABµC123abc123' -- SELECT '^°!"§$%&/()=?`+#äöüÜÖÄ'',.-_:;@ABµC123abc123' AS original_text ,REGEXP_REPLACE( UPPER( '^°!"§$%&/()=?`+#äöüÜÖÄ'',.-_:;@ABµC123abc123' ), '\W', '', 'g') AS replace_word ,REGEXP_REPLACE( UPPER( '^°!"§$%&/()=?`+#äöüÜÖÄ'',.-_:;@ABµC123abc123' ), '[^0-9A-Z]', '\1 ', 'g') AS replace_upper_keep_num_a_to_z ,REGEXP_REPLACE( UPPER(UNACCENT( '^°!"§$%&/()=?`+#äöüÜÖÄ'',.-_:;@ABµC123abc123' )), '[^0-9A-Z]', '\1', 'g') AS replace_keep_num_a_to_z_umlaut ;

    上述 SQL 代码的结果为 HTML 表格 最终结果中的降价表很复杂(预览版正在运行)。

    <table border="1"><tr BGCOLOR="#CCCCFF"><th>original_text</th><th>replace_word</th><th>replace_upper_keep_num_a_to_z</th><th>replace_keep_num_a_to_z_umlaut</th></tr>
    <tr><td>^°!&quot;§$%&amp;/()=?`+#äöüÜÖÄ',.-_:;@&lt;&gt;ABµC123abc123</td><td>ÄÖÜÜÖÄ_AB?C123ABC123</td><td>ABC123ABC123</td><td>AOUUOAABC123ABC123</td></tr>
    </table>

    您可能已经注意到,使用 '\W' 替换将第二列中的字符“µ”转换为大写“M”,这有点奇怪。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多