Split string into words using Postgres - sql

I am looking for some help in separating scientific names in my data. I want to take only the genus names and group them, but they are both connected in the same column. I saw the SQL Sever had a CHARINDEX command, but PostgreSQL does not. Does there need to be a function created for this? If so, how would it look?
I want to change 'Mallotus philippensis' to just 'Mallotus' or to just 'philippensis'
I am currently using Postgres 11, 12.

Use SPLIT_PART:
WITH yourTable AS (
SELECT 'Mallotus philippensis'::text AS genus
)
SELECT
SPLIT_PART(genus, ' ', 1) AS genus,
SPLIT_PART(genus, ' ', 2) AS species
FROM yourTable;
Demo

Probably string_to_array will be slightly more efficient than split_part here because string splitting will be done only once for each row.
SELECT
val_arr[1] AS genus,
val_arr[2] AS species
FROM (
SELECT string_to_array(val, ' ') as val_arr
FROM (
VALUES
('aaa bbb'),
('cc dddd'),
('e fffff')
) t (val)
) tt;

Related

Regex: how to get the text between a few colons?

So, i have a lot of strings like the ones below in my database:
product1:1stparty:single_aduls:android:
product2:3rdparty:married_adults:ios:
product3:3rdparty:other_adults:android:
I need a regex to get only the text after the product name and before the device category. So, in the first line I'd get 1stparty:single_aduls, in the second 3rdparty:married_adults and in the third 3rdparty:other_adults. I'm stuck and can't find a way to solve that. Could anyone help me please?
As a regular expression, you can use:
select regexp_extract('product1:1stparty:single_aduls:android:', '^[^:]*:(.*):[^:]*:$')
This returns every after the first colon and before the penultimate colon.
We can try using REGEXP_REPLACE here:
SELECT REGEXP_REPLACE(val, r"^.*?:|:[^:]+:$", "") AS output
FROM yourTable;
This approach removes either the leading ...: or trailing :...: from the column, leaving behind the content you want. Here is a demo showing that the regex replacement is working:
Demo
You can also use standard split function and access result array element by index, which is quite clear to read and understand.
with a as (
select split('product1:1stparty:single_aduls:android:', ':') as splitted
)
select splitted[ordinal(2)] || ':' || splitted[ordinal (3)] as subs
from a
Consider below example
with your_table as (
select 'product1:1stparty:single_aduls:android:' txt union all
select 'product2:3rdparty:married_adults:ios:' union all
select 'product3:3rdparty:other_adults:android:'
)
select *,
(
select string_agg(part, ':' order by offset)
from unnest(split(txt, ':')) part with offset
where offset in (1, 2)
) result
from your_table
with output

Using string methods in a SELECT query to select up to the second space?

In an MS-Access database I'm working with, one of the tables has a field called "Name". The format of this field will generally be along the lines of "firstname surname integer", but sometimes may just be "firstname surname".
I need to select just the first name and the surname from the name field.
I've looked at using the Left function
SELECT DISTINCT LEFT([Name], x)
However since names are different lengths, this isn't going to work since there is no constant integer to use as the second parameter. Nor can it be used with
SELECT DISTINCT LEFT(InStr([Name], " "), x)
for the above reason, but also because because that would split the field at the first space.
Is there a way using LEFT, TRIM, SPLIT or any other string manipulation that I can create a query to select just the first two parts of the name? I need the space included.
You can try this.
SELECT DISTINCT IIf( ( InStr( InStr([Name],' ') + 1 , [Name], ' ') > 0 ), Left( [Name], InStr(InStr([Name],' ') + 1 , [Name], ' ') ), [Name])
FROM MyTable;

Get group maxima from combined strings

