【问题标题】:RegEx for PostgreSQL 'interval' functionPostgreSQL '间隔' 函数的正则表达式
【发布时间】:2021-07-11 06:09:07
【问题描述】:

我需要对将在我的服务器端使用的值实施正则表达式验证,以获取某些时间戳比now() - interval 'myValue' 更早(更小)的数据。

pSQL区间函数解释here,简而言之它可以有2 days3 years12 hours等值,但也可以嵌套更多不同的值,如2 days 6 hours 30 minutes等。

我目前有一个正则表达式/^\d+\s(seconds?|minutes?|hours?|days?|weeks?|months?|years?)$/i,它只接受一个值(例如2 days),但不知道如何允许多个值,并设置一个规则,该组中的某个字符串只能重复一次或一次都没有。

这个正则表达式 /^\d+\s(seconds?|minutes?|hours?|days?|weeks?|months?|years?)(\s\d+\s(seconds?|minutes?|hours?|days?|weeks?|months?|years?))*$/i 允许嵌套,但也允许重复值,例如2 days 12 hours 6 hours 2 minutes 这将导致 pSQL 查询出现致命错误。

我尝试使用 \1{0,1} 正则表达式运算符组合来限制该组中值的重复,但我无法准确地确定它。

注意:不幸的是,正则表达式是我验证此值的唯一方法,因为我无权访问接收此值的服务器端控制器,也无权访问此表单的客户端前端。我不能只是抛出异常或跳过查询,因为它是重要的 cron-job 的一部分,并且必须始终保持稳定。

(我只能访问这个值的 json 模式,因此只能为其定义正则表达式模式)

感谢您的帮助。

【问题讨论】:

  • 试试^(?!$)(?:\d+\s+years?)?(?:(?:\s+|^)\d+\s+months?)?(?:(?:\s+|^)\d+\s+weeks?)?(?:(?:\s+|^)\d+\s+days?)?(?:(?:\s+|^)\d+\s+hours?)?(?:(?:\s+|^)\d+\s+minutes?)?(?:\s+\d+\s+seconds?)?$
  • 它适用于4352 YEARS 2 weeks 4 days 5 days 2 minutes 30 second 的情况(两个days 值彼此相邻),但当您在较小的值之后插入“较大”值时不允许使用大小写,例如4352 YEARS 2 weeks 4 days 2 minutes 30 second 3 months(请注意,我在末尾插入了几个月,这将在 pSQL 中工作,但这个正则表达式会阻止它)
  • 那个正则表达式does not allow4352 YEARS 2 weeks 4 days 5 days 2 minutes 30 second
  • 是的,我知道。我写了“它有效”,我的意思是它不允许该值:) 但它也不允许它应该允许的4352 YEARS 2 weeks 4 days 2 minutes 30 second 3 months
  • 现在完美运行,干得好,非常感谢!您可以将其发布为答案,我将其标记为正确。

标签: regex postgresql


【解决方案1】:

你可以使用

^(?!.*(second|minute|hour|day|week|month|year).*\1)\d+\s+(?:second|minute|hour|day|week|month|year)s?(?:\s+\d+\s+(?:second|minute|hour|day|week|month|year)s?)*$

regex demo

详情

  • ^ - 字符串开头
  • (?!.*(second|minute|hour|day|week|month|year).*\1) - 没有 second, minute, hour day, week, monthoryear` 字符串在整个字符串中允许重复
  • \d+\s+(?:second|minute|hour|day|week|month|year)s? - 1 个或多个数字,一个或多个空格,然后是 secondminutehourdayweekmonthyear,然后是可选的 @987654339 @信
  • (?:\s+\d+\s+(?:second|minute|hour|day|week|month|year)s?)* - 一个或多个空格的零次或多次重复,遵循上述模式
  • $ - 字符串结束。

【讨论】:

    【解决方案2】:

    算了。 interval 支持的值的唯一完整文档是 the implementation(胆量在 ParseDateTime 中)。

    考虑这些:

    SELECT INTERVAL '12 00:12:00';
    
         interval     
    ══════════════════
     12 days 00:12:00
    (1 row)
    
    SELECT INTERVAL '12 d 12 mins';
    
         interval     
    ══════════════════
     12 days 00:12:00
    (1 row)
    
    SELECT INTERVAL '3-2';
    
        interval    
    ════════════════
     3 years 2 mons
    (1 row)
    

    我要做的就是编写一个函数,将字符串转换为interval 并捕获并报告错误:

    CREATE FUNCTION interval_ok(text) RETURNS boolean
       LANGUAGE plpgsql AS
    $$BEGIN
       PERFORM CAST ($1 AS interval);
       RETURN TRUE;
    EXCEPTION
       WHEN invalid_datetime_format THEN
          RETURN FALSE;
    END;$$;
    

    【讨论】:

    • 感谢您的建议,但不幸的是,我的客户想要插入他们想要的任何值,但我要阻止他们插入垃圾:)。正如我所提到的,这个值是由 cron 作业运行的查询的一部分,该作业删除了某些较旧的值由客户端插入的值,并且查询在任何时候都不能失败:(
    • 嗯,这是自相矛盾的:你必须阻止他们插入垃圾,但它不能在任何时候失败?如果他们插入垃圾,它必须失败。因此,您运行他们通过该函数插入的值,并且您只接受好的值。在这种情况下,这比正则表达式要好。
    • 这怎么自相矛盾?由他们来定义时间范围,这是他们的要求。但是我有责任确保它顺利运行并且永不失败,因为我无法确定他们在输入值时在计算机上做了什么,他们可以复制过去的垃圾并意外插入 2 个重复的值并查询分裂。上面由@Wiktor Stribiżew 发布的正则表达式正是这样做的。
    • 好的,那就不用担心了。你有你的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2022-11-12
    • 2020-05-25
    • 1970-01-01
    • 2022-07-18
    • 2013-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多