如何在postgres select

时间:2018-08-10 20:10:51

标签: sql regex postgresql parsing

我有一个包含SDK名称和版本的字段,并且没有标准化,因此我可以轻松提取名称和版本。这些是人为的值,但代表我正在使用的可能值:

  • JavaScript / 2.3.4
  • JavaScript / 4.3.1
  • Android4.6.5
  • Android3.2.1
  • Swift4.5.3
  • Swift / 3.1.1.5

如您所见,“ /”的使用不一致,因此我需要一种方法来一致地解析数字中的字母,以便最终得到两列,如下所示:

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查询。

3 个答案:

答案 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);

正则表达式执行以下操作:

  1. ([^0-9/]*)捕获所有内容,直到第一位数字或正斜杠。请注意,这还将匹配包含A-Za-z以外的字符的SDK名称。
  2. /*跳过正斜杠(如果存在)
  3. ([0-9.]*)捕获以下任何数字或点。如果您确信只跟随数字和点,那么您也可以执行(*)

还要注意,我将DISTINCT子句放在了单独的子查询中。首先处理每一行然后丢弃所有重复项并不是非常有效。相反,请先消除重复项。

PG-10之前的版本

版本10中引入了功能regexp_match()。如果您使用的是较旧的版本,则可以将regexp_matches()g标志一起使用,以获得相同的结果(PG8.3 +)。