Sha2 function return empty/null values in snowflake - hash

I am using sh2 function to generate the hash value but It is generating null/empty if any of the column value has null/empty.
For example
Input
select sha2(NULL, 256);
Output
NULL
is there any way to generate value even when the value inside sha2 function is empty/null.

NULL in SQL, means no value, so the result you are getting is expected.
You can create a query that will check for NULLs and return some predefined or random output for them, depending on what value you want, and the hash when there is the actual value. Example:
Create some test data:
create or replace table test_table (col1 string);
insert into test_table values ('string1'), (null), ('string2');
Return hash when it is not NULL
select
col1,
case
when col1 is null then 'value you want get instead of the hash'
else sha2(col1, 256)
end as result
from test_table;
Output:
Alternatively, you can output some random hash instead of a predefined string:
select
col1,
case
when col1 is null then sha2(random())
else sha2(col1, 256)
end as result
from test_table;
Output:

Related

Check if value is not null and not empty in a select query

I have created a function in Postgresql and specified the returned type as TABLE (id uuid, data boolean).
This is the code that I have tried:
BEGIN
RETURN QUERY SELECT table.id, (table.data <> '') as data FROM table;
END
But it will return NULL for "data" when data is NULL in the table. I was expecting it to return FALSE.
Data column is storing a JSON and I am trying to check if the stored value is not null and not empty
How can I make this code work?
Use is distinct from to use a null-safe comparison:
SELECT table.id, table.data is distinct from '' as data
FROM table;
Another option is to treat an empty string like null:
SELECT table.id, nullif(table.data, '') is not null as data
FROM table;

How to insert values from a select query

how do I insert the std_id value and sub_id value in the student_subject table
insert into student_subjects(student_id,subject_id)
values(std_id,(select id from subjects
where guid in
(select * from
unnest(string_to_array(subjects_colls,',')::uuid[])))::int);
ERROR: more than one row returned by a subquery used as an expression
Get rid of the values clause and use the SELECT directly as the source for the INSERT statement:
You also don't need to unnest your array, using = any() will be a bit more efficient (although I would recommend you do not pass comma separated strings, but an array of uuid directly)
insert into student_subjects(student_id,subject_id)
select std_id, s.id
from subjects s
where guid = any(string_to_array(subjects_colls,',')::uuid[])
I assume this is part of a procedure or function and std_id and subjects_colls are parameters passed to it.

Seperate Input/Output type for a Table in PostgreSQL

I was wondering if it is possible to have the input for a column be one string, with the output being a different string through some dictionary in PostgreSQL. I do know how to use CASE to convert numbers to strings using a SELECT statement, however, I was hoping to create a table such that inputs only require numbers but outputs always give strings.
As an example, for currency USD, CDN and GBP, where 1 = USD, 2 = CDN and 3 = GBP, an example would be:
CREATE TABLE test_table (
currency CHAR (1) CHECK (currency IN ('1','2','3'))
)
Where I could do this:
INSERT INTO test_table (currency)
VALUES ('1')
INSERT INTO test_table (currency)
VALUES ('1')
INSERT INTO test_table (currency)
VALUES ('2')
INSERT INTO test_table (currency)
VALUES ('3')
INSERT INTO test_table (currency)
VALUES ('3')
and the output would look like this:
You can use a CASE expression:
select case currency
when '1' then 'USD'
when '2' then 'CDN'
when '3' then 'GBP'
when '4' then 'EUR'
end as currency
from test_table;
But a better solution would be to create a currency table:
create table currency
(
id integer primary key,
currency_code varchar(3)
);
Then create a foreign key from your base table to the lookup table:
create table test_table
(
...
currency_id integer not null references currency,
...
);
Then use a join to display the code:
select c.code
from test_table t
join currency c on c.id = t.currency_id;

IN Lookup Postgres query in prepared statement, Golang

