Split string with two delimiters and convert type

大憨熊 提交于 2019-12-06 10:21:33

问题


I have a PL/pgSQL function like this (thanks to the guy who made this possible):

 CREATE OR REPLACE FUNCTION public.split_string(text, text)
    RETURNS SETOF text
    LANGUAGE plpgsql
    AS $function$
    DECLARE 
      pos int; 
      delim_length int := length($2);
    BEGIN
      WHILE $1 <> ''
      LOOP
        pos := strpos($1,$2);
        IF pos > 0 THEN
          RETURN NEXT substring($1 FROM 1 FOR pos - 1);
          $1 := substring($1 FROM pos + delim_length);
        ELSE
          RETURN NEXT $1;
          EXIT;
        END IF; 
      END LOOP;
      RETURN;
    END;
    $function$

It splits a string with a delimiter. Like this:

select * from split_string('3.584731 60.739211,3.590472 60.738030,3.592740 60.736220', ' ');

"3.584731"
"60.739211,3.590472"
"60.738030,3.592740"
"60.736220"

How can I save the results in a temp_array or temp_table. So I can get the the results in temp_x and split up these points again. Like:

"3.584731"
"60.739211"
"3.590472"
"60.738030"
"3.592740"
"60.736220"

and return the values as double precision. And all of this should be done in the function.


回答1:


If you need the intermediary step:

SELECT unnest(string_to_array(a, ' '))::float8
       -- or do something else with the derived table
FROM   unnest(string_to_array('3.584731 60.739211,3.590472 60.738030', ',')) a;

This is more verbose than regexp_split_to_table(), but may still be faster because regular expressions are typically more expensive. (Test with EXPLAIN ANALYZE.)

I first split at ',', and next at ' ' - the reversed sequence of what you describe seems more adequate.

If need be, you can wrap this into a PL/pgSQL function:

CREATE OR REPLACE FUNCTION public.split_string(_str text
                                             , _delim1 text = ','
                                             , _delim2 text = ' ')
  RETURNS SETOF float8 AS
$func$
BEGIN
   RETURN QUERY
   SELECT unnest(string_to_array(a, _delim2))::float8
          -- or do something else with the derived table from step 1
   FROM   unnest(string_to_array(_str, _delim1)) a;
END
$func$ LANGUAGE plpgsql IMMUTABLE;

Or just an SQL function:

CREATE OR REPLACE FUNCTION public.split_string(_str text
                                             , _delim1 text = ','
                                             , _delim2 text = ' ')
  RETURNS SETOF float8 AS
$func$
   SELECT unnest(string_to_array(a, _delim2))::float8
   FROM   unnest(string_to_array(_str, _delim1)) a
$func$ LANGUAGE sql IMMUTABLE;

Make it IMMUTABLE to allow performance optimization and other uses.

Call (using the provided defaults for _delim1 and _delim2):

SELECT * FROM split_string('3.584731 60.739211,3.590472 60.738030');

Or:

SELECT * FROM split_string('3.584731 60.739211,3.590472 60.738030', ',', ' ');

Fastest

For top performance, combine translate() with unnest(string_to_array(...)):

SELECT unnest(
          string_to_array(
             translate('3.584731 60.739211,3.590472 60.738030', ' ', ',')
           , ','
          )
       )::float8



回答2:


You do not need special functions, use built-in regexp_split_to_table:

SELECT *
  FROM regexp_split_to_table(
        '3.584731 60.739211,3.590472 60.738030,3.592740 60.736220',
        '[, ]') s;

EDIT: I don't see why you wish to stick with PL/pgSQL function if there's a built-in one.

Anyway, consider this example:

WITH s AS
(
    SELECT ' ,'::text sep,
           '3.584731 60.739211,3.590472 60.738030,3.592740 60.736220'::text str
 )
SELECT sep, left(sep,1), right(sep,-1),
       str,
       translate(str, right(sep,-1), left(sep,1))
  FROM s;

This means, that you can:

  1. do similar transformations before you call your function or
  2. integrate this code inside, but this will mean you'll need to introduce at least one extra variable, unless you feel comfortable replacing all $1 into translate($1, right($2,-1), left($2,1)) throughout the code. Obviously, plain $2 should be changed to left($2,1).



回答3:


If I understand to your questions well, you can do:

-- store context to temp table
CREATE TEMP TABLE foo AS SELECT v::double precision FROM split_string('...') g(v);

-- store context to ARRAY
SELECT ARRAY(SELECT v::double precision FROM split_string('....') g(v))


来源:https://stackoverflow.com/questions/27148516/split-string-with-two-delimiters-and-convert-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!