PostgreSQL: retrieving multiple array elements - arrays

Let's say we have a query like:
SELECT regexp_split_to_array('foo,bar', ',');
Results:
+-----------------------+
| regexp_split_to_array |
+-----------------------+
| {foo,bar} |
+-----------------------+
(1 row)
To access a single element of an array we can use code like:
SELECT (regexp_split_to_array('foo,bar', ','))[1];
Which will return:
+-----------------------+
| regexp_split_to_array |
+-----------------------+
| foo |
+-----------------------+
(1 row)
Or use slices like:
SELECT (regexp_split_to_array('foo,bar', ','))[2:];
Result:
+-----------------------+
| regexp_split_to_array |
+-----------------------+
| {bar} |
+-----------------------+
(1 row)
However, when I try to access 2 elements at once, like:
SELECT (regexp_split_to_array('foo,bar', ','))[1,2];
or
SELECT (regexp_split_to_array('foo,bar', ','))[1][2];
or any other syntax, I receive an error:
ERROR: syntax error at or near ","
Is it possible to retrieve two different and not adjacent elements of an array in PostgreSQL?

Extracting multiple elements through a select from an array should either mean you can have them returned as multiple columns or all those elements part of a single array.
This returns you one column as an array of the two elements.
knayak=# select ARRAY[arr[1],arr[2]] FROM regexp_split_to_array('foo,bar', ',') as arr;
array
-----------
{foo,bar}
(1 row)
..and this simply gives you the two elements as columns.
knayak=# select arr[1],arr[2] FROM regexp_split_to_array('foo,bar', ',') as arr;
arr | arr
-----+-----
foo | bar
(1 row)

The colon ':' in the array indexer does allow you to access multiple elements as a from-thru.
select (array[1,2,3,4,5])[2:4]
returns
{2,3,4}
This would work in your example above, but not if 1 an 2 weren't next to each other. If that's the case, the suggestion from #KaushikNayak is the only way I could think of.
Using your example:
SELECT (regexp_split_to_array('foo,bar', ','))[1:2]

Related

How to convert array<string> to string in hive

I want to convert an array<string> to string in hive. The array data is as follow:
+-------------------------------------+--+
| NULL |
| ["Extension","Terms & Conditions"] |
| ["Value (generic or item level)"] |
+-------------------------------------+--+
I want to collect array values to convert to string without [""] so that I could get result like:
+-------------------------------------+--+
| NULL |
| Extension,Terms & Conditions |
| Value (generic or item level) |
+-------------------------------------+--+
Following query: select concat_ws(',', col_name) as col_name from table_stg; provides the result but is is returning NULL as empty. I tried several reference like:
How can I convert array to string in hive sql?
Hive - How to cast array to string?
But not getting the desired result. Is there any way to get the desired result?
With reference to Vamsi comment, I managed to get this and thought to answer as well for the community reference.
select case when col_name is NULL then NULL
else concat_ws(',',col_name) end from table_name;

Insert array of JSONB objects from one table as multiple rows in second table

We are trying to migrate data from an array column containing JSONB to a proper Postgres table.
{{"a":1,"b": 2, "c":"bar"},{"a": 2, "b": 3, "c":"baz"}}
a | b | c
---+---------+---
1 | 2 | "bar"
2 | 3 | "baz"
As part of the process, we have made several attempts using functions like unnest and array_to_json. In the unnest case, we get several JSONB rows, but cannot figure out how to insert them into the second table. In the array_to_json case, we are able to cast the array to a JSON string, but the json_to_recordset does not seem to accept the JSON string from a common table expression.
What would be a good strategy to 'mirror' the array of JSONB items as a proper table, so that we can run the query inside of a stored procedure, triggered on insert?
Use unnest() in a lateral join:
with my_data(json_column) as (
values (
array['{"a":1,"b":2,"c":"bar"}','{"a":2,"b":3,"c":"baz"}']::jsonb[])
)
select
value->>'a' as a,
value->>'b' as b,
value->>'c' as c
from my_data
cross join unnest(json_column) as value
a | b | c
---+---+-----
1 | 2 | bar
2 | 3 | baz
(2 rows)
You may need some casts or converts, e.g.:
select
(value->>'a')::int as a,
(value->>'b')::int as b,
(value->>'c')::text as c
from my_data
cross join unnest(json_column) as value
Lateral joining means that the function unnest() will be executed for each row from the main table. The function returns elements of the array as value.

Use TQuery.Locate() function to find other then first matching

