Adding CASE WHEN field IN () with jooq - jooq

How can I create a CASE... WHEN IN (list) condition with jooq?
I'm converting a complex order by with cases to a jooq query builder and am unable to find documentation or examples for creating IN conditions:
CASE WHEN myfield IN ("val1", "val2") THEN 1 ELSE -1 END ASC,
I can make case... when statements for a single value match:
queryBuilder.addOrderBy(case_(MYTABLE.MYFIELD)
.when("val1", 1)
.else_(-1).asc());

I managed to find this solution:
queryBuilder.addOrderBy(DSL.decode()
.when(MYTABLE.MYFIELD)
.in("val1", "val2"), 1)
.else_(-1).asc());

Related

CQL query to return a boolean column based on a condition

Is it possible to retrieve a true/false answer from a CQL query that checks a condition -- for example, if a collection has a specific value?
Consider:
CREATE TABLE Test (Id text PRIMARY KEY, Roles set<text>)
INSERT INTO Test(Id, Roles)
VALUES ('123', {'Driver', 'Pilot', 'Janitor'})
I would like to get a true or false value depending on whether or not the set associated with Id='123' contains a specific value. Here is an imaginary syntax for what I'd like to get; it does not work:
SELECT
Roles CONTAINS 'Pilot' // <<== Not a valid syntax; this does not work
FROM Test
WHERE Id = '123'
Ok, here's what I came up with in the airport, quick...
Unfortunately, Cassandra CQL doesn't have a lot of the things that folks have grown accustomed to in SQL. For the problem of querying by id and roles CONTAINS 'Pilot', I came up with a similar solution.
CREATE TABLE roles (Id text, Roles set<text>);
CREATE INDEX on roles(roles);
Although, I used a secondary index to permit filtering on the roles collection.
The boolean is a little trickier. I created a user defined function (setting user_defined_functions_enabled: true in my cassandra.yaml).
Then the UDF:
CREATE OR REPLACE FUNCTION textToBoolean (input TEXT)
RETURNS NULL ON NULL INPUT RETURNS BOOLEAN
LANGUAGE java AS 'if (!input.equals("True")) { return false; }
return Boolean.valueOf(input);';
And then this works:
SELECT texttoboolean('True') AS success FROM roles WHERE id='123' AND roles CONTAINS 'Pilot';
success
---------
True
(1 rows)
All the UDF really does is let you return a boolean True if you really need to. So it returns true if it works, but returns nothing if it doesn't. Your solution of returning the COUNT might work better depending on what you're trying to accomplish.
It is possible to obtain a 1 or 0 result using COUNT:
SELECT COUNT(*)
FROM Test
WHERE Id = '123' AND Roles CONTAINS 'Pilot'
ALLOW FILTERING
You need ALLOW FILTERING to suppress a performance warning.

Nested subquery in FOR ALL ENTRIES

