how to bind a list of tuples using Spring JDBCTemplate? - sql

I have some queries like this:
List listOfIntegers = Arrays.asList(new Integer[] {1, 2, 3});
List objects =
namedParameterJdbcTemplate.query("select * from bla where id in ( :ids )",
Collections.singletonMap("ids", listOfIntegers),
myRowMapper);
This will send this SQL query to the database:
select * from bla where id in ( 1, 2, 3 )
Now I want to send this type of query to the database:
select * from bla where (id,name) in ( (1,'foo'), (2,'bar'), (3,'foobar'))
Do I need to pass a List<List<Object>> to accomplish this? Will it work with Spring JDBCTemplate?

I have debugged Spring code and found that it expects tuples to be provided as Object[], so for it to work with a List it should be a List<Object[]>.

Related

Multi-value object search in CrateDB when this one is within an array of objects

I'm trying to migrate our current ES to CrateDB and one of the issues I'm facing is searching for two specific values within the same object when this object is part of an array of objects.
CREATE TABLE test.artefact (
id INTEGER,
metadata ARRAY(OBJECT(STATIC) AS (
key_id INTEGER,
value TEXT
))
);
insert into test.artefact(id, metadata) values (
1,
[
{
"key_id" = 1,
"value" = 'TEST1'
},
{
"key_id" = 2,
"value" = 'TEST2'
}
]
);
So basically, I'm trying to search metadata providing key_id and value.
A select like this one finds artefact 1 as a match, even when key and value are in different objects:
select * from test.artefact where 1 = ANY(metadata['key_id']) AND 'TEST2' = ANY(metadata['value'])
I have tried other functions, like UNNEST, with no luck.
Copy from CrateDB Community:
One way that should work is
SELECT *
FROM test.artefact
WHERE {key_id = 1, value = 'TEST2'} = ANY(metadata)
however this is probably not the most performant way.
together with the queries on the fields it might be quick enough.
SELECT *
FROM test.artefact
WHERE
1 = ANY(metadata['key_id'])
AND 'TEST2' = ANY(metadata['value'])
AND {key_id = 1, value = 'TEST2'} = ANY(metadata)

How to run prepared Postgres query in vert.x with an IN ($1) parameter?

Given this SQL query:
SELECT * FROM users WHERE user_id IN (1, 2, 3)
with the (1, 2, 3) being a List<Int> of variable length, how can I bind the parameter with the vert.x Reactive Postgres client?
In the nodejs pg-promise client, it works like this:
await db.any('SELECT * FROM users WHERE user_id IN ($1:list)', [ [1, 2, 3] ])
Ended up changing the query, replacing user_id IN ($1) with user_id = ANY($1), binding an IntArray as as a single parameter

PostgreSQL: Extract specific information from json

I'm using PostgreSQL 9.6 and I have the following table, let's call it table1:
id | json_data
____________________
200 | {"state": [3, 4, 5]}
I want to be able to perform the following query: extract the array inside the "state" key in json_data for the record with id 1, but also remove some of the integers from the array in the process.
For example (in pseudo-code):
extract_state(id = 200, remove_numbers_from_json_data_state = [3, 5]) should return [4]
There is no built-in function for that, but you can easily write your own:
create function remove_numbers(p_array jsonb, p_nr variadic int[] )
returns jsonb
as
$$
select jsonb_agg(x)
from jsonb_array_elements(p_array) as t(x)
where t.x::int <> ALL(p_nr);
$$
language sql
immutable;
Then you can use it like this:
select id, remove_numbers(json_data -> 'state', 4,5)
from t1
where id = 1;
If you prefer to pass a JSON array value, you can define the function like this:
create function remove_numbers(p_array jsonb, p_to_remove jsonb)
returns jsonb
as
$$
select jsonb_agg(x)
from jsonb_array_elements(p_array) as t(x)
where t.x not in (select *
from jsonb_array_elements(p_to_remove))
$$
language sql;
Then you would need to use remove_numbers(json_data -> 'state', '[4,5]')
You'd use a query like this:
SELECT json_data->'state'->>1 FROM table1 WHERE id = 200;
You can test this way, without having a table:
SELECT '{"state": [3, 4, 5]}'::jsonb->'state'->>1
Here is a reference for using JSON in Postgres:
https://www.postgresql.org/docs/current/functions-json.html
It returns 4, not [4], so if you need to, you could select it as an array:
SELECT ARRAY[('{"state": [3, 4, 5]}'::jsonb->'state'->>1)::int]

