parse key:value pairs in SQL postgresql - postgresql

I have a string that contains a semicolon separated list of key value pairs.
E.g ref:12345;code:ab etc.
I would like to split it into 'ab' as code,'241376' as ref,etc.
Any help really appreciated.

You can use a combination of regexp_split_to_table and split_part.
Have a look at PostgreSQL docs.
CREATE TABLE t (myText text);
INSERT INTO t VALUES ('ref:12345;code:ab;ref:5678;code:cd');
SELECT
split_part(pair, ':', 1) as name,
split_part(pair, ':', 2) as value
FROM
(SELECT regexp_split_to_table(myText, ';') pair FROM t) t1
Result:
name
value
ref
12345
code
ab
ref
5678
code
cd
db<>fiddle here
UPDATE
According to your comment if your desired result is:
xxx as code
xxx as ref
You can use:
SELECT
CONCAT(split_part(pair, ':', 2), ' as ', split_part(pair, ':', 1)) RESULT
FROM (SELECT regexp_split_to_table(myText, ';') pair FROM t) t1
That returns:
result
12345 as ref
ab as code
5678 as ref
cd as code
db<>fiddle here

A little bit messy but I hope self explanatory.
with t as
(
select (r + 1)/2 as r,
split_part(txt, ':', 1) as k,
split_part(txt, ':', 2) as v
from unnest(string_to_array('ref:12345;code:ab;ref:5678;code:cd;ref:9876;code:yz', ';'))
with ordinality as t(txt, r)
)
select
max(v) filter (where k = 'ref') as ref_fld,
max(v) filter (where k = 'code') as code_fld
from t group by r;
Result:
ref_fld
code_fld
12345
ab
9876
yz
5678
cd

Related

How to use redshift regex to get out numbers in an array

In redshift, I have a column that contains an array-like string like [1,2,3] and I want to return 1,2,3 using Redshift's regex functionality. How can one do this? I don't want to do this:
SELECT LISTAGG(option_name , ',') WITHIN GROUP (ORDER BY option_name) as pets_names
FROM reference.vital_options
WHERE option_id in
(
-- this nested CTE splits the json string array into comma separated pet ids
with NS AS (
SELECT vo.option_id + 1 as n
FROM <column with number id> as vo
WHERE upper(vo.country) = 'US'
...
)
select TRIM(JSON_EXTRACT_ARRAY_ELEMENT_TEXT(u.pets_vital, NS.n - 1)) AS val
FROM NS
INNER JOIN go_prod.users AS u ON NS.n <= JSON_ARRAY_LENGTH(u.pets_vital)
WHERE u.id = %(user_id)s
)
AND ...
Is all you are trying to do is remove the square brackets? If so then the translate() function is likely what you want to use. For example:
create table test as (select '[1,2,3]'::text as A);
select a, translate(a, '][', '') as b from test;

TSQL - in a string, replace a character with a fixed one every 2 characters

