Select 如果数组中的任何值是列值的开头
Select if any value in an array is the beginning of a column's value
所以我从 Python 网络服务器得到了一个数组 codes
,我需要 select 具有与其中任何一个匹配的列的所有行 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 || '%'
那样附加到数组是不合法的,那么我该怎么做呢?
您需要弄清楚如何将数组传递到查询中。显然,您可以在 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 || '%';
如果您想 return 匹配的模式,使用 Postgres 数组特别有用。
可能有更好的方法来做到这一点。但一般概念对我来说很有意义:如果项目的长度小于 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
这个小函数可以完成这项工作:
create or replace function array_for_like(text[])
returns text[] language sql as $$
select string_to_array(concat(array_to_string(, '%,'), '%'), ',')
$$;
示例:
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)
所以我从 Python 网络服务器得到了一个数组 codes
,我需要 select 具有与其中任何一个匹配的列的所有行 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 || '%'
那样附加到数组是不合法的,那么我该怎么做呢?
您需要弄清楚如何将数组传递到查询中。显然,您可以在 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 || '%';
如果您想 return 匹配的模式,使用 Postgres 数组特别有用。
可能有更好的方法来做到这一点。但一般概念对我来说很有意义:如果项目的长度小于 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
这个小函数可以完成这项工作:
create or replace function array_for_like(text[])
returns text[] language sql as $$
select string_to_array(concat(array_to_string(, '%,'), '%'), ',')
$$;
示例:
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)