Can I run an "explain analyze" on a query using JOOQ? - java

Can I run explain analyze on a query in JOOQ? like:
explain analyse select some, columns from some_table
but do it using JOOQ on PostgreSQL database?
I have found an interface org.jooq.Explain, with a method DSLContext.explain​(Query query) - but it seems just to use EXPLAIN on a query:
#Support({AURORA_MYSQL,AURORA_POSTGRES,H2,HSQLDB,MARIADB,MEMSQL,MYSQL,ORACLE,POSTGRES,SQLITE})
Explain explain​(Query query)
Run an EXPLAIN statement in the database to estimate the cardinality of the query.
Is there any sensible way to run an EXPLAIN ANALYZE on the database from the code side?

Yes you can run explain. Example
SelectWhereStep<ModuldefRecord> where = dsl.selectFrom(MODULDEF);
Explain explain = dsl().explain(where);
System.out.println(explain);
The output look like this (for Oracle)
+------------------------------------------------------------------------------+
|PLAN_TABLE_OUTPUT |
+------------------------------------------------------------------------------+
|Plan hash value: 3871168833 |
| |
|------------------------------------------------------------------------------|
|| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time ||
|------------------------------------------------------------------------------|
|| 0 | SELECT STATEMENT | | 61303 | 30M| 1305 (1)| 00:00:01 ||
|| 1 | TABLE ACCESS FULL| MODULDEF | 61303 | 30M| 1305 (1)| 00:00:01 ||
|------------------------------------------------------------------------------|
+------------------------------------------------------------------------------+
Explain also contains rows and cost
/**
* The number of rows (cardinality) that is estimated to be returned by the query.
* <p>
* This returns {#link Double#NaN} if rows could not be estimated.
*/
double rows();
/**
* The cost the database associated with the execution of the query.
* <p>
* This returns {#link Double#NaN} if cost could not be retrieved.
*/
double cost();

It's not supported yet: https://github.com/jOOQ/jOOQ/issues/10424. Use plain SQL templating, instead:
ctx.fetch("explain analyze {0}", select);

For mariadb I needed to do:
SelectConditionStep<TableNameRecord> select =
context.selectFrom(Tables.TABLE_NAME)
.where(filter);
System.out.println(context.fetch("analyze " + select.getSQL(ParamType.INLINED)));
which produced the output:
+----+-----------+----------+-----+-----------------+-----------------+-------+------+----+-------+--------+----------+------------------------+
| id|select_type|table |type |possible_keys |key |key_len|ref |rows|r_rows |filtered|r_filtered|Extra |
+----+-----------+----------+-----+-----------------+-----------------+-------+------+----+-------+--------+----------+------------------------+
| 1|SIMPLE |table_name|range|table_column_name|table_column_name|20 |{null}|1000|1000.00| 100.0| 100.0|Using where; Using index|
+----+-----------+----------+-----+-----------------+-----------------+-------+------+----+-------+--------+----------+------------------------+
If you use context.explain(select) as proposed by another answer you lose a few columns:
+----+-----------+----------+-----+-----------------+-----------------+-------+------+----+------------------------+
| id|select_type|table |type |possible_keys |key |key_len|ref |rows|Extra |
+----+-----------+----------+-----+-----------------+-----------------+-------+------+----+------------------------+
| 1|SIMPLE |table_name|range|table_column_name|table_column_name|20 |{null}|1000|Using where; Using index|
+----+-----------+----------+-----+-----------------+-----------------+-------+------+----+------------------------+

Related

Hibernate Query - Complex