Consultant sent me this code example, here is something he expects to get
SELECT m1~vbeln_im m1~vbelp_im m1~mblnr smbln
INTO CORRESPONDING FIELDS OF TABLE lt_mseg
FROM mseg AS m1
INNER JOIN mseg AS m2 ON m1~mblnr = m2~smbln
AND m1~mjahr = m2~sjahr
AND m1~zeile = m2~smblp
FOR ALL ENTRIES IN lt_vbfa
WHERE
AND m2~bwart = '102'
AND 0 = ( select SUM( ( CASE
when SHKZG = 'S' THEN 1
when SHKZG = 'H' THEN -1
else 0
END ) *MENGE ) MENGE
into lt_mseg-summ
from mseg
where
VBELN_IM = m1~vbeln_im
and VBELP_IM = m1~vbelp_im
).
The problem is I don't see how that should work in current syntax. I think about deriving internal select and using it as condition to main one, but is there a proper way to write this nested construction?
As i get it, if nested statement = 0, then main query executes. The problem here is the case inside nested statement. Is it even possible in ABAP? And in my opinion this check could be used outside from main SQL query.
Any suggestions are welcome.
the logic that you were given is part of Native/Open SQL and has some shortcomings that you need to be aware of.
the statement you are showing has to be placed between EXEC SQL and ENDEXEC.
the logic is platform dependent.
there is no syntax checking performed between the EXEC and ENDEXEC
the execution of this bypasses the database buffering process, so its slower
To me, I would investigate a better way to capture the data that performs better outside of open/native sql.
If you want to move forward with this type of logic, below are a couple of links which should be helpful. There is an example select using a nested select with a case statement.
Test Program
Example Logic
This is probably what you need, it works at least since ABAP 750.
SELECT vbeln UP TO 100 ROWS
FROM vbfa
INTO TABLE #DATA(lt_vbfa).
DATA(rt_vbeln) = VALUE range_vbeln_va_tab( FOR GROUPS val OF <line> IN lt_vbfa GROUP BY ( low = <line>-vbeln ) WITHOUT MEMBERS ( sign = 'I' option = 'EQ' low = val-low ) ).
SELECT m1~vbeln_im, m1~vbelp_im, m1~mblnr, m2~smbln
INTO TABLE #DATA(lt_mseg)
FROM mseg AS m1
JOIN mseg AS m2
ON m1~mblnr = m2~smbln
AND m1~mjahr = m2~sjahr
AND m1~zeile = m2~smblp
WHERE m2~bwart = '102'
AND m1~vbeln_im IN ( SELECT vbelv FROM vbfa WHERE vbelv IN #rt_vbeln )
GROUP BY m1~vbeln_im, m1~vbelp_im, m1~mblnr, m2~smbln
HAVING SUM( CASE m1~shkzg WHEN 'H' THEN 1 WHEN 'S' THEN -1 ELSE 0 END * m1~menge ) = 0.
Yes, aggregating and FOR ALL ENTRIES is impossible in one SELECT, but you can trick the system with range and subquery. Also you don't need three joins for summarizing reversed docs, your SUM subquery is redundant here.
If you need to select documents not only by delivery number but also by position this will be more complicated for sure.

Automapper Project().To Error A query body must end with a select

I'm trying to prevent that a query to an entity bring more columns than necessary. Should only bring those columns specified in the target model.
Below is my code built following some examples to achieve my goal but I get syntax error "A query body must end with a select clause or a group clause linq”
int the query line.
var studentEventsModel = from c in DbContext.StudentEvent.Project().To<StudentEventViewModel>();
Please let me know what I’m doing wrong.
public IEnumerable<StudentEventViewModel> GetStudentEventsListViewModel()
{
Mapper.CreateMap<StudentEvent, StudentEventViewModel>();
var studentEventsModel = from c in DbContext.StudentEvent.Project().To<StudentEventViewModel>();
return studentEventsModel;
}
As #hometoast mentioned you may add select at the end of your query like this:
var studentEventsModel =
from c in DbContext.StudentEvent.Project().To<StudentEventViewModel>() select c;
or alerternatively you may use the lambda expression like this:
var studentEventsModel = DbContext.StudentEvent.Project().To<StudentEventViewModel>();
And as to the question on why you are seeing that error, it is due to the fact that a query syntax must end with select or group, mentioned here in the documentation.
A query expression must begin with a from clause and must end with a
select or group clause. Between the first from clause and the last
select or group clause, it can contain one or more of these optional
clauses: where, orderby, join, let and even additional from clauses.
You can also use the into keyword to enable the result of a join or
group clause to serve as the source for additional query clauses in
the same query expression.

How can I search on list of values using Lucene Query interface

Simplistic Problem description:
Lucene index has two fields per document: ID and NAME.
I want to make a query using the Lucene Query interface such that I can find all the documents where ID is 1 OR 2 OR 3 OR so on. The IDs to be searched will be in a list and can potentially have upto 30 elements.
If I was using the query parser I would have done something like
ID:(1 OR 2 OR 3)
But the application is already heavily committed to the Query interface and I want to follow the current pattern. Only way I can think of doing this with Query interface is create n term queries and group them using the Boolean query as below
BooleanQuery booleanQuery = new BooleanQuery();
(String searchId : lstIds)
{
booleanQuery.add(new TermQuery(new Term("ID", searchId)), BooleanClause.Occur.SHOULD);
}
But is there a better/more efficient way of doing this?
Combining queries togetheer with a BooleanQuery is the correct way to reproduce a query like ID:(1 OR 2 OR 3). The query parser will generate a BooleanQuery similar to what you provided for that syntax, so you are absolutely doing the right thing here.
You might be able to make use of PrefixQuery, NumericRangeQuery or TermRangeQuery to simplify matters, if they actually suit your needs in practice, but there is nothing wrong with what you are doing already.
BooleanQuery is the solution for handling OR operator as you have shown in the code but if you want simple alternative of the it you could also use simple Query and pass the IDs as "1 OR 2 OR 3".
Here is the code snippet lucene 7.
Query query = new QueryParser("ID", analyzer).parse("1 OR 2 OR 3");
TopDocs topDocs = searcher.search(query, 10);
OR if you have all the OR you could also use QueryParser default Operator.
Here is the code snippet for lucene 7.
QueryParser queryParser = new QueryParser("ID", analyzer);
queryParser.setDefaultOperator(QueryParser.Operator.OR);
Query query = queryParser.parse("1 2 3");
TopDocs topDocs = searcher.search(query, 10);
I hope that work for you.

Astyanax parepared statement withvalues() not working properly

today I migrated to Astyanax 1.56.42 and discovered, that withValues() on prepared statements doesn't seem to work properly with SQL SELECT...WHERE...IN ().
ArrayList<ByteBuffer> uids = new ArrayList<ByteBuffer>(fileUids.size());
for (int i = 0; i < fileUids.size(); i++) {
uids.add(ByteBuffer.wrap(UUIDtoByteArray(fileUids.get(i)), 0, 16));
}
result = KEYSPACE.prepareQuery(CF_FILESYSTEM)
.withCql("SELECT * from files where file_uid in (?);")
.asPreparedStatement()
.withValues(uids)
.execute();
If my ArrayList contains more than one entry, this results in error
SEVERE: com.netflix.astyanax.connectionpool.exceptions.BadRequestException: BadRequestException: [host=hostname(hostname):9160, latency=5(5), attempts=1]InvalidRequestException(why:there were 1 markers(?) in CQL but 2 bound variables)
What am I doing wrong? Is there any other way to handle a SQL SELECT...WHERE...IN () - statement or did I find a bug?
Best regards
Chris
As you mentioned because you are supplying a collection (ArrayList) to a single ? Astyanax throws an exception. I think you need to add a ? for each element you want to have inside the IN clause.
Say you want to have 2 ints stored in an ArrayList called arrayListObj the where clause, your statement looks like this:
SELECT & FROM users WHERE somevalue IN (arrayListObj);
Because you are suppling a collection, this cant work, so you will need multiple ?'s. I.e. you want :
SELECT name, occupation FROM users WHERE userid IN (arrayListObj.get(0), arrayListObj.get(1));
I couldn't find anything on the Astyanax wiki about using the IN clause with prepared statements.

Resources