【问题标题】:Using regex in PSQL Case Statement在 PSQL 案例语句中使用正则表达式
【发布时间】:2021-07-02 19:51:53
【问题描述】:

使用 postgresql 开发 Rails 应用程序。

我正在尝试使用 case 语句来实现 pg 约束。

          ALTER TABLE geolocation_postcodes
        ADD CONSTRAINT canadian_format CHECK (
          CASE WHEN country_code = 'CA' AND code !~ '\A^[A-Z]\d[A-Z]\s\d[A-Z]\d\Z' THEN FALSE
          END
          );

当尝试插入一个实例(应该通过)时,它每次都会被约束停止:

输入:

Geolocation::Postcode.insert(code: "G6V 4C9", country_code: "CA", created_at: Time.now, updated_at: Time.now)

结果:

ActiveRecord::StatementInvalid: PG::CheckViolation: ERROR:  new row for relation "geolocation_postcodes" violates check constraint "canadian_format" DETAIL:  Failing row contains (46, G6V 4C9, t, 2021-04-07 01:37:34.005891, 2021-04-07 01:37:34.005896, CA).

【问题讨论】:

  • 您真正希望对国家/地区代码进行过滤的逻辑是什么?
  • 邮政编码将来自不同的国家,它们有不同的格式,因此有不同的限制。因此,如果国家/地区是加拿大(国家代码的“CA”值),则根据 canadian_format 约束检查格式。所以基本上,如果 country_code 是“CA”,则检查代码(IE. G5V 4C9)以查看它是否与正则表达式匹配。如果失败,记录会反弹。
  • 我已经在下面给出了答案。基本上,您的检查约束应该说明积极接受的案例是什么。你甚至不需要CASE 表达式。

标签: sql ruby-on-rails ruby postgresql


【解决方案1】:

这里有几个问题:

  1. 您的正则表达式并没有按照您的想法执行。
  2. 您只包含加拿大邮政编码(但这可能是故意的)。
  3. 我怀疑您的 CHECK 约束未正确访问数据库。

(1) 您的正则表达式正在尝试同时使用\A(字符串开头)和^(行开头)。你只想要其中一个,我会选择 \A 以与 Ruby 中的 \Z\A 保持一致:

\A[A-Z]\d[A-Z]\s\d[A-Z]\d\Z

(2) 您可以通过将约束重命名为 postal_code_format 之类的名称并重组如下逻辑来解决此问题:

code ~ case country_code
       when 'CA' then '\A[A-Z]\d[A-Z]\s\d[A-Z]\d\Z'
       when 'US' then ...
       end

这种结构使 SQL case 看起来像一个 Ruby 哈希,并将所有内容放在一起且易于阅读。

(3) 这可能是您问题的根源。我猜你在迁移中有这样的事情:

connection.execute(%Q(
  ALTER TABLE geolocation_postcodes
  ADD CONSTRAINT canadian_format CHECK (
    CASE WHEN country_code = 'CA' AND code !~ '\A^[A-Z]\d[A-Z]\s\d[A-Z]\d\Z' THEN FALSE
    END
  );
))

或 heredoc 或其他双引号字符串。所有这些字符串都将解释反斜杠,因此\A 最终会变成A\d 会变成d,......当你的正则表达式进入数据库时​​,它看起来像:

A^[A-Z]d[A-Z] d[A-Z]dZ

你的 CHECK 会失败。

如果你使用单引号字符串和正确的正则表达式,事情应该没问题:

connection.execute(%q(
  alter table geolocation_postcodes
  add constraint postal_code_format check (
    code ~ case country_code
           when 'CA' then '\A[A-Z]\d[A-Z]\s\d[A-Z]\d\Z'
           end
  )
))

【讨论】:

  • 非常感谢您花时间回答。非常明确的答案。我能够解决我的问题,改进我的代码并学习。我想知道为什么 \d 不能用于数字...
  • @TimBiegeleisen 这与 Oracle 有什么关系?
  • 所以...我在 Rails 迁移中使用 psql 正则表达式进行了一系列测试。我无法正确转义这些字母,因此 \d 在数据库中显示为 d。我试过单引号和双引号。有什么建议吗?
  • 我的答案中的%q(...) 版本应该可以工作。 %q(...) 就像 Ruby 中的单引号字符串一样工作,但允许您包含单引号而不转义它们。
  • 我可能不得不坚持 [0-9] 而不是 \d
【解决方案2】:

检查约束内的逻辑应该用于肯定匹配情况。所以我会在这里使用:

ALTER TABLE geolocation_postcodes
ADD CONSTRAINT canadian_format CHECK (
    (country_code = 'CA' AND code ~ '\A[A-Z][0-9][A-Z]\s[0-9][A-Z][0-9]\Z') OR
    -- other possible matching country codes here
);

【讨论】:

  • 非常感谢您的回答。
猜你喜欢
  • 2016-12-23
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-23
  • 1970-01-01
相关资源
最近更新 更多