我有一个包含SDK名称和版本的字段,并且没有标准化,因此我可以轻松提取名称和版本。这些是人为的值,但代表我正在使用的可能值:
如您所见,“ /”的使用不一致,因此我需要一种方法来一致地解析数字中的字母,以便最终得到两列,如下所示:
JavaScript 2.3.4
JavaScript 4.3.1
Android 4.6.5
Android 3.2.1
Swift 4.5.3
Swift 3.1.1.5
我搜索了不同的方法来执行此操作,但是我发现没有任何事情可以满足我的要求。
最终,我需要将其放入这样的Postgres SELECT语句中:
我认为DISTINCT关键字是不必要且令人困惑的。它最初是COUNT / GROUP BY查询的一部分,但是为了简单起见,我只想用三列分别列出所有行:sdk,sdk_name和sdk_version。从那里开始,我将根据答案使用最佳的解析公式。
SELECT sdk, [parse sdk name formula] as "sdk_name", [parse sdk version formula] as "sdk_version"
此外,我在查询中没有提供固定的SDK列表,因此我不确定with/as/values
策略是否对我有用,但是我并不了解并且看起来很有用。我想with / values可以只是另一个SELECT查询。
答案 0 :(得分:3)
您可以为此使用translate:
SELECT sdk, translate(sdk,'0123456789/.','') AS sdk_name,
translate(lower(sdk),'abcdefghijklmnopqrstuvwxyz/','') AS sdk_version
FROM table1;
工作fiddle
编辑(作者戈登):
这是一个好主意。我发现使用regexp_replace()
更简单:
select regexp_replace(sdk, '[0-9/.]', '', 'g') as sdk_name,
regexp_replace(sdk, '[a-zA-Z/]', '', 'g') as sdk_version
答案 1 :(得分:3)
使用正则表达式功能substring()
:
with my_data(sdk) as (
values
('JavaScript/2.3.4'),
('JavaScript/4.3.1'),
('Android4.6.5'),
('Android3.2.1'),
('Swift4.5.3'),
('Swift/3.1.1.5')
)
select
substring(sdk from '[^\d/]*') as sdk_name,
substring(sdk from '\d.*') as sdk_version
from my_data
sdk_name | sdk_version
------------+-------------
JavaScript | 2.3.4
JavaScript | 4.3.1
Android | 4.6.5
Android | 3.2.1
Swift | 4.5.3
Swift | 3.1.1.5
(6 rows)
该功能绝对应该是最快的解决方案,请参见benchmarks on about 90 thousand rows example data.
更新。
您可以将select
查询放在with
部分(而不是values
):
with my_data(sdk) as (
<select sdk from ...>
)
select
substring(sdk from '[^\d/]*') as sdk_name,
substring(sdk from '\d.*') as sdk_version
from my_data
或在from
子句中:
select
substring(sdk from '[^\d/]*') as sdk_name,
substring(sdk from '\d.*') as sdk_version
from (
<select sdk from ...>
) my_data
答案 2 :(得分:0)
正则表达式解析非常耗费计算量,因此,您不应该使用两个函数调用(如在其他答案中那样),而是应将它们组合为一个调用,然后从结果中提取所需的值:
WITH d(sdk) AS (
VALUES
('JavaScript/2.3.4'),
('JavaScript/4.3.1'),
('Android4.6.5'),
('Android3.2.1'),
('Swift4.5.3'),
('Swift/3.1.1.5'),
('C#/23.1') )
SELECT unq.sdk, re.match[1] AS sdk_name, re.match[2] AS sdk_version
FROM (SELECT DISTINCT sdk FROM d) unq,
regexp_match(unq.sdk, '([^0-9/]*)/*([0-9.]*)') re (match);
正则表达式执行以下操作:
([^0-9/]*)
捕获所有内容,直到第一位数字或正斜杠。请注意,这还将匹配包含A-Za-z
以外的字符的SDK名称。/*
跳过正斜杠(如果存在)([0-9.]*)
捕获以下任何数字或点。如果您确信只跟随数字和点,那么您也可以执行(*)
。还要注意,我将DISTINCT
子句放在了单独的子查询中。首先处理每一行然后丢弃所有重复项并不是非常有效。相反,请先消除重复项。
PG-10之前的版本
版本10中引入了功能regexp_match()
。如果您使用的是较旧的版本,则可以将regexp_matches()
与g
标志一起使用,以获得相同的结果(PG8.3 +)。