Locate moves the cursor to the first row matching a specified set of search criteria.
Let's say that q is TQuery component, which is connected to the database with two columns TAG and TAGTEXT. With next code I am getting letter a. And I would like to use Locate() function to get letter d.
If q.Locate('TAG','1',[loPartialKey]) Then
begin
tag60 := q.FieldByName('TAGTEXT');
end
For example if I got table like this:
TAG | TAGTEXT
+---+--------+
| 1 | a |
+---+--------+
| 2 | b |
+---+--------+
| 3 | c |
+---+--------+
| 1 | d |
+---+--------+
| 4 | e |
+---+--------+
| 1 | f |
+---+--------+
is it possible to locate the second time number one occurred in table?
EDIT
My job is to find the occurrence of TAG with value 1 (which occurrence I need depends on the parameter I get), I need to iterate through table and get the values from all the TAGTEXT fields till I find that value in TAG field is again number 1. Number 1 in this case represents the start of new segment, and all between the two number 1s belongs to one segment. It doesn't have to be same number of rows in each segment. Also I am not allowed to do any changes on table.
What I thought I could do is to create a counter variable that is going to be increased by one every time it comes to TAG with value 1 in it. When the counter equals to the parameter that represents the occurrence I know that I am in the right segment and I am going to iterate through that segment and get the values I need.
But this might be slow solution, and I wanted to know if there was any faster.
You need to be a bit wary of using Locate for a purpose like this, because some
TDataSet descendants' implementation of Locate (or the underlying db-access layer) construct a temporary index on the dataset. which can be discarded immediately afterwards, so repeatedly calling Locate to iterate the rows of a given segment may be a lot more inefficient than one might expect it to be.
Also, TClientDataSet constructs, uses and then discards an expression parser for each invocation of Locate (in its internal call to LocateRecord), which is a lot of overhead for repeated calls, especial when they are entirely avoidable.
In any case, the best way to do this is to ensure that your table records which segment a given row belongs to, adding a column like the SegmentID below if your table does not already have one:
TAG | TAGTEXT|SegmentID
+---+--------+---------+
| 1 | a | 1
| 2 | b | 1
| 3 | c | 1
| 1 | d | 2
+---+--------+---------+ // btw, what happened to the 2 missing rows after this one?
| 4 | e | 2
| 1 | f | 3
+---+--------+---------+
Then, you could use code like this to iterate the rows of a segment:
procedure IterateSegment(Query : TSomeTypeOfQueryComponent; SegmentID : Integer);
var
Sql; String;
begin
Sql := Format('select * from mytable where SegmentID = %d order by Tag', [SegmentID]);
if Query.Active then
Query.Close;
Query.Sql.Text := Sql;
Query.Open;
Query.DisableControls;
try
while not Query.Eof do begin
// process row here
Query.Next;
end;
finally
Query.EnableControls;
end;
end;
Once you have the SegmentID column in the table, if you don't want to open a new query to iterate a block, you can set up a local index (by SegmentID then Tag), assuming your dataset type supports it, set a filter on the dataset to restrict it to a given SegmentID and then iterate over it
You have much options to do this.
If your component donĀ“t provide a locateNext you can make your on function locateNext, comparing the value and make next until find.
You can also bring the sql with order by then use locate for de the first value and test if the next value match the comparision.
If you use a clientDataset you can filter into the component filter propertie, or set IndexFieldNames to order values instead the "order by" of sql in the prior suggestion.
You can filter it on the SQL Where clausule too.

Extract last two elements of an array in HIVE

I have an array in a hive table, and I want to extract the two last elements of each array, something like this:
["a", "b", "c"] -> ["b", "c"]
I tried a code like this:
SELECT
*,
array[size] AS term_n,
array[size - 1] AS term_n_1
FROM
(SELECT *, size(array) AS size FROM MyTable);
But it didn't work, someone has any idea please?
array is a reserved word and should be qualified.
An inner sub-query should be aliased.
Array index start with 0. If the array size is 5 then the last index is 4.
Demo
with MyTable as (select array('A','B','C','D','E') as `array`)
SELECT *
,`array`[size - 1] AS term_n
,`array`[size - 2] AS term_n_1
FROM (SELECT *
,size(`array`) AS size
FROM MyTable
) t
;
+-----------------------+--------+--------+----------+
| t.array | t.size | term_n | term_n_1 |
+-----------------------+--------+--------+----------+
| ["A","B","C","D","E"] | 5 | E | D |
+-----------------------+--------+--------+----------+
I don't know the error that you are getting, but it should be something like
select
yourarray[size(yourarray)],
yourarray[size(yourarray)-1]
from mytable
This is a solution to extract the last element of an array in the same query (notice it is not very optimal, and you can apply the same principle to extract n last elements of the array), the logic is to calculate the size of the last element (amount of letters minus the separator character) and then make a substring from 0 to the total size minus the calculated amount of characters to extract
Table of example:
col1 | col2
--------------
row1 | aaa-bbb-ccc-ddd
You want to get (extracting the last element, in this case "-ddd"):
row1 | aaa-bbb-ccc
the query you may need:
select col1, substr(col2,0,length(col2)-(length(reverse(split(reverse(col2),'-')[0]))+1)) as shorted_col2_1element from example_table
If you want to add more elements you have to keep adding the positions in the second part of the operation.
Example to extract the last 2 elements:
select col1, substr(col2,0,length(col2)-(length(reverse(split(reverse(col2),'-')[0]))+1) + length(reverse(split(reverse(col2),'-')[1]))+1)) as shorted_col2_2element from example_table
after executing this second command line you will have something like:
row1 | aaa-bbb
*As said previously this is a not optimal solution at all, but may help you

sort 2d Array re-order the first column

For example:
Array
ID | Primary | Data2
------------------
1 | N | Something 1
2 | N | Something 2
3 | Y | Something 3
I'm trying to sort it based on the primary column and I want the "Y" to show first. It should bring all the other column at the top.
The end result would be:
Sorted Array
ID | Primary | Data2
------------------
3 | Y | Something 3
1 | N | Something 1
2 | N | Something 2
Is there a pre-made function for that. If not, how do we do this?
It is declared like this:
Dim Array(,) As String
regards,
I like using LINQ's OrderBy and ThenBy to order collections of objects. You just pass in a selector function to use to order the collections. For example:
orderedObjs = objs.OrderByDescending(function(x) x.isPrimary).ThenBy(function(x) x.id).ToList()
This code orders a collection first by the .isPrimary boolean, then by the id. Finally, it immediately evaluates the query into a List and assigns it to some variable.
Demo
There's a similar C# question whose solution applies just as well to VB. In short, you can use an overload of Array.Sort if you first split your 2D array into separate (1D) arrays:
Dim Primary() As String
Dim Data2() As String
// ...
Array.Sort(Primary,Data2)
This would reorder Data2 according to the Y/N sort of Primary, after which point you could then recombine them into a 2D array.

Resources