【问题标题】:Select if any value in an array is the beginning of a column's value选择数组中的任何值是否是列值的开头
【发布时间】:2017-11-13 19:35:24
【问题描述】:

所以我从 Python 网络服务器获得了一个数组 codes,我需要选择所有行,其列与这些 codes 中的任何一个匹配:

SELECT a.*
FROM a
LEFT JOIN b ON b.code = a.bcode
WHERE b.code = ANY(codes);

但问题是,这些代码可能只是部分代码,只是代码的开头。因此,虽然完整的代码总是 5 个字母长(它们存储为字符串),但我收到的代码数组可能类似于 ['01', '4332', '34443']。如果b 中的一行将'01233' 作为列的值,它应该与数组中的'01' 匹配。请注意,它不会匹配到'23',它应该只匹配到开头。

本质上我想要这样的东西:

SELECT a.*
FROM a
LEFT JOIN b ON b.code = a.bcode
WHERE b.code LIKE ANY(codes || '%');

显然附加到 codes || '%' 这样的数组是不合法的,那我该怎么做呢?

【问题讨论】:

  • 获得部分匹配是否总是可以,还是应该完全匹配 5 个字符的代码?这些列中的数据也是文本、整数等吗?
  • @eurotrash 我不确定你的第一个问题是什么意思。我存储的代码长度为 5 个字符,因此 '01234%' 可以作为部分代码,因为它只会匹配 '01234'。数据始终以文本形式出现,无处不在,因为前导零很重要(即0412341230,只有后者会部分匹配4123)。
  • @eurotrash “部分”是指匹配开头。所以代码中有'01'是不够的,它必须以'01'开头(假设我在输入数组中有'01'
  • WHERE b.code LIKE ANY(select unnest(codes) || '%');

标签: sql arrays postgresql postgresql-9.5


【解决方案1】:

可能有更好的方法来做到这一点。但一般概念对我来说是有意义的:如果项目的长度小于 5,则更改数组以附加 % 字符。

SELECT [...]
FROM [...] x
WHERE x.code LIKE ANY(ARRAY(
    SELECT CASE WHEN LENGTH(u) < 5 THEN u || '%' ELSE u END
    FROM UNNEST([your array]) u)
);

工作示例:

SELECT g
FROM generate_series(9995, 10005) g
WHERE g::TEXT LIKE ANY(ARRAY(
    SELECT CASE WHEN LENGTH(u) < 5 THEN u || '%' ELSE u END
    FROM UNNEST(ARRAY['999','10005']) u)
);

结果:

9995
9996
9997
9998
9999
10005

【讨论】:

    【解决方案2】:

    这个小功能可以完成这项工作:

    create or replace function array_for_like(text[])
    returns text[] language sql as $$
        select string_to_array(concat(array_to_string($1, '%,'), '%'), ',')
    $$;
    

    例子:

    with my_table(code) as (
    values
        ('10001'),
        ('10002'),
        ('20001'),
        ('20002'),
        ('30001')
    )
    
    select *
    from my_table
    where code like any(array_for_like(array['10', '30']))
    
     code  
    -------
     10001
     10002
     30001
    (3 rows)
    

    【讨论】:

      【解决方案3】:

      您需要弄清楚如何将数组传递给查询。显然,您可以在 Python 中构造一个WHERE 子句并执行以下操作:

      where code like '01%', or
            code like '4332%' or 
            code like '34443%'
      

      您可以将其缩短为正则表达式:

      where code ~ '^(01|4332|34443)'
      

      或者,您可以将其作为 Postgres 数组传入,执行如下操作:

      select v.*, p2.pat
      from (values ('a'), ('abc'), ('abcd'), ('bacd')) v(x) cross join
           (select array['a', 'b'] as pat) p, lateral
           unnest(p.pat) p2(pat)
      where v.x like p2.pat || '%';
      

      如果您想返回匹配的模式,使用 Postgres 数组特别有用。

      【讨论】:

        猜你喜欢
        • 2022-06-21
        • 2011-12-21
        • 2020-10-13
        • 2011-05-29
        • 1970-01-01
        • 2014-07-26
        • 2015-06-30
        • 2019-02-12
        • 1970-01-01
        相关资源
        最近更新 更多