I have a query, represented by a string:
final String q = "select 1 union select 2 union select 3";
This string comes from an external source (configuration), hence it is a string. In the real scenario, the query is ofcourse more meaningful.
I would like to execute this query as a subquery within a jOOQ type-safe query. The following works, but it is not really what I want:
System.out.println(<context>.select().from(DSL.table("person")).where(DSL.field("identifier").in(
<context>.fetch(q).intoArray(0)
)).fetch());
The problem here is that I am essentially executing two queries. This introduces overhead.
Is it possible to execute the string-query as a real subquery? I somehow have to convert the string-query to a Select<Record1> instance (I guess), but I cannot find how to do that.
There are a variety of places where you can inject a Select type as plain SQL. For instance:
As a plain SQL WHERE clause:
<context>.select()
.from(DSL.table("person"))
.where(
"identifier in ({0})", DSL.resultQuery(q)
)
.fetch();
As a plain SQL Table:
<context>.select()
.from(DSL.table("person"))
.where(DSL.field("identifier").in(
DSL.select().from("(" + q + ")")
))
.fetch();
There are others. The important thing to notice is that by using plain SQL, you have the possibility to embed your own SQL strings in templates that have enumerated placeholders
... {0} ... {1} ...
Related
let's say I have the following hibernate query:
#Query("""
SELECT s FROM Submission s
WHERE s.id = :#{#submissionId}
""")
Submission getSubmissionById(#Param("submissionId") Long submissionId);
In our code I find two different ways of putting the parameter into the query:
:#{#submissionId} and :submissionId (without the brackets and #)
Is there a particular reason to use either of those? As far as I know they do exactly the same thing.
I have to write this query using QueryDSL:
select *
from table
where(field1, field2) in (
select inner_field_1, inner_field2
from ...
);
However, I don't know how to use two fields (field1 and field2) with an "in" operator in QueryDSL. I have been looking for it in the documentation but I haven't seen any example of two fields.
This is what I have so far:
Expression<?>[] projection = {
table.field1,
table.field2
};
SQLSubQuery outterQuery= new SQLSubQuery()
.from(table)
.where([some expression].in(inneryQuery.list(projection))) // ???
.groupBy(contentcache1.programId, contentcache1.id);
Any help would be appreciated
Thank you very much in advance
You can express it via
SQLSubQuery outerQuery = new SQLSubQuery()
.from(table)
.where(Expressions.list(column1, column2, ...).in(inneryQuery.list(projection)))
.groupBy(contentcache1.programId, contentcache1.id);
You can rewrite your original query as:
select *
from table, (select distinct inner_field_1, inner_field2 from ...) subquery
where field1 = subquery.field1 and field2 = subquery.field2
Then you don't have to use the IN operator.
You can manually transform your row-value-expression IN predicate into an equivalent EXISTS predicate, which should probably work with QueryDSL. Some details are explained in this blog post, which essentially explains how jOOQ automatically handles such SQL transformations for you, operating directly on the SQL AST, you'd write:
DSL.using(configuration)
.select()
.from(TABLE)
.where(row(TABLE.FIELD1, TABLE.FIELD2).in(
select(INNER_FIELD1, INNER_FIELD_2)
.from(...)
))
Your original query:
select *
from table
where(field1, field2) in (
select inner_field_1, inner_field_2
from ...
);
Is equivalent to this one:
select *
from table
where exists (
select 1
from ...
where table.field1 = inner_field_1 and table.field2 = inner_field2
)
... which I'm sure you can express with QueryDSL (unfortunately, I don't know the API well enough to show the actual query).
Note on compatibility
Chances are that your database doesn't support this kind of row value expression predicate anyway, in case of which you're on the safe side with EXISTS. At least these databases do support that predicate:
DB2
HSQLDB
MySQL
Oracle
Postgres
I recently encountered the following problem with buiding queries in jooq (version 3.1.0):
I want to build delete statement with order and limit constraints. So, my aim is to build something like this:
DELETE FROM table ORDER BY field DESC LIMIT 1 (this is MySql syntax)
But i haven't found nesessary methods in result delete query object:
DSLContext context = createContext();
DeleteWhereStep delete = context.delete(createTable(table));
DeleteConditionStep whereStep = delete.where(condition);
whereStep.orderBy(...)//and no such method here
There are all nesessary methods in select statements and none for delete.
Is it possible to set order and limit for delete request in jooq?
As of jOOQ 3.2, these sorts of extensions are currently not implemented yet. Chances are, that #203 could be implemented in jOOQ 3.3, though.
In the mean time, you have two options:
Resort to plain SQL
i.e. write something like:
context.execute("DELETE FROM {0} ORDER BY {1} DESC LIMIT 1",
createTable(table),
field);
Manually transform your SQL statement into something equivalent
I suspect that the ORDER BY .. LIMIT extension to the MySQL DELETE statement is just sugar for:
DELETE FROM table t
WHERE t.id IN (
SELECT id FROM table
ORDER BY field LIMIT 1
)
Or with jOOQ:
context.delete(TABLE)
.where(TABLE.ID.in(
select(TABLE.ID)
.from(TABLE)
.orderBy(TABLE.FIELD)
.limit(1)
))
I'm trying to build a web page to better learn Java and SQL. My question is, is there a way in Java to make a generic SQL select statement? For example:
SELECT var1 FROM var2 WHERE var3=var4
or something of the sort.
My idea is to fill the vars with user selected items from the web page. I know this can be done in PHP using the Post method, but I'm not using PHP. Also, I've read about the Prepared Statement in Java, but seems only to work when the used after the comparison operator; ex:
SELECT * FROM table Where attr = ? &
Also, I do know i can do the hard coded version of "SELECT " + var1 + "FROM " + var2 + "WHERE attr = " + var3 + " " but that doesn't seem very generic and prone to a lot of errors.
Incase: I'm trying to build this test page using HTML & JSP.
What you are doing with the ? is parameterizing the query. The query can only be parameterized for values not names of tables or columns.
Every time you run a query. The database has to create a query plan. If you are running the same query again and again, you can reduce this overhead by creating a PreparedStatement.
The first execution of PreparedStatement will generate the query plan. The subsequent executions will reuse the same plan.
Same query here means, it is identical in all respects except values used in where clause, expressions etc.
If you change the Column or Table name or modify the structure of the query, then it is a different query and will require a different query plan. A PreparedStement is not useful in this case and you should stick to the hardcoded version you talked about. Because of this reason you will get an error if you try to parameterize Table or Column names in PreparedStement.
Having said that. It is not advisable to take such a generic approach for queries. If your queries are that simple, you can benefit from ORM tools. You would not have to maintain even a line of SQL. For complex queries you have an option of using ORM specific query language or JPQL or Native SQL. Look for JPA + Hibernate
Your specific usage is not permitted by JDBC. You need to hard code the table name when creating the prepared statement. If you really do want to do that I suggest you use String concatenation to create the SQL statements and then create a PreparedStatement with parameters to handle the where part. In case you are wondering why bother with PreparedStatements in the specific solution, it's to avoid SQL injection.
You can use PreparedStatement to achive your objective.
For example -
String query = "SELECT * FROM table Where attr = ?";
PreparedStatement pt = con.prepareStatement(query);
pt.setString(1, attribete);
pt.executeUpdate();
There is no such direct provision in any of SQL packaged classes or others to replace table, column names along with query parameter values, in a query string, using a single method.
You require to depend on both PreparedStatement and any of String methods replace(...) and replaceFirst(...) to achieve your requirement.
String sql = "Select $1, $2 from $3 where $4=? and $5=?";
sql = sql.replaceFirst( "$1", "col1_name" );
sql = sql.replaceFirst( "$2", "col2_name" );
sql = sql.replaceFirst( "$3", "table_name" );
sql = sql.replaceFirst( "$4", "col4_name" );
sql = sql.replaceFirst( "$5", "col5_name" );
// .. and so on
PreparedStatement pst = con.prepareStatement( sql );
// use relevant set methods to set the query parametrs.
pst.setXXX( 1, value_for_first_query_parameter ); // from a variable or literal
pst.setXXX( 2, value_for_second_query_parameter); // from a variable or literal
// ... and so on
If you are using JDBC, can try this
PreparedStatement statement = connection.prepareStatement("SELECT ? FROM ? WHERE ?=? ");
then
statement.setString(1, "column_name");
statement.setString(2, "table_name");
statement.setString(3, "column_name");
statement.setBigDecimal(4, 123);
If you are using other ORM like Hibernate or JPA, I believe there are also ways to do.
I am trying to write query with multiple select subnets in it.But I defined a nativequery
I am giving error. Compiler specifies that "(" after "from" is not proper. How can I define
a native query in JPA 2.0
For eaxmple:
SELECT *
from (SELECT ****C) REI3 where column1 != 1
GROUP BY REI3.column2 order by REI3.column3 ASC
JPA does not have too much to do with validating SQL syntax, query is passed to JDBC driver. Likely you are trying run query such a way, that it is interpreted as JP QL. Instead try following method to execute it as
Query q = em.createNativeQuery("Your SQL here");
Other alternative is to use NamedNativeQuery Example