I'm dabbling in SQL at the moment and would love some help on a problem that I've created.
To practice some programming i'm making an IOU app. Below is the table I store
my IOU records (ignoring some relevant columns). This table allows users to say "Hey, you owe me X amount of money" and "I owe you X amount of money".
| Creator | Type | User_Involved| Amount |
|:-----------|------------:|:------------:|
| 1 | 0 | 2 | 3.0
| 2 | 0 | 1 | 4.0
| 3 | 1 | 1 | 5.0
Note: Type represents if the user has "lent or requested money".
0 = Lent
1 = Requested
Goal: My goal is to get a list of all Users and their total owed amount (negative or positive).
This has become quite difficult as I can't do a simple group by, as there are two columns i need to group on (Creator and User_Involved).
I managed to write an SQL query that gives the correct results, however i'm unable to translate it into a Hibernate version.
The issue mostly comes down to JPA unable to perform unions.
The Query in question:
/** assumption 1 is owed to creator **/
select sum(owed_to_you) as owed_to_you, friend_id
from
(
/** you created **/
select sum(CASE WHEN transaction_type = 0 THEN -amount ELSE amount END) as owed_to_you, friend_involved as friend_id
from iou
where
creator = 3
group by friend_involved
union all
/** someone else created **/
select sum(CASE WHEN transaction_type = 1 THEN -amount ELSE amount END) as owed_to_you, creator as friend_id
from iou
where
friend_involved = 3
group by creator) as theunion
group by friend_id
Besides loading all Ious into memory and sorting it that way, I'm completely stumped. I've done a lot of research today and learned a lot, however, i still have not made progress.
Any help would be much appreciated.
You can execute the two queries separately and combine the results in Java-code.

cassandra high volume writes sometimes silently fail

I am recording realtime trade data with the Datastax Cassandra java driver. I have configured Cassandra with a single node, replication factor of 1, and consistency level ALL.
I frequently have writes which do not record, but do not fail. The java client does not throw any errors, and the async execute successful callback is called. Trace doesn't seem to show anything unusual:
[CassandraClient] - Adding to trades memtable on /10.0.0.118[SharedPool-Worker-1] at Mon Dec 22 22:54:04 UTC 2015
[CassandraClient] - Appending to commitlog on /10.0.0.118[SharedPool-Worker-1] at Mon Dec 22 22:54:04 UTC 2015
[CassandraClient] - Coordinator used /10.0.0.118
but, when I look at the data in the cassandra shell, notice the skipped IDs (ignoring bad dates):
cqlsh:keyspace> select * from trades where [...] order by date desc limit 10;
date | id | price | volume
--------------------------+--------+--------+------------
1970-01-17 19:00:19+0000 | 729286 | 435.96 | 3.4410000
1970-01-17 19:00:19+0000 | 729284 | 436.00 | 17.4000000
1970-01-17 19:00:19+0000 | 729283 | 436.00 | 0.1300000
1970-01-17 19:00:19+0000 | 729277 | 436.45 | 5.6972000
1970-01-17 19:00:19+0000 | 729276 | 436.44 | 1.0000000
1970-01-17 19:00:19+0000 | 729275 | 436.44 | 0.9728478
1970-01-17 19:00:19+0000 | 729274 | 436.43 | 0.0700070
1970-01-17 19:00:19+0000 | 729273 | 436.45 | 0.0369260
1970-01-17 19:00:19+0000 | 729272 | 436.43 | 1.0000000
1970-01-17 19:00:19+0000 | 729271 | 436.43 | 1.0000000
why do some inserts silently fail? indications point to a timestamp issue, but I don't detect a pattern.
similar question: Cassandra - Write doesn't fail, but values aren't inserted
might be related to: Cassandra update fails silently with several nodes
The fact that the writes succeed and some records are missing is a symptom that C* is overwriting the missing rows. The reason you may see such behavior is the misuse of bound statements.
Usually people prepare the statements with:
PreparedStatement ps = ...;
BoundStatement bs = ps.bind();
then they issue something like:
for (int i = 0; i < myHugeNumberOfRowsToInsert; i++) {
session.executeAsync(bs.bind(xx));
}
This will actually produce the weird behavior, because the bound statement is the same across most of the executeAsync calls, and if the loop is fast enough to enqueue (say) 6 queries before the driver fires the first query at all, all the submitted queries will share the same bound data. A simple fix is to actually issue different BoundStatement:
for (int i = 0; i < myHugeNumberOfRowsToInsert; i++) {
session.executeAsync(new BoundStatement(ps).bind(xx));
}
This will guarantee that each statement is unique and no overwrites are possible at all.

How to process columns of an SQLite table in Java android?