I can't replace every 2 characters of a string with a '.'
select STUFF('abcdefghi', 3, 1, '.') c3,STUFF('abcdefghi', 5, 1,
'.') c5,STUFF('abcdefghi', 7, 1, '.') c7,STUFF('abcdefghi', 9, 1, '.')
c9
if I use STUFF I should subsequently overlap the strings c3, c5, c7 and c9. but I can't find a method
can you help me?
initial string:
abcdefghi
the result I would like is
ab.de.gh.
the string can be up to 50 characters
Create a numbers / tally / digits table, if you don't have one already, then you can use this to target each character position:
with digits as ( /* This would be a real table, here it's just to test */
select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))x(n)
), t as (
select 'abcdefghi' as s
)
select String_Agg( case when d.n%3 = 0 then '.' else Substring(t.s, d.n, 1) end, '')
from t
cross apply digits d
where d.n <Len(t.s)
Using for xml with existing table
with digits as (
select n from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))x(n)
),
r as (
select t.id, case when d.n%3=0 then '.' else Substring(t.s, d.n, 1) end ch
from t
cross apply digits d
where d.n <Len(t.s)
)
select result=(select '' + ch
from r r2
where r2.id=r.id
for xml path('')
)
from r
group by r.id
You can try it like this:
Easiest might be a quirky update ike here:
DECLARE #string VARCHAR(100)='abcdefghijklmnopqrstuvwxyz';
SELECT #string = STUFF(#string,3*A.pos,1,'.')
FROM (SELECT TOP(LEN(#string)/3) ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM master..spt_values) A(pos);
SELECT #string;
Better/Cleaner/Prettier was a recursive CTE:
We use a declared table to have some tabular sample data
DECLARE #tbl TABLE(ID INT IDENTITY, SomeString VARCHAR(200));
INSERT INTO #tbl VALUES('')
,('a')
,('ab')
,('abc')
,('abcd')
,('abcde')
,('abcdefghijklmnopqrstuvwxyz');
--the query
WITH recCTE AS
(
SELECT ID
,SomeString
,(LEN(SomeString)+1)/3 AS CountDots
,1 AS OccuranceOfDot
,SUBSTRING(SomeString,4,LEN(SomeString)) AS RestString
,CAST(LEFT(SomeString,2) AS VARCHAR(MAX)) AS Growing
FROM #tbl
UNION ALL
SELECT t.ID
,r.SomeString
,r.CountDots
,r.OccuranceOfDot+2
,SUBSTRING(RestString,4,LEN(RestString))
,CONCAT(Growing,'.',LEFT(r.RestString,2))
FROM #tbl t
INNER JOIN recCTE r ON t.ID=r.ID
WHERE r.OccuranceOfDot/2<r.CountDots-1
)
SELECT TOP 1 WITH TIES ID,Growing
FROM recCTE
ORDER BY ROW_NUMBER() OVER(PARTITION BY ID ORDER BY OccuranceOfDot DESC);
--the result
1
2 a
3 ab
4 ab
5 ab
6 ab.de
7 ab.de.gh.jk.mn.pq.st.vw.yz
The idea in short
We use a recursive CTE to walk along the string
we add the needed portion together with a dot
We stop, when the remaining length is to short to continue
a little magic is the ORDER BY ROW_NUMBER() OVER() together with TOP 1 WITH TIES. This will allow all first rows (frist per ID) to appear.

How to count the frequency of integers in a set of querystrings in postgres

I have a column in a postgres database which logs search querystrings for a page on our website.
The column contains data like
"a=2&b=4"
"a=2,3"
"b=4&a=3"
"a=4&a=3"
I'd like to work out the frequency of each value for a certain parameter (a).
value | freq
------|------
3 | 3
2 | 2
4 | 1
Anyway to do this in a single SQL statement?
Something like this:
with all_values as (
select string_to_array(split_part(parameter, '=', 2), ',') as query_params
from the_table d,
unnest(string_to_array(d.querystring, '&')) as x(parameter)
where x.parameter like 'a%'
)
select t.value, count(*)
from all_values av, unnest(av.query_params) as t(value)
group by t.value
order by t.value;
Online example: http://rextester.com/OXM67442
try something like this :
select data_value,count(*) from (
select data_name,unnest(string_to_array(data_values,',')) data_value from (
select split_part(data_array,'=',1) data_name ,split_part(data_array,'=',2) data_values from (
select unnest(string_to_array(mydata,'&')) data_array from mytable
) a
) b
) c where data_name='a' group by 1 order by 1
Assuming tha table that keeps the counts is called paramcount:
WITH vals(v) AS
(SELECT regexp_replace(p, '^.*=', '')
FROM regexp_split_to_table(
'b=4&a=3,2',
'&|,'
) p(p)
)
INSERT INTO paramcount (value, freq)
SELECT v, 1 FROM vals
ON CONFLICT (value)
DO UPDATE SET freq = paramcount.freq + 1
WHERE paramcount.value = EXCLUDED.value;
get csv integer after 'a='
split that to numbers
stat values
select v, count(*) from (
SELECT c,unnest(string_to_array(unnest(regexp_matches(c,'a=([0-9,]+)','g')),',')) as v FROM qrs
) x group by v;
Parametrize:
WITH argname(aname) as (values ('a'::TEXT))
select v, count(*) from (SELECT c,unnest(string_to_array(unnest(regexp_matches(c,aname||'=([0-9,]+)','g')),',')) as v FROM qrs,argname) x group by v;

Split a string in characters SQL

How can I split a string in characters and add a new line after each character in PostgreSQL
For example
num desc
1 Hello
2 Bye
num desc
1 H
e
l
l
o
2 B
y
e
select num, regexp_split_to_table(descr,'')
from the_table
order by num;
SQLFiddle: http://sqlfiddle.com/#!15/13c00/4
The order of the characters is however not guaranteed and achieving that is a bit complicated.
Building on Erwin's answer regarding this problem:
select case
when row_number() over (partition by id order by rn) = 1 then id
else null
end as id_display,
ch_arr[rn]
from (
select *,
generate_subscripts(ch_arr, 1) AS rn
from (
select id,
regexp_split_to_array(descr,'') as ch_arr
from data
) t1
) t2
order by id, rn;
Edit:
If you just want a single string for each id, where the characters are separated by a newline, you can use this:
select id,
array_to_string(regexp_split_to_array(descr,''), chr(10))
from data
order by id

Validating Excel using XML and moving to SQL Server destination

Is there a built in function (as opposed to a UDF) or can someone provide sample code to split a String to two columns when a character is encountered?
Sample:
1234:abcd
split the above string into 1234 and abcd into two columns
Have a go with this. Its not pretty but it produces the two columns (assuming : is always the divider):
declare #test varchar(20)
set #test = '1234:abcd'
select
leftcol = left(#test,charindex(':',#test)-1),
rightcol = right(#test,len(#test) - charindex(':',#test))
In otherwords, its not a build in function, but it is inline sql code.
Title/tag mismatch?
For Excel, if A1 contains the value:
make B1 =LEFT(A1,IF(ISERROR(FIND(":",A1)),LEN(A1),FIND(":",A1)-1))
make C1 =RIGHT(A1,IF(ISERROR(FIND(":",A1)),0,LEN(A1)-FIND(":",A1)))
Or for T-SQL + a string variable;
DECLARE #F VARCHAR(64) = '1234:ABCD'
IF #F LIKE '%:%'
SELECT SUBSTRING(#F, 1, CHARINDEX(':', #F, 1) - 1) AS COL1,
SUBSTRING(#F, CHARINDEX(':', #F, 1) + 1, LEN(#F)) AS COL2
ELSE
SELECT #F AS COL1, NULL AS COL2
for a select;
;WITH faketable (fld) AS (
SELECT 'aaa:123' as fld
UNION SELECT 'ddddd'
)
SELECT
CASE WHEN fld LIKE '%:%' THEN SUBSTRING(fld, 1, CHARINDEX(':', fld, 1) - 1) ELSE fld END AS COL1 ,
CASE WHEN fld LIKE '%:%' THEN SUBSTRING(fld, CHARINDEX(':', fld, 1) + 1, LEN(fld)) ELSE NULL END AS COL2
FROM faketable
>COL1 COL2
>aaa 123
>ddddd NULL