Getting selection of structs from an array of structs in BQ - sql

I have a table where one column is defined as:
my_column ARRAY<STRUCT<key STRING, value FLOAT64, description STRING>>
Is there some easy way how to specify list of parameters to be returned in a SELECT statement? For instance removing description, so the result column would be still an array of structs but containing only key and value.

Below is for BigQuery Standard SQL
#standardSQL
SELECT * REPLACE(
ARRAY(
SELECT AS STRUCT * EXCEPT(description)
FROM UNNEST(my_column)
) AS my_column)
FROM `project.dataset.table`
Above fully preserves schema of table and only does change in my_column field by removing description

I would just unnest and then re-aggregate your selected fields.
select array_agg(struct(m.key,m.value)) as my_new_column
from table
left join unnest(my_column) m

I found this way:
SELECT
ARRAY(SELECT AS VALUE STRUCT(key, value) FROM a.my_column) as my_new_column
FROM my_table a
No joining or unnesting needed.

Related

query json data from oracle 12.1 having fields value with "."

I have a table that has JSON data stored and I'm using json_exists functions in the query. Below is my sample data from the column for one of the rows.
{"fields":["query.metrics.metric1.field1",
"query.metrics.metric1.field2",
"query.metrics.metric1.field3",
"query.metrics.metric2.field1",
"query.metrics.metric2.field2"]}
I want all those rows which have a particular field. So, I'm trying below.
SELECT COUNT(*)
FROM my_table
WHERE JSON_EXISTS(fields, '$.fields[*]."query.metrics.metric1.field1"');
It does not give me any results back. Not sure what I'm missing here. Please help.
Thanks
You can use # operator which refers to an occurrence of the array fields such as
SELECT *
FROM my_table
WHERE JSON_EXISTS(fields, '$.fields?(#=="query.metrics.metric1.field1")')
Demo
Edit : The above case works for 12R2+, considering that it doesn't work for your version(12R1), try to use JSON_TABLE() such as
SELECT fields
FROM my_table,
JSON_TABLE(fields, '$.fields[*]' COLUMNS ( js VARCHAR2(90) PATH '$' ))
WHERE js = 'query.metrics.metric1.field1'
Demo
I have no idea how to "pattern match" on the array element, but just parsing the whole thing and filtering does the job.
with t(x, json) as (
select 1, q'|{"fields":["a", "b"]}|' from dual union all
select 2, q'|{"fields":["query.metrics.metric1.field1","query.metrics.metric1.field2","query.metrics.metric1.field3","query.metrics.metric2.field1","query.metrics.metric2.field2"]}|' from dual
)
select t.*
from t
where exists (
select null
from json_table(
t.json,
'$.fields[*]'
columns (
array_element varchar2(100) path '$'
)
)
where array_element = 'query.metrics.metric1.field1'
);
In your code, you are accessing the field "query.metrics.metric1.field1" of an object in the fields array, and there is no such object (the elements are strings)...

How to use LOWER() on elements of a jsonb column in PostgreSQL?

I have a PostgreSQL table like this one:
Table t
id | keys (jsonb)
---+----------------
1 | ["Key1", "Key2"]
My goal is to query this table to find out if one of the keys of a list is contained in the jsonb array column "keys".
I managed to get a result using:
SELECT *
FROM t
WHERE keys ?| Array ['Key1', 'Key2'];
I can not find a way to make this query broader by applying a lower() on the "keys" values in the table though.
Is there a way to iterate over elements to apply the lower() on each one?
Thanks to the replies above I managed to find a way to do it like this:
SELECT *
FROM t, jsonb_array_elements_text(keys) key
WHERE lower(key) in ('key1', 'key2') ;
You might need to unnest both arrays:
select *
from t
where exists (
select 1
from jsonb_array_elements_text(t.keys) k1(val)
inner join unnest(array['Key1', 'Key2']) k2(val)
on lower(k1.val) = lower(k2.val)
)

Extract elements from an array inside a jsonb field in Postgresql

