Translating SUBSTRING_INDEX() in MySQL to PostgreSQL - postgresql

There are a number of question related to this and the answer is to use split_part(). For example:
emulating MySQL's substring_index() in PGSQL
Mysql`s SUBSTRING_INDEX equivalent in postgresql
I'm not getting the same behavior, however. I'm trying to figure out how to get the following functionality in Postgres.
If you have a string that looks like:
+------------------------------------------+
| string |
+------------------------------------------+
| A_123, B_123, C_123, D_123, E_123, F_123 |
+------------------------------------------+
MySQL will return the following with the given statement:
mysql> select SUBSTRING_INDEX(string, ',', 4) AS test FROM tbl;
+----------------------------+
| test |
+----------------------------+
| A_123, B_123, C_123, D_123 |
+----------------------------+
PostgreSQL will return the following with the given statement:
mysql> select split_part(string, ',', 4) AS test FROM tbl;
+-------+
| test |
+-------+
| D_123 |
+-------+
Is there a similar function or just implementing a function like this?

As a_horse_with_no_name suggested in the comments, this had the desired result:
array_to_string((regexp_split_to_array(string, '\s*,\s'))[:4], ', ')

Related

postgres - inline variable assignment from select

In SQL Server it's possible to do inline variable assignment.
For example, table dbo.tblSynonym:
+--+-------+-----------+
|id|keyword|replacement|
+--+-------+-----------+
|1 |aaa |bbb |
|2 |xxx |yyy |
|3 |ddd |eee |
+--+-------+-----------+
when I run this:
DECLARE #body varchar(max)='aaa111xxx111ddd'
SELECT #body = REPLACE(#body,keyword,replacement)
FROM dbo.tblSynonym
SELECT #body
The result should be bbb111yyy111eee.
So the value of #body will be updated on each row (interaction) and the replace input will be from result of previous rows on the source table.
Is it possible to do something like this in postgres (without cursor)?
Thanks
I think this will help you
regexp_replace('Thomas', '.[mN]a.', 'M') //ThM
Look here
https://www.postgresql.org/docs/current/static/functions-matching.html#FUNCTIONS-POSIX-REGEXP

function array_agg(text) is not unique when connecting with pgAdmin III

When I connect to PostgreSQL database with pgAdmin III (1.22.2), I get this error:
ERROR: function array_agg(text) is not unique
LINE 5: (SELECT array_agg(label) FROM pg_shseclabel sl1 WHERE sl1.ob...
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
The database server is PostgreSQL 9.6.
Same instance of pgAdmin works fine with databases running PostgreSQL 9.0 versions.
How can I fix this problem?
I found that problem is caused by migration from 9.0 to 9.6.
Log in to the database using psql and run \df array_agg. This is how it should look in 9.0:
db=# \df array_agg
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+-----------+------------------+---------------------+------
pg_catalog | array_agg | anyarray | anyelement | agg
(1 row)
and in 9.6:
db=# \df array_agg
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+-----------+------------------+---------------------+------
pg_catalog | array_agg | anyarray | anyarray | agg
pg_catalog | array_agg | anyarray | anynonarray | agg
(2 rows)
However, when I ran \df array_agg on my server, I get:
db=> \df array_agg
Lista funkcji
Schemat | Nazwa | Typ danych wyniku | Typy danych argumentów | Typ
------------+-----------+-------------------+------------------------+-------
pg_catalog | array_agg | anyarray | anyarray | agreg
pg_catalog | array_agg | anyarray | anynonarray | agreg
public | array_agg | anyarray | anyelement | agreg
(3 rows)
As mentioned here, it can be fixed by:
DROP AGGREGATE public.array_agg(anyelement);
In my case, explicitly typecasting the argument to either TEXT or VARCHAR works.
E.g.,
SELECT array_agg('Group1');
Results in:
ERROR: function array_agg(unknown) is not unique
LINE 1: SELECT array_agg('Group1');
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
SQL state: 42725
Character: 8
However, both of
SELECT array_agg('Group1'::VARCHAR);
and
SELECT array_agg('Group1'::TEXT);
work as expected and return the respective TEXT or VARCHAR array.

Querying partial value from a field - SQL SERVER 2008

I need to return only a portion of the value in a given field.
Example:
A given field returns something like 'AB-1X3.4567' but the desired value is only the '1X3.4567'portion. So for this example I need to remove anything that precedes the pattern of
[0-9,A-Z][0-9,A-Z][0-9,A-Z][.][0-9,A-Z][0-9,A-Z][0-9,A-Z][0-9,A-Z].
What query could I write to do this?
using stuff() and patindex():
create table t (val varchar(32))
insert into t values
('AB-1X3.4567') -- given example
,('1X3.4567AB-1X3.4567') --extra junk on the end
,('1X3.4567') -- goldy locks
,('X3.4567') -- too short
,('AB-1X#.4567') -- # is not [0-9A-Z]
select
val
, str = stuff(val,1,patindex('%[0-9A-Z][0-9A-Z][0-9A-Z][.][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',val)-1,'')
from t
rextester demo: http://rextester.com/ITUJ68634
returns:
+---------------------+---------------------+
| val | str |
+---------------------+---------------------+
| AB-1X3.4567 | 1X3.4567 |
| 1X3.4567AB-1X3.4567 | 1X3.4567AB-1X3.4567 |
| 1X3.4567 | 1X3.4567 |
| X3.4567 | NULL |
| AB-1X#.4567 | NULL |
+---------------------+---------------------+
Your pattern alludes to anything which is XXX.XXXX where X = any single digit or letter. In that case we can use RIGHT() and LEN()
DECLARE #value VARCHAR(4000)='AB-1X3.4567'
SELECT RIGHT(#value,LEN(#value) - 3)

Column-wise autocomplete

I have a table in a PostgreSQL database with four columns that contain increasingly more detailed information (think state->city->street->number), along with a column where everything is concatenated according to some simple formatting rules. Example:
| kommun | trakt | block | enhet | beteckning |
| Mora | Gislövs Läge | 9 | 16 | Mora Gislövs Läge 9:16 |
| Mora | Gisslaved | * | 8 | Mora Gisslaved 8 |
| Mora | Gisslaved | * | 9 | Mora Gisslaved 9 |
| Lilla Edet | Sanda | GA | 1 | Lilla Edet Sanda GA:1 |
A web service uses this table to implement a word-wise autocomplete, where the user gets input suggestions as they drill down. An input of mora gis will result in
["Mora Gislövs", "Mora Gisslaved"]
Currently, this is done by splitting the concatenated column by word in this query:
select distinct trim(substring(beteckning from '(^(\S+\s?){NUMPARTS})')) as bet
from beteckning_ac
where upper(beteckning) like upper('mora gis%')
order by bet
Where NUMPARTS is the number of words in the input - 2 in this case.
Now I want the autocomplete to be done column-wise rather than word-wise, so mora gis would now result in this instead:
["Mora Gislövs Läge", "Mora Gisslaved"]
Since the first two columns can contain an arbitrary number of words, I can no longer use the input to determine how many columns to include in the response. Is there a way to do this, or have I maybe gone about this autocomplete business all wrong?
CREATE OR REPLACE FUNCTION get_auto(text)
--$1 is here your input
RETURNS setof text
LANGUAGE plpgsql
AS $function$
declare
NUMPARTS int := array_length(regexp_split_to_array($1,' '), 1);
begin
return query
select
case
when (NUMPARTS = 1) then kommun
when (NUMPARTS = 2) then kommun||' '||trakt
when (NUMPARTS = 3) then kommun||' '||trakt||' '||block
when (NUMPARTS = 4) then kommun||' '||trakt||' '||block||' '||enhet
--alter if you want to
end
from
auto_complete --your tablename here
where
beteckning like $1||'%';
end;
$function$;

Update intermediate result

EDIT
As requested a little background of what I want to achieve. I have a table that I want to query but I don't want to change the table itself. Next the result of the SELECT query (what I called the 'intermediate table') needs to be cleaned a bit. For example certain cells of certain rows need to be swapped and some strings need to be trimmed. Of course this could all be done as postprocessing in, e.g., Python, but I was hoping to do all of this with one query statement.
Being new to Postgresql I want to update the intermediate table that results from a SELECT statement. So I basically want to edit the resulting table from a SELECT statement in one query. I'd like to prevent having to store the intermediate result.
I've tried the following 'with clause':
with result as (
select
a
from
b
)
update result as r
set
a = 'd'
...but that results in ERROR: relation "result" does not exist, while the following does work:
with result as (
select
a
from
b
)
select
*
from
result
As I said, I'm new to Postgresql so it is entirely possible that I'm using the wrong approach.
Depending on the complexity of the transformations you want to perform, you might be able to munge it into the SELECT, which would let you get away with a single query:
WITH foo AS (SELECT lower(name), freq, cumfreq, rank, vec FROM names WHERE name LIKE 'G%')
SELECT ... FROM foo WHERE ...
Or, for more or less unlimited manipulation options, you could create a temp table that will disappear at the end of the current transaction. That doesn't get the job done in a single query, but it does get it all done on the SQL server, which might still be worthwhile.
db=# BEGIN;
BEGIN
db=# CREATE TEMP TABLE foo ON COMMIT DROP AS SELECT * FROM names WHERE name LIKE 'G%';
SELECT 4677
db=# SELECT * FROM foo LIMIT 5;
name | freq | cumfreq | rank | vec
----------+-------+---------+------+-----------------------
GREEN | 0.183 | 11.403 | 35 | 'KRN':1 'green':1
GONZALEZ | 0.166 | 11.915 | 38 | 'KNSL':1 'gonzalez':1
GRAY | 0.106 | 15.921 | 69 | 'KR':1 'gray':1
GONZALES | 0.087 | 18.318 | 94 | 'KNSL':1 'gonzales':1
GRIFFIN | 0.084 | 18.659 | 98 | 'KRFN':1 'griffin':1
(5 rows)
db=# UPDATE foo SET name = lower(name);
UPDATE 4677
db=# SELECT * FROM foo LIMIT 5;
name | freq | cumfreq | rank | vec
--------+-------+---------+-------+---------------------
grube | 0.002 | 67.691 | 7333 | 'KRP':1 'grube':1
gasper | 0.001 | 69.999 | 9027 | 'KSPR':1 'gasper':1
gori | 0.000 | 81.360 | 28946 | 'KR':1 'gori':1
goeltz | 0.000 | 85.471 | 47269 | 'KLTS':1 'goeltz':1
gani | 0.000 | 86.202 | 51743 | 'KN':1 'gani':1
(5 rows)
db=# COMMIT;
COMMIT
db=# SELECT * FROM foo;
ERROR: relation "foo" does not exist