I know this is a "not build in" or "the way dba thinks" but a programmer approach, how could one request from 3 fields to get the one that is not null, into a result field.
Let's say we have a table with f1,f2,f3,f4,f5.
Let's say f2,f3,f4 are the same type.
Let's say the content of the table be tuples of
(key1,null,null,value1,value2)
(key2,null,value3,value4,value5)
(key3,null,null,null,value6)
Now if we return the first tuple then we get (key1) we get (key1,value1,value2)
If we ask for key2 we get (key1,value3,value5)
If we ask for key3 we get (key1,null,value6)
How is it possible to get the fields in the priority of if you have value in f2, then its set into the returned field, only then if we have value in f3 then its set into the middle returned field, only then if we have value in f4 then its set into the middle returned field
The main goal is to get the result into a single field and prevent the overhead work needed at the result end.
As Bernd_k suggested, COALESCE is your friend here
SELECT [key], COALESCE(f2, f3, f4), f5
FROM YourTable
Select IsNull(IsNull(f1, f2), f3))
Related
I have searched Stack Overflow to get an answer to my question, but while I found many interesting cases, none of them quite address mine.
I have a column called fields in my data, that contains JSON information, such as presented below:
Row Fields
1 [{"label":"Label 1","key":"label_1","description":"Value of label_1"},{"label":"Label 2","key":"label_2","error":"Something"}]
2 [{"description":"something","label":"Row 1","key":"row_1"},{"label":"Row 2","message":"message_1","key":"row_2"}]
In essence, I have many rows of JSON that contain label and key, and bunch of other parameters like that. From every {}, I want to extract only label and key, and then (optional, but ideally) stretch every label and key in every {} to its own row. So, as a result, I would have the following output:
Row Label Key
1 Label 1 label_1
1 Label 2 label_2
2 Row 1 row_1
2 Row 2 row_2
Please note, contents of label and key within JSON can be anything (strings, integers, special characters, a mix of everything, etc. In addition, key and label can be anywhere in relation to other parameters within each {}.
Here is the Big Query SQL dummy data for convenience:
SELECT '1' AS Row, '[{"label":"Label 1","key":"label_1","description":"Value of label_1"},{"label":"Label 2","key":"label_2","error":"Something"}]' AS Fields
UNION ALL
SELECT '2' AS Row, '[{"description":"something","label":"Row 1","key":"row_1"},{"label":"Row 2","message":"message_1","key":"row_2"}]' AS Fields
I have first thought of using REGEX to isolate all the brackets and only show me information with label and key. Then, I looked into BQ Documentation of JSON functions and got very stuck on json_path parameters, specifically because their example doesn't match mine.
Consider below approach
select `row`,
json_extract_scalar(el, '$.label') label,
json_extract_scalar(el, '$.key') key
from your_table, unnest(json_extract_array(fields)) el
if applied to sample data in your question - output is
I have a table in Snowflake where one of the fields, called 'value' is sometimes plain text sometimes JSON, this field is stored as string in Snowflake
I created this view to get only the rows where there is a Json format
CREATE OR REPLACE VIEW tmp_events AS
SELECT PARSE_JSON(value) as json_data,
id
FROM SessionEvent
WHERE session_event_type_id=7;
Then I flatten the rows to create a new field
CREATE OR REPLACE VIEW tmp_events_step2 AS
SELECT id,
json_data:meta:selected::string AS choice
from tmp_events ,
LATERAL FLATTEN(input => tmp_events.json_data)
WHERE choice IS NOT NULL
Everything runs fine until now, I can preview data from these two views, no error and I get the results I was expecting.
The error comes when I try to get distinct values from choice
SELECT DISTINCT choice from tmp_events_step2;
Error parsing JSON: unknown keyword "brain", pos 6
This name Brain seems to come from my initial table without the WHERE statement.
If I run the query without DISTINCT there is no error.
Weird thing I noticed while trying to debug: when I put a limit in tmp_events_step2, the code works fine again, even though I put a limit that's bigger than the number of rows in the table
CREATE OR REPLACE VIEW tmp_events_step2 AS
SELECT id,
json_data:meta:selected::string AS choice
from tmp_events ,
LATERAL FLATTEN(input => tmp_events.json_data)
WHERE choice IS NOT NULL
LIMIT 10000000;
SELECT DISTINCT choice from tmp_events_step2;
What's the catch? Why does it work only with the limit?
The very simple answer to this is the built-in function TRY_PARSE_JSON()
Er, not. You seem to have problems with the Query optimizer that may do incorrect predicate pushdowns. One way to prevent the optimizer from doing this is to use the secure view option:
CREATE SECURE VIEW tmp_events_step2 ...
and file a support ticket...
We reported this error two years ago and they said they where not going to fix, because by hoisting the JSON access prior to running the filters in the WHERE clause that makes the cast valid/safe, impacted performance.
create table variant_cast_bug(num number, var variant);
insert into variant_cast_bug
select column1 as num, parse_json(column2) as var
from values (1, '{"id": 1}'),
(1, '{"id": 2}'),
(2, '{"id": "text"}')
v;
select * from variant_cast_bug;
select var:id from variant_cast_bug;
select var:id from variant_cast_bug where num = 1;
select var:id::number from variant_cast_bug where num = 1; -- <- broken
select TRY_TO_NUMBER(var:id) from variant_cast_bug where num = 1; -- <- works
Sometimes you can nest the select and it will work, and then you can add another SELECT layer around it, and do some aggregation and the cost explodes again.
The only two safe solutions are SERCURE VIEW as Hans mentions, but that is a performance nightmare.
Or to understand this problem and use TRY_TO_NUMBER or it's friends.
At the time this was made bad worse because JSON boolean values where not valid values to pass to TRY_TO_BOOLEAN..
One of the times we got burnt by this was after a snowflake release when code that had been running for a year, started getting this error, because it was complex enough the hoisting did not impact, and then after release it did. This is where Snowflake are rather responsive, and then rolled the release back, and we put TRY_TO on a chunk of already working SQL just to play it safe.
Please submit a support case for this issue.
Is it possible to do something like this?
SELECT
Id,
'1999-01-01T23:01:01Z' for Some_New_Date_Field__c,
SystemModstamp
FROM Opportunity
Some_New_Date_Field__c has not been added to Opportunity yet, but I would like to return a literal value for this for now to unblock some downstream engineering work.
It's not possible to include a literal return value for a column in SOQL, and you also cannot set values for arbitrary property names on returned sObjects.
In Aggregate SOQL, but not non-aggregate queries like this one, you can provide a name for a value that's used in the returned List<AggregateResult>; for example, you could do
SELECT OwnerId, MAX(CloseDate) largestCloseDate FROM Opportunity GROUP BY OwnerId
and get back AggregateResult objects that have a property largestCloseDate. I don't think that helps you here.
As stated in the title, I am in a situation where I need to return a count of occurrences within an array, that is within a jsonb column. A pseudo example is as follows:
CREATE TABLE users (id int primary key, tags jsonb);
INSERT INTO users (id, j) VALUES
(1, '{"Friends": ["foo", "bar", "baz"]}'),
(2, '{"Friends": ["bar", "bar"]}');
please note that the value for friends can contain the same value more than once. This will be relevant later (in this case the second value contains contains the name "bar" twice in jsonb column under the key "Friends".)
Question:
For the example above, if I were to search for the value "bar" (given a query that I need help to solve), I want the number of times "bar" appears in the j (jsonb) column within the key "Friends"; in this case the end result I would be looking for is the integer 3. As the term "bar" appears 3 times across 2 rows.
Where I'm at:
Currently I have sql written, that returns a text array containing all of the friends values (from the multiple selected rows) in a single, 1 dimensional array. That sql is as follows
SELECT jsonb_array_elements_text(j->'Friends') FROM users;
yielding result is the following:
jsonb_array_elements_text
-------------------------
foo
bar
baz
bar
bar
Given that this is an array, is it possible to filter this by the term "bar" in some fashion in order to get the count of the number of times it appears? Or am I way off in my approach?
Other Details:
Version: psql (PostgreSQL) 9.5.2
The table in question and a gin index on it.
Please let me know if any additional information is needed, thanks in advance.
You need to use the result of the function as a proper table, then you can easily count the number of times the value appears.
select count(x.val)
from users
cross join lateral jsonb_array_elements_text(tags->'Friends') as x(val)
where x.val = 'bar'
Let's say you run a SOQL aggregate query that looks like this:
select OwnerId, sum(ExpectedRevenue)val from Opportunity GROUP BY ROLLUP(OwnerId)
For whatever reason, there are no Opportunities with ExpectedRevenue fields populated.
You get a table that looks like this:
val___|OwnerId
|Id1
|Id2
|Id3
4/4 records.
(sidenote: how do you put in tabular data without it looking terrible?)
Take note that the "val" columns are all null and the last OwnerId column is also null.
There are 4 rows because SOQL returns a "total" row as well on rollups.
While looping through the AggregateResult[] that gets returned the code blows up on a line that looks like this: AggregateResult[0].get('val'); with a "System.NullPointerException: Attempt to de-reference a null object"
However, if just 1 of those users has some data, then the whole thing works. So I'm guessing that if no rows have any data in a particular column, that column does not exist at all and calls to retrieve it blow up.
So my question is how do you determine if a column exists to avoid the null reference error?
You have said that the ownerid column and the val columns are all null, therefore AggregateResult[0] is a pointer to a null object and any attempt to get a alue from that object give you the error you are having.
What I expect you want to be doing is before you run
AggregateResult[0].get('val');
you want to have an if statement say
if(AggregateResult.size() > 0)
or possibly
if(AggregateResult[0] != null)
to ensure that you are not attempting to access an empty object.
Try that and it should work. Otherwise post up a bigger code listing to look through.
Paul
If there is no data to summarize for the filters you specify in your where clause you'll get an empty list of AggregateResult back. You can test for this using list isEmpty() in Apex.