Groovy SQL named list parameter

I want to use a keyset of a Map as a list parameter in a SQL query:
query = "select contentid from content where spaceid = :spaceid and title in (:title)"
sql.eachRow(query, [spaceid: 1234, title: map.keySet().join(',')]) {
rs ->
println rs.contentid
}
I can use single values but no Sets or Lists.
This is what I've tried so far:
map.keySet().join(',')
map.keySet().toListString()
map.keySet().toList()
map.keySet().toString()
The map uses Strings as key
Map<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
Also, I don't get an error. I just get nothing printed like have an empty result set.
You appoach will not give the expected result.
Logically you are using a predicate such as
title = 'value1,value2,value3'
This is the reason why you get no exception but also no data.
Quick search gives a little evidence, that a mapping of a collections to IN list is possible in Groovy SQL.
Please check here and here
So very probably you'll have to define the IN list in a proper length and assign the values from your array.
title in (:key1, :key2, :key3)
Anyway something like this works fine:
Data
create table content as
select 1 contentid, 1 spaceid, 'AAA' title from dual union all
select 2 contentid, 1 spaceid, 'BBB' title from dual union all
select 3 contentid, 2 spaceid, 'AAA' title from dual;
Groovy Script
map['key1'] = 'AAA'
map['key2'] = 'BBB'
query = "select contentid from content where spaceid = :spaceid and title in (${map.keySet().collect{":$it"}.join(',')})"
println query
map['spaceid'] = 1
sql.eachRow(query, map) {
rs ->
println rs.contentid
}
Result
select contentid from content where spaceid = :spaceid and title in (:key1,:key2)
1
2
The key step is to dynamicall prepare the IN list with proper names of the bind variable using the experssion map.keySet().collect{":$it"}.join(',')
Note
You may also want to check the size if the map and handle the case where it is greater than 1000, which is an Oracle limitation of a single IN list.
It has worked for me with a little adaptation, I've added the map as a second argument.
def sql = Sql.newInstance("jdbc:mysql://localhost/databaseName", "userid", "pass")
Map<String,Long> mapProduitEnDelta = new HashMap<>()
mapProduitEnDelta['key1'] = 1
mapProduitEnDelta['key2'] = 2
mapProduitEnDelta['key3'] = 3
produits : sql.rows("""select id, reference from Produit where id IN (${mapProduitEnDelta.keySet().collect{":$it"}.join(',')})""",mapProduitEnDelta),
Display the 3 products (colums + values from the produit table) of id 1, 2, 3

Select where id = array (Oracle JDBC)

I'm currently working with trying to compare an ID in Oracle(A VARCHAR2) with an array of IDs I have as input.
This is what I want to do:
Select user, city where id = :arrayOfIds
In Postgres with JDBC I would use:
Select user, city where id = any(:arrayOfIds)
Is there an equivalent function in Oracle, or a way to do this?
You should use:
Select user, city where id in (:arrayOfIds)
and in you code, you need to trasform your array in string with ids:
arrayOfIds[0] --> 1
arrayOfIds[1] --> 3
arrayOfIds[2] --> 5
...
in
1, 3, 5, ...
and you can use:
Array array = conn.createArrayOf("arrayOfIds", new Object[]{"1", "2","3"});
pstmt.setArray(1, array);
How to use an arraylist as a prepared statement parameter