In mysql I want to execute a query like this
SELECT MIN(id) FROM table;
The more I read about JOOQ syntax and the aggregate functions, the confused I get.
I thought something like this would work
select( EVENT.EVENTID , min() ).from( EVENT ).fetch();
or
Result<Integer> er = context.select( EVENT.EVENTID.min()).fetch();
I tried a work around by selecting the whole first record
Result<EventRecord> er2 = context.selectFrom(EVENT).orderBy(EVENT.EVENTID.asc()).limit(1).fetch();
If the result has size 0, a record does not exist, but when it is not 0 I get the right record. I would like to use the min() function but can't get the syntax right.
The query you want to write in SQL is this one:
SELECT MIN(event.eventid) FROM event
This is why your two attempts didn't work
// 1. You cannot combine single columns with aggregate functions in SQL,
// unless you're grouping by those columns
// 2. You didn't pass any cargument column to the MIN() function
context.select( EVENT.EVENTID , min() ).from( EVENT ).fetch();
// 3. This doesn't specify any FROM clause, so your database won't know what
// table you want to select the MIN(eventid) from
context.select( EVENT.EVENTID.min()).fetch();
Note that these thoughts are not specific to jOOQ, they are related to SQL in general. When using jOOQ, always think of the SQL statement you want to express first (the one at the top of my answer). So your jOOQ statement would look like any of these:
// "Postfix notation" for MIN()
context.select(EVENT.EVENTID.min()).from(EVENT).fetch();
// "Prefix notation" for MIN(), where min() is static-imported from
// org.jooq.impl.DSL
context.select(min(EVENT.EVENTID)).from(EVENT).fetch();
It looks like the fetchAny() method will return the record with the first/lowest record id.
EventRecord record = context.selectFrom(EVENT).fetchAny();
As #LukasEder mentioned there are many alternative methods, and he may be generous and follow up on some of those. Thanks Lucas
Related
I am trying to batch updates using PreparedStatement where the data you get in varies in what you have available.
Simple example:
You have a table with column x and y. Your inputdata is a representation of rows where only the modified column is present. So for row 1 you have x='foo' and for row 2 you have y='bar'. In order to batch these with a PreparedStatement you need one SQL statement: "update table where x=?,y=? where etc".
The solution that I am working towards is setting the column where you dont have any value to the value that is already present, but I'm not sure if this is possible. In raw SQL you could write "update table where x=x and y='foo' where etc", however I haven't found any ways to achieve this by setting the "?" parameter to be a reference to the column, it seems it's not possible.
Is it possible to handle this case at all with PreparedStatements? I apologize if my explanation is poor.
Assuming the values you want to set are all non-null, you could use a statement like
update sometable set column1 = coalesce(?, column1), colum2 = coalesce(?, column2)
Then when the value should not be updated, use either PreparedStatement.setNull with an appropriate java.sql.Types value or a PreparedStatement.setXXX of the appropriate type with a null as value.
As discussed in the comments, an alternative if you do need to update to null, is to use a custom function with a sentinel value (either for updating to null or for using the current value). Something like:
update sometable set column1 = conditional_update(?, column1), colum2 = conditional_update(?, column2)
Where conditional_update would be something like (using Firebird 3 PSQL syntax):
create function conditional_update(new_value varchar(500), original_value varchar(500))
returns varchar(500)
as
begin
if (new_value = '$$NO_UPDATE$$') then
return original_value;
else
return new_value;
end
Where using $$NO_UPDATE$$ is a sentinel value for not updating. A potential downside of this solution is the typing of columns as a string type. You always need to use string types (I'm not sure if there are databases that would supports dynamic parameters and return types in this situation).
Let me rephrase your problem and you please tell me if this is right:
You want to be able to have the update process
sometimes you only update for a specific x
sometimes you only update for a specific y
sometimes you update for specific x and y combined
Correct? If 'yes' then the next issue is implementation possibilities.
You suggested trying to put the column itself into the parameter. Even if that's possible I'd still recommend against it. The code will get confusing.
I suggest you create a java method that builds and returns the query string (or at least the 'where' clause) based on your available parameters (x, y, etc)
Your code for invoking the JDBC Prepared statement will invoke that method to get a query String.
You will still benefit from using prepared statements. The cost is that your database will be caching several different statements instead of one, but I imagine that is a minimal issue.
You can think of auxiliar "?" variable for each column which will manage if the column should be updated or not.
update table
set x = case when 0 = ? then x else ? end,
y = case when 0 = ? then y else ? end
where etc;
Passing 0 for each 0 = ? will not update the column whereas passing 1 will update it to the value you specified.
I have an instance of JPAQuery<?> and need to retrieve the count. However, since the table may contain many items (millions), I want to limit the count to a given maximum, say 50,000.
The current QueryDSL-Code effectively does this:
query.fetchCount();
Now my desired modifications are quite trivial in raw sql:
select count(*) from (<whatever query> limit 50000);
However, I do not know how I would express this in querydsl. The following code is not correct, because .from() takes an entity path, but query is a query:
JPAExpressions.select(Wildcard.all)
.from(query.limit(50000))
.fetchCount();
I am using querydsl 4.
JPAExpressions.select(Wildcard.all) returns a child of SimplyQuery, which you can call limit on.
JPAExpressions.select(Wildcard.all)
.from(entity)
.limit(50000)
.fetchCount();
I'm using hibernate to call the Postgres array_agg function. The problem is that I need to call the function with an order by included. I cannot pass the order by through hibernate in the correct syntax so I need to create a function that does exactly what array_agg does with ordering being automatic. Here's what I need to duplicate in a function:
array_agg(table.TEXT order by table.text asc)
I've tried and tried but I cannot figure it out - and google isn't helping much. Thanks in advance!
You can substitute the missing feature by feeding an ordered set to the aggregate function - which works in Postgres but is not the standard SQL way to do this (as #Richard mentioned in his comment):
SELECT array_agg(sub.text) AS ordered_array
FROM (SELECT text FROM tbl ORDER BY text) sub
Or, for a simple case like this, just use the array constructor instead:
SELECT ARRAY( SELECT text FROM tbl ORDER BY 1 ) AS ordered_array
You should be able to implement that with the limited means of Hibernate.
I figured it out just by creating a custom aggregate function and using the final function (FFUNC)
CREATE OR REPLACE FUNCTION order_func(anyarray) RETURNS anyarray AS $BODY$
BEGIN
return ARRAY(SELECT unnest($1) ORDER BY 1);
END;
$BODY$ LANGUAGE 'plpgsql';
CREATE AGGREGATE array_agg_order_func(anyelement) (
SFUNC=array_append,
STYPE=anyarray,
FFUNC=order_func,
INITCOND='{}'
);
This will then pass the aggregated array to the sorting function.
So I have a MYSQL db in which boolean values are stored as binary(1). I was to investigate why certain queries were slow even though there was an index on the relevant columns. The issue was that when building the SELECT query, the system was using the setBoolean method of PreparedStatement which, as I understand it, converts the value to MYSQL TINYINT. The query found the correct rows, but never used the index since the index was on a binary column. However, if I instead used the setString method and converted the boolean to a string, namely '0' for false and '1' for true, MYSQL was able to use the index and find the wanted rows fast.
Basically, the first query is what I got when using setBoolean and the second when using setString:
SELECT someColumn FROM table WHERE binaryColumn = 1 //Does not use index
SELECT someColumn FROM table WHERE binaryColumn = '1'//Uses index
In Java the change was this:
PreparedStatement ps1 = ...
ps1.setBoolean(1, true);
...
PreparedStatement ps2 = ...
ps2.setString(1, "1");
...
My question is simply if there is a better way to do this? Everything works fine but for some reason I think the code "smells" but I cant really motivate why.
I prefer always the setBoolean, because of abstraction.
The real interesting point is when your DB uses the index.
The optimizier of the DB use a index only, if it makes sense. If you have 1000 entries and a booleanvalue only split it into 50/50 it make no sense for that index, especial when its not the PK - but if you use a additional limitation, to get only 10 rows, as result, a good optimizer should use the index you specified - maybe a "composed index" on 2 columns (booleanColumn1, StringColumn1)
MySQL uses TINYINT(1) for the SQL BOOL/BOOLEAN. So I would change the data type to BOOLEAN, in accordance to standard SQL.
By your relay, the issue should then be resolved. By the way BIT(1) would be another option.
This is a very simplified version of what i'm working on but hopefully it will get my point across.
I have a mysql table which looks something like this:
CREATE TABLE Table1(
ID INT AUTO_INCREMENT,
Val VARCHAR(50),
PRIMARY KEY (id)
);
INSERT INTO Table1 (Val) SELECT "Reference.Int('ABCD')";
INSERT INTO Table1 (Val) SELECT "Reference.Str('EFG','ABC')";
INSERT INTO Table1 (Val) SELECT "Reference.Int('HIJ','EFG','ABC')";
The method i'm working on receives as parameter one of the values in the brackets, for example: "EFG". Is it possible for me to grab all the rows in the table which contain this value. I am aware that if i do something like:
SELECT * from Table1 where Val LIKE "%EFG%"
i can get the right values, my problem is that i need to be more specific because for example one of the values can look something like :
Reference.Int('ABCD') + EFGX/200
or
EFG + anything else
Meaning that i need to somehow include this parts also: "Reference.Str()". I don't really care about anything else that is in the brackets, my only concern is to get the ones which contain the value of the parameter i receive.
I was wondering if this could be solved by using REGEXP, but my knowledge of this is weak at best.
Any help is appreciated.
You can use regexp, but there's no need. Just include the quotes surrounding your values in the search term:
select ...
where val like '%\'EFG\'%'
I don't think this is a good fit for a SQL query. You're probably best off creating a second table that stores tokens found in strings, and a reference back to the parent record.
So you could do something like
select record_id from tokens where token_val = 'EFG';
and then
select * from records where record_id in (*results from pervious query)
Basically the idea is to do the hard work up front, ONCE per record, rather than trying to parse on the fly on every query.
Try this:
SELECT * FROM Table1 WHERE Val LIKE "Reference.Str(%'EFG'%"
SQL FIDDLE DEMO
I think you could use SUBSTRING_INDEX to get only the part of the strings between brackets:
SUBSTRING_INDEX(SUBSTRING_INDEX(Val, "(", -1), ")", 1)
and then you can use FIND_IN_SET:
SELECT *
FROM Table1
WHERE
FIND_IN_SET(
'\'EFG\'',
SUBSTRING_INDEX(SUBSTRING_INDEX(Val, "(", -1), ")", 1)
)
See it here.