Postgres - Repeating an element N times as array - postgresql

For example, where the element is 'hi', and where N is 3, I need a PostgreSQL snippet I can use in a SELECT query that returns the following array:
['hi', 'hi', 'hi']

Postgres provides array_fill for this purpose, e.g.:
SELECT array_fill('hi'::text, '{3}');
SELECT array_fill('hi'::text, array[3]);
The two examples are equivalent but the 2nd form is more convenient if you wish to replace the dimension 3 with a variable.
See also: https://www.postgresql.org/docs/current/functions-array.html

You may use array_agg with generate_series
select array_agg(s) from ( values('hi')) as t(s) cross join generate_series(1,3)
Generic
select array_agg(s) from ( values(:elem)) as t(s) cross join generate_series(1,:n)
DEMO

sql demo
with cte as (
select 'hi' as rep_word, generate_series(1, 3) as value
) -- ^^^ n = 3
select array(SELECT rep_word::text from cte);

Related

Postgresql node traversal using Recursive CTE

I am just trying to learn graph traversal using Recursive CTE in postgresql.
Below is my data set:
i am using the below code to get the path along with existing columns(node & edges).
It is giving me output but path column is not in ARRAY format.
;WITH RECURSIVE CTE AS
(
SELECT NODE,EDGES,ARRAY[G.NODE]::TEXT AS PATH,1 AS LEVEL
FROM property_graph G
UNION ALL
SELECT G.NODE,G.EDGES,C.PATH || G.NODE,LEVEL + 1
FROM property_graph G
INNER JOIN CTE C ON G.NODE = ANY(C.EDGES)
WHERE G.NODE <> ALL(STRING_TO_ARRAY(C.PATH,'')) --Cond added to avoid cyclic graph
)
SELECT NODE,EDGES,PATH,LEVEL
FROM CTE
ORDER BY NODE,LEVEL;
Output:
Could you guys help me?
Thanks in advance.
The problem is that your PATH column is of type TEXT, and so is NODE, therefore the || operator performs string concatenation rather than array concatenation.
You should change the type of your PATH column from TEXT to TEXT[] (and then you can remove the STRING_TO_ARRAY in the WHERE clause.
For example:
WITH RECURSIVE CTE AS
(
SELECT NODE,EDGES,ARRAY[G.NODE]::TEXT[] AS PATH,1 AS LEVEL
FROM property_graph G
UNION ALL
SELECT G.NODE,G.EDGES,C.PATH || ARRAY[G.NODE]::TEXT[],LEVEL + 1
FROM property_graph G
INNER JOIN CTE C ON G.NODE = ANY(C.EDGES)
WHERE G.NODE <> ALL(C.PATH) --Cond added to avoid cyclic graph
)
SELECT NODE,EDGES,PATH,LEVEL
FROM CTE
ORDER BY NODE,LEVEL;

Split and sequentially join string parts in Postgresql

I need to create a DB view with parts of sequential combinations of string parts of a source column. Example:
IN:
tag
--------
A_B_C_D
X_Y_Z
OUT:
subtag
--------
A
A_B
A_B_C
A_B_C_D
X
X_Y
X_Y_Z
The answer seems to be somewhere around WITH RECURSIVE, but I cannot put it all together.
demo:db<>fiddle
SELECT
array_to_string( -- 3
array_agg(t.value) OVER (PARTITION BY tags ORDER BY t.number), --2
'_'
) AS subtag
FROM
tags,
regexp_split_to_table(tag, '_') WITH ORDINALITY as t(value, number) -- 1
Split the string into one row per element. The WITH ORDINALITY adds a row count which can be used to hold the original order of the elements
Using array_agg() window function to aggregate the elements. The ORDER BY makes it cumulative
Reaggregate the array into a string.
You can use a recursive query:
WITH RECURSIVE s AS (
SELECT tag FROM tag
UNION
SELECT regexp_replace(tag, '_[^_]*$', '') FROM s
)
SELECT * FROM s;
tag
---------
A_B_C_D
X_Y_Z
A_B_C
X_Y
A_B
X
A
(7 rows)
The idea is to successively cut off _* at the end.
Thanks a lot #laurenz-albe! There is a problem with your code that it's missing recursion break condition. So I ended up with this:
WITH RECURSIVE s AS (
SELECT tag FROM tag
UNION
SELECT regexp_replace(tag, '_[^_]*$', '')
FROM s
WHERE tag LIKE '%\_%'
)
SELECT * FROM s;
db<>fiddle

how to split a column that is created with array_agg

I'm trying to split up an arrayed column that was created using an array_agg. the following is severely cut down, original query is something like 210 lines
select
distinct on (visitor.id)
id,
array_agg(distinct item.code::text)
from
8xfullouter joins
where
exists(
select
distinct on(visitor.id)
item.code::text
from
3 full outer join that all appear in the first query
group by
visit.id,
item.code
order by
visit.id
)
I need to break the array_agg(distinct item.code::text) into multiple columns. I've tried split_part(array_agg(distinct item.code::text), ',', 1) but received the following
> [Err] ERROR: function split_part(character varying[], unknown,
> integer) does not exist LINE 159: split_part (array_agg(distinct
> "public".procedure_group_cpt_...
thanks!!!
Use an index of the array, e.g.:
select (array_agg(code::text))[1]
from (values ('code1'), ('code2')) as codes(code)
array_agg
-----------
code1
(1 row)
Alternatively, you can use string_agg() instead of array_agg(), e.g.:
select split_part(string_agg(code::text, ','), ',', 1)
from (values ('code1'), ('code2')) as codes(code)
split_part
------------
code1
(1 row)

select the inverse of sql result as a string list

having a sql e.g. something like the following resulting in some rows with one value.
I search a different sql than SELECT * FROM some_sql which results in one row with comma separated values.
WITH some_sql AS (
SELECT 1 FROM DUAL
UNION
SELECT 2 FROM DUAL
)
SELECT * FROM some_sql
this SQL results in the two rows with value 1 and 2.
I seach a SQl resulting in 1,2 without changing the code of 'some_sql'.
Consider http://halisway.blogspot.com/2006/08/oracle-groupconcat-updated-again.html
Sice you are on 11G you can use LISTAGG
WITH some_sql AS (
SELECT 1 x FROM DUAL
UNION
SELECT 2 x FROM DUAL
)
SELECT LISTAGG(x, ',') WITHIN GROUP(ORDER BY x) FROM some_sql

Filter union result

I'm making select with a union.
SELECT * FROM table_1
UNION
SELECT * FROM table_2
Is it possible to filter query results by column values?
Yes, you can enclose your entire union inside another select:
select * from (
select * from table_1 union select * from table_2) as t
where t.column = 'y'
You have to introduce the alias for the table ("as t"). Also, if the data from the tables is disjoint, you might want to consider switching to UNION ALL - UNION by itself works to eliminate duplicates in the result set. This is frequently not necessary.
A simple to read solution is to use a CTE (common table expression). This takes the form:
WITH foobar AS (
SELECT foo, bar FROM table_1
UNION
SELECT foo, bar FROM table_2
)
Then you can refer to the CTE in subsequent queries by name, as if it were a normal table:
SELECT foo,bar FROM foobar WHERE foo = 'value'
CTEs are quite powerful, I recommend further reading here
One tip that you will not find in that MS article is; if you require more than one CTE put a comma between the expression statements. eg:
WITH foo AS (
SELECT thing FROM place WHERE field = 'Value'
),
bar AS (
SELECT otherthing FROM otherplace WHERE otherfield = 'Other Value'
)
If you want to filter the query based on some criteria then you could do this -
Select * from table_1 where table_1.col1 = <some value>
UNION
Select * from table_2 where table_2.col1 = <some value>
But, I would say if you want to filter result to find the common values then you can use joins instead
Select * from table_1 inner join table_2 on table_1.col1 = table_2.col1