In my Postgresql table, I have a jsonb field called data, which contains data in the following format:
{
list:[1,2,3,4,5]
}
I use the query:
select data->'list' from "Table" where id=1
This gives me the array [1,2,3,4,5]
The problem is that I want to use this result in another select query within the IN clause. It's not accepting the array.
IN ([1,2,3,4,5]) fails
It wants:
IN (1,2,3,4,5)
So, In my original query I don't know how to covert [1,2,3,4,5] to just 1,2,3,4,5
My current query is:
select * from "Table2" where "items" in (select data->'list' from "Table" where id=1)
Please help
You can use the array contains operator (#>) rather than IN if you cast the search value to jsonb. For example:
SELECT *
FROM "Table2"
WHERE items::jsonb <# (SELECT data->'list' FROM "Table" WHERE id=1)
Note that if items is an int you will need to cast it char before casting to jsonb:
SELECT *
FROM "Table2"
WHERE cast(items as char)::jsonb <# (SELECT data->'list' FROM "Table" WHERE id=1)
Demo on dbfiddle
Use jsonb_array_elements() to turn the elements into rows
select t2.*
from table_2 t2
where t2.items in (select jsonb_array_elements_text(t1.data -> 'list')::int
from table_1 t1
where t1.id = 1);
This assumes that items is defined as text or varchar and contains a single value - however the name (plural!) seems to indicate yet another de-normalized column.

Detect if a jsonb attribute is array or object

Many jsonb/json functions expect all values of the column either to be of type json array ( like jsonb_array_length ) or only an json object (like jsonb_build_oject) .
There are some jsonb columns in the database which contain a mix of both arrays and object roots, is there any easy way to filter out arrays and objects so that queries like
SELECT DISTINCT jsonb_object_keys(my_column) FROM my_table;
cannot call jsonb_object_keys on an array
or
SELECT my_column FROM my_table WHERE jsonb_array_length(column) > 0;
cannot get array length of a non-array
As described in documentation the functions jsonb_typeof or json_typeof can be used to apply this kind of filtering
like
SELECT DISTINCT jsonb_object_keys(my_column)
FROM my_table WHERE jsonb_typeof(column) ='object' ;
or
SELECT my_column FROM my_table
WHERE jsonb_array_length(column) > 0
AND jsonb_typeof(column) ='array' ;

Accessing BigQuery RECORD - Repeated in Tableau

I have a BigQuery Table with a column of RECORD type & mode REPEATED. I have to query and use this table in Tableau. Using UNNEST or FLATTEN in BigQuery is performing CROSS JOIN of the Table which is impacting performance. Is there any other way to use this table in Tableau without flattening it. Have posted the table schema image link below.
[Schema of Table]
https://i.stack.imgur.com/T4jHg.png
Is there any other way to use ... ?
You should not afraid UNNEST just because it “does” CROSS JOIN
The trick is that even though it is cross join but it is cross join within the row only and global to all rows in table. At the same time, there are always way to do stuff different
So, below example 1 – presents dummy example using UNNEST
And then Example 2 – shows how to do the same without using UNNEST, but rather using SQL UDF
You have not presented specifics about your case, so below is generic enough to show ‘other’ way
With Flattening via UNNEST
#standardSQL
WITH yourTable AS (
SELECT 1 AS id, ARRAY<STRUCT<details INT64, flag STRING, value STRING, description STRING>>
[(1,'y','a','xxx'),(2,'n','b','yyy'),(3,'y','c','zzz'),(4,'n','d','vvv')] AS type UNION ALL
SELECT 2 AS id, ARRAY<STRUCT<details INT64, flag STRING, value STRING, description STRING>>
[(11,'t','c','xxx'),(21,'n','a','yyy'),(31,'y','c','zzz'),(41,'f','d','vvv')] AS type
)
SELECT id, SUM(t.details) AS details
FROM yourTable, UNNEST(type) AS t
WHERE t.flag = 'y'
GROUP BY id
With SQL UDF
#standardSQL
CREATE TEMP FUNCTION do_something (
type ARRAY<STRUCT<details INT64, flag STRING, value STRING, description STRING>>
)
RETURNS INT64 AS ((
SELECT SUM(t.details) AS details
FROM UNNEST(type) AS t
WHERE t.flag = 'y'
));
WITH yourTable AS (
SELECT 1 AS id, ARRAY<STRUCT<details INT64, flag STRING, value STRING, description STRING>>
[(1,'y','a','xxx'),(2,'n','b','yyy'),(3,'y','c','zzz'),(4,'n','d','vvv')] AS type UNION ALL
SELECT 2 AS id, ARRAY<STRUCT<details INT64, flag STRING, value STRING, description STRING>>
[(11,'t','c','xxx'),(21,'n','a','yyy'),(31,'y','c','zzz'),(41,'f','d','vvv')] AS type
)
SELECT id, do_something(type) AS details
FROM yourTable