The use case requires running of exclusion queries.
Something like:
select col1
from awesome_table
where col2 not in (a,b,c,d)
and col3 not in (a1,a2,a3,a4);
As the set of excluded col1 values and excluded col2 values is variable sized, what is a good way to generate the prepared statement?
One hack that I can think of is to define an upper limit on the set say 15 and fill all placeholders with repeated values if number of query set size input by user is less than max value, is there a better way? And how are prepared statements suppose to handle this, as per the philosophy of the community?
Can you pass (Postgres) arrays from Go?
Then you could rewrite the statement to
where col2 <> ALL ($1)
and col3 <> all ($2)
where $1 and $2 are (Postgres) arrays containing the values.
If you can't pass proper array instances, you can pass the values as a string that's formatted so that it can be cast to an array.
select col1
from awesome_table
where col2 <> ALL ( (cast $1 as int[]) )
and col3 <> ALL ( (cast $2 as text[]) );
Then you could pass '{1,2,3}' for the first parameter and e.g. '{"foo", "bar"}' as the second parameter. You need to adjust the array types to the actual data types of your columns
Adding to #a_horse_with_no_name's answer,
In Golang, the psql driver github.com/lib/pq contains a method Array() that can be used to convert a Golang slice into a psql Array.
...
import (
"github.com/lib/pq"
)
...
select col1
from awesome_table
where col2 <> ALL ($1)
and col3 <> ALL ($2);
where
slice1 := []string{val1, val2}
slice2 := []string{val3, val4}
pq.Array(slice1) can be passed for $1 and pq.Array(slice2) can be passed for $2 placeholder while passing the values in the prepared statements.
More about ANY and ALL functions can be found at here

Update hstore values with other hstore values

I have a summary table that is updated with new data on a regulary basis. One of the columns is of type hstore. When I update with new data I want to add the value of a key to the existing value of the key if the key exists, otherwise I want to add the pair to the hstore.
Existing data:
id sum keyvalue
--------------------------------------
1 2 "key1"=>"1","key2"=>"1"
New data:
id sum keyvalue
--------------------------------------------------
1 3 "key1"=>"1","key2"=>"1","key3"=>"1"
Wanted result:
id sum keyvalue
--------------------------------------------------
1 5 "key1"=>"2","key2"=>"2","key3"=>"1"
I want to do this in a on conflict part of an insert.
The sum part was easy. But I have not found how to concatenate the hstore in this way.
There is nothing built int. You have to write a function that accepts to hstore values and merges them in the way you want.
create function merge_and_increment(p_one hstore, p_two hstore)
returns hstore
as
$$
select hstore_agg(hstore(k,v))
from (
select k, sum(v::int)::text as v
from (
select *
from each(p_one) as t1(k,v)
union all
select *
from each(p_two) as t2(k,v)
) x
group by k
) s
$$
language sql;
The hstore_agg() function isn't built-in as well, but it's easy to define it:
create aggregate hstore_agg(hstore)
(
sfunc = hs_concat(hstore, hstore),
stype = hstore
);
So the result of this:
select merge_and_increment(hstore('"key1"=>"1","key2"=>"1"'), hstore('"key1"=>"1","key2"=>"1","key3"=>"1"'))
is:
merge_and_increment
-------------------------------------
"key1"=>"2", "key2"=>"2", "key3"=>"1"
Note that the function will fail miserably if there are values that can't be converted to an integer.
With an insert statement you can use it like this:
insert into the_table (id, sum, data)
values (....)
on conflict (id) do update
set sum = the_table.sum + excluded.sum,
data = merge_and_increment(the_table.data, excluded.data);
demo:db<>fiddle
CREATE OR REPLACE FUNCTION sum_hstore(_old hstore, _new hstore) RETURNS hstore
AS $$
DECLARE
_out hstore;
BEGIN
SELECT
hstore(array_agg(key), array_agg(value::text))
FROM (
SELECT
key,
SUM(value::int) AS value
FROM (
SELECT * FROM each('"key1"=>"1","key2"=>"1"'::hstore)
UNION ALL
SELECT * FROM each('"key1"=>"1","key2"=>"1","key3"=>"1"')
) s
GROUP BY key
) s
INTO _out;
RETURN _out;
END;
$$
LANGUAGE plpgsql;
each() expands the key/value pairs into one row per pair with columns key and value
convert type text into type int and group/sum the values
Aggregate into a new hstore value using the hstore(array, array) function. The array elements are the values of the key column and the values of the value column.
You can do such an update:
UPDATE mytable
SET keyvalue = sum_hstore(keyvalue, '"key1"=>"1","key2"=>"1","key3"=>"1"')
WHERE id = 1;