I have an SQLite table like:
+---+-------------+----------------+
|_id| lap_time_ms |formatted_elapse|
+---+-------------+----------------+
| 1 | 5600 | 00:05.6 |
| 2 | 4612 | 00:04.6 |
| 3 | 4123 | 00:04.1 |
| 4 | 15033 | 00:15.0 |
| 5 | 4523 | 00:04.5 |
| 6 | 6246 | 00:06.2 |
Where lap_time_ms is an of the type long and represents the amount of time in milliseconds for a lap while formatter_elapse is a String that represents the formatted (displayable) form of the first column (elapse).
My question is that if I want to add (say) 5 seconds (5000) to each lap_time_ms then I use a statement like:
DB.execSQL("update table_name set KEY_ELAPSE=KEY_ELAPSE+5000);
Which works fine however the problem is that formatted_elapse still retains its outdated value!
So, what is the best way to update the values in the formatted_elapse column if I have a function like:
public static String getFormattedTime(long milliseconds) {
// custom code that processes the long
return processedString;
}
It may be a long shot (metaphorically speaking of course ;) but is it possible to have SQLite link the two columns such that if I update a lap_time_ms row, the formatted_elapse will automatically update appropriately.
Thanks in advance!
In theory, it would be possible to create a trigger to update that column, but only if the formatting can be done with some built-in SQLite function (Android does not allow user-defined functions):
CREATE TRIGGER update_formatted_elapse
AFTER UPDATE OF lap_time_ms ON MyTable
FOR EACH ROW
BEGIN
UPDATE MyTable
SET formatted_elapse = strftime('%M:%f', NEW.lap_time_ms, 'unixepoch')
WHERE _id = NEW._id;
END;
However, it would be bad design to store the formatted string in the database; it would be duplicated information that is in danger of becoming inconsistent.
Drop the formatted_elapse column and just call getFormattedTime in your code whenever you need it.

sql insert statement to set rows proportional

I have a table with the following data (ofcourse there are many rows).
metric_id(int) | employee_id (string) | quota (double)
1 | abcd | 100
1 | wxyz | 120
What i want to do is a SQL INSERT that takes all values with a specific metric_id.
select * from mytable where metric_id=1;
Then use the results to create new entries with different metric_id that will have the quota value set at a specific proportion. (ie: metric_id=2 quota is 50% of metric_id=1)
So my end table after the insert would look like
metric_id(int) | employee_id (string) | quota (double)
1 | abcd | 100
1 | wxyz | 120
2 | abcd | 50
2 | wxyz | 60
Can this be done in one sql statement? I would like to use prepared statement to prevent inject if possible.
Try this. It should be what you need.
insert into some_table
(metric_id, employee_id, quota)
select
2, employee_id, quota * 0.5
from
some_table
where
metric_id = 1
More alternative way (no where clause needed):
INSERT INTO TableName
(metric_id,employee_id,quota)
SELECT metric_id+1,employee_id,quota/2
FROM TableName

Criteria joining two tables using more than one parameter

I have two tables which are related:
+-----------+ +----------+
| First | * 1 | Second |
+-----------+ --------- +----------+
| fk_Second | | id |
| version | | ... |
| ... | | y |
| x | +----------+
+-----------+
Hibernate has a ManyToOne definition from First to Second. The {fk_Second, version} is a composite-id of the First table (although I don't think it's relevant in this case).
I am trying to write Criteria call, which in SQL would look like as:
SELECT * FROM First WHERE
First.fk_Second = Second.id AND
First.x = Second.y
I'm finding trouble in generating the last bit - the extra join condition.
Criteria c = session.createCriteria(First.class);
.createCriteria("second", "join_between_first_and_second")
.add(Restrictions.eqProperty("y", "x") // <- fails: "x" is not found
I can not use HQL queries in this situation. Is there any way writing this differently? Can this be written avoiding subquery/DetachedCriteria?
Criteria c = session.createCriteria(First.class, "first");
c.createAlias("first.second", "theSecond");
c.add(Restrictions.eqProperty("first.x", "theSecond.y");
If you don't prepend an alias to your property, the property is considered part of the root entity of the criteria (First in this case).
Try HQL 'With' clause..
SELECT f.* FROM First f left join Second s ON f.fk_Second = s.id with f.x = s.y;

Categories