I have a table with a column code containing multiple pieces of data like this:
001/2017/TT/000001
001/2017/TT/000002
001/2017/TN/000003
001/2017/TN/000001
001/2017/TN/000002
001/2016/TT/000001
001/2016/TT/000002
001/2016/TT/000001
002/2016/TT/000002
There are 4 items in 001/2016/TT/000001: 001, 2016, TT and 000001.
How can I extract the max for every group formed by the first 3 items? The result I want is this:
001/2017/TT/000003
001/2017/TN/000002
001/2016/TT/000002
002/2016/TT/000002
Edit
The subfield separator is /, and the length of subfields can vary.
I use PostgreSQL 9.3.
Obviously, you should normalize the table and split the combined string into 4 columns with proper data type. The function split_part() is the tool of choice if the separator '/' is constant in your string and the length of can vary.
CREATE TABLE tbl_better AS
SELECT split_part(code, '/', 1)::int AS col_1 -- better names?
, split_part(code, '/', 2)::int AS col_2
, split_part(code, '/', 3) AS col_3 -- text?
, split_part(code, '/', 4)::int AS col_4
FROM tbl_bad
ORDER BY 1,2,3,4 -- optionally cluster data.
Then the task is trivial:
SELECT col_1, col_2, col_3, max(col_4) AS max_nr
FROM tbl_better
GROUP BY 1, 2, 3;
Related:
Split comma separated column data into additional columns
Of course, you can do it on the fly, too. For varying subfield length you could use substring() with a regular expression like this:
SELECT max(substring(code, '([^/]*)$')) AS max_nr
FROM tbl_bad
GROUP BY substring(code, '^(.*)/');
Related (with basic explanation for regexp pattern):
Filter strings with regex before casting to numeric
Or to get only the complete string as result:
SELECT DISTINCT ON (substring(code, '^(.*)/'))
code
FROM tbl_bad
ORDER BY substring(code, '^(.*)/'), code DESC;
About DISTINCT ON:
Select first row in each GROUP BY group?
Be aware that data items cast to a suitable type may behave differently from their string representation. The max of 900001 and 1000001 is 900001 for text and 1000001 for integer ...
Use the LEFT and RIGHT functions.
SELECT MAX(RIGHT(code,6)) AS MAX_CODE
FROM yourtable
GROUP BY LEFT(code,12)
check this out, possible helpfull
select
distinct on (tab[4],tab[2]) tab[4],tab[3],tab[2],tab[1]
from
(
select
string_to_array(exe.x,'/') as tab,
exe.x
from
(
select
unnest
(
array
['001/2017/TT/000001',
'001/2017/TT/000002',
'001/2017/TN/000003',
'001/2017/TN/000001',
'001/2017/TN/000002',
'001/2016/TT/000001',
'001/2016/TT/000002',
'001/2016/TT/000001',
'002/2016/TT/000002']
) as x
) exe
) exe2
order by tab[4] desc,tab[2] desc,tab[3] desc;

Changing LastName,FirstName to LastName,FirstInitial

I'm sure this is super easy, but how would I go about converting LastName,FirstName to LastName,FirstInitial?
For example changing Smith,John to Smith,J or Johnson,John to Johnson,J etc.
Thank You!
In case of LastName and FirstName columns:
select LastName,substr(FirstName,1,1)
from mytable
;
In case of a fullname saved in a single column:
select substr(fullname,1,instr(fullname || ',',',')-1) || substr(fullname,instr(fullname || ',',','),2)
from mytable
;
or
select regexp_replace (fullname,'([^,]*,?)(.).*','\1\2')
from mytable
;
Here is one way, using just "standard" instr and substr. Assuming your input is a single string in the format 'Smith,John':
select substr(fullname, 1, instr(fullname, ',')+1) from yourtable;
yourtable is the name of the table, and fullname is the name of the column.
instr(fullname, ',') finds the position of the comma within the input string (it would be 6 in 'Smith,John'); thensubstrtakes the substring that begins at the first position (the1in the function call) and ends at the position calculated byinstr`, PLUS 1 (to get the first initial as well).

How to find repeating numbers in a column in SQL server . Eg 11111, 33333333, 5555555555,7777777 etc

I need to identify repeated numbers( Eg: 1111, 33333333, 5555555555,777777777 etc.) in a column.
How can I do this in sql server without having to hard code every scenario. The max length is 10 of the column. Any help is appreciated.
This will check if the column has all the same value in it.
SELECT *
FROM tablename
WHERE columnname = REPLICATE(LEFT(columnname,1),LEN(columnname))
As Nicholas Cary notes, if the column is numbers you'd need to cast as varchar first:
SELECT *
FROM tablename
WHERE CAST(columnname AS VARCHAR(10)) = REPLICATE(LEFT(CAST(columnname AS VARCHAR(10)),1),LEN(CAST(columnname AS VARCHAR(10))))
Riffing on #Dave.Gugg's excellent answer, here's another way, using patindex() to look for a character different than the first.
select *
from some_table t
where 0 = patindex( '[^' + left(t.some_column,1) + ']' , t.some_column )
Again, this only works for string types (char,varchar, etc.). Numeric types such as int will need to be converted first.