JPA date truncation group by using POSTGRES and eclipselink - java

I am trying to truncate a date, and group by the values.
I have tried this:
JPA
select v.sop, FUNC('DATE_TRUNC', 'day', v.scrappedAt) as dt, sum(v.totalValue)
from TABLE v
where v.coordStatus like 'done%' and (:plant is null or v.target =
:plant) and v.scrappedAt is not null
group by v.sop, dt
I have also tried grouping by the FUNC
select v.sop, FUNC('DATE_TRUNC', 'day', v.scrappedAt) as dt, sum(v.totalValue)
from TABLE v
where v.coordStatus like 'done%' and (:plant is null or v.target =
:plant) and v.scrappedAt is not null
group by v.sop, FUNC('DATE_TRUNC', 'day', v.scrappedAt)
The error is the same
org.postgresql.util.PSQLException:
ERROR: column "t_mcp_verschrottungs_db_sharepoint.scrappedat"
must appear in the GROUP BY clause or be used in an aggregate function

Strictly speaking, if FUNC were a native Postgres function, then your query should be working, and the query would be ANSI compliant. After reading this SO question, it appears that Postgres can't figure out that the two FUNC calls are actually the same thing. Perhaps you can rephrase the sum using a correlated subquery:
SELECT
v1.sop,
FUNC('DATE_TRUNC', 'day', v1.scrappedAt) AS dt,
(SELECT SUM(totalValue) FROM TABLE v2
WHERE v2.sop = v1.sop AND
FUNC('DATE_TRUNC', 'day', v2.scrappedAt) =
FUNC('DATE_TRUNC', 'day', v1.scrappedAt) AND
v2.acoordStatus LIKE 'done%' AND
(:plant is null OR v2.target = :plant) AND
v2.scrappedAt IS NOT NULL) value_sum
FROM TABLE v1
WHERE
v1.coordStatus LIKE 'done%' AND
(:plant is null OR v1.target = :plant) AND
v1.scrappedAt IS NOT NULL;
An alternative to the above, should it either not work or not be performant, would be to use a native Postgres query.

Related

jOOq MERGE In Oracle

It feels like I'm close, but I cannot figure out how to do something like the below in jOOq.
MERGE INTO USER_ASSIGNMENTS ua
USING (
SELECT core_object_id
FROM core_objects
WHERE exists(SELECT *
FROM LKU_CODE lc JOIN LKU_CODE_TYPE lct
ON lc.LKU_CODE_TYPE_ID = lct.LKU_CODE_TYPE_ID AND lct.CODE_TYPE = 'OBJECT_TYPE' AND
lc.CODE = 'PORTFOLIOS'
WHERE lc.LKU_CODE_ID = core_objects.OBJECT_TYPE_ID) AND object_id = 83
) "co"
ON (ua.CORE_OBJECT_ID = "co".CORE_OBJECT_ID AND USER_ID = 24 AND SECTION = 1)
WHEN MATCHED THEN UPDATE
SET create_date = sysdate, created_by = '24', capabilities = 12
WHERE capabilities <> 12
WHEN NOT MATCHED THEN INSERT
(CAPABILITIES, CORE_OBJECT_ID, CREATE_DATE, CREATED_BY, SECTION, USER_ID)
VALUES (5, "co".CORE_OBJECT_ID, sysdate, '24', 1, 24);
The big thing to note is that I'm trying to use the value returned by USING, so I have to alias it and .values() has to accept a field call. I think I can get around the .values() issue using the .values(Collection<?>) call, bundling things, including that field, into a Collection, so I think that I have that part. What concerns me is that I cannot do an .as() call after .using(). If I make the USING query a "table" via .asTable(), supplying an alias, will that let me call the field? Here's kind of what I have at the moment:
Table<Record1<BigDecimal>> usingStatement = readContext
.select(_co.CORE_OBJECT_ID)
.from(_co)
.where(DSL.exists(readContext.select(_lc.fields()).from(
_lc.join(_lct).onKey(Keys.LC_LCT___FK)
.and(_lc.CODE.equal(capability.getObjectTypeCode()))
.and(_lct.CODE_TYPE.equal(LkuCodeTypeLookup.OBJECT_TYPE))))).asTable("sdf");
...
return writeContext
.mergeInto(_ua)
.using(usingStatement)
.on(sectionalConditions.and(_ua.CORE_OBJECT_ID.equal(coidField)))
.whenMatchedThenUpdate()
.set(_ua.CREATE_DATE, time)
.set(_ua.CREATED_BY, creator)
.set(_ua.CAPABILITIES, capabilities)
.where(_ua.CAPABILITIES.notEqual(capabilities))
.whenNotMatchedThenInsert(_ua.CAPABILITIES, _ua.CORE_OBJECT_ID, _ua.CREATE_DATE,
_ua.CREATED_BY, _ua.SECTION, _ua.USER_ID)
.values(capabilities, gcoid, time, creator, section, uuid).execute();
A "straight merge" using dual is simple in jOOq, but I'd like to try to combine that select into the merge to save queries and let the DB do what it does best, so I'm trying not to have to get core_object_id in another query, if possible.
The aliasing really happens on the table (i.e. the select), not on some artefact returned by the USING clause. At least, that's how jOOQ models it. You have already correctly aliased your usingStatement variable. Now all you have to do is dereference the desired column from it, e.g.:
usingStatement.field(_co.CORE_OBJECT_ID);
This will look for the column named CORE_OBJECT_ID in the usingStatement table.

Using two fields with "in" operator in QueryDSL

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

jOOQ problems with limit..offset - no values sets

I am trying to build a query using jOOQ, this is my test code:
DSLContext create = DSL.using(SQLDialect.DERBY);
String query = create.select().from(TABLE).limit(1).offset(0).getSQL()
I get as query:
select field1, field2...fieldN etc from TABLE offset ? rows fetch next ? rows only
the problem is ? in ? rows fetch next ? rows only it seems to ignore the values that i used in limit and offset to build the query, why?
I am trying to select the first row from the results and I am using jooq 3.4.1
Thanks for the help
Query.getSQL() returns your SQL string with ? as placeholders for your bind variables. The idea is that you can feed this statement to a PreparedStatement and then explicitly bind all variables, which are available through Query.getBindValues().
You can also have jOOQ inline all your bind variables, by calling Query.getSQL(ParamType) as such:
String sql = query.getSQL(ParamType.INLINED);

Convert Postgresql query to Hibernate

In my Java Web application I use Postgresql and some data tables are filled automatically in server. In the database I have a STATUS table like below:
I want to select the data related to a vehicle between selected dates and where the vehicle stayed connected. Simply I want to select the data which are green in the above table which means I exactly want the data when firstly io1=true and the data when io1=false after the last io1=true. I have postgresql query statement which exactly gives me the desired data; however, I have to convert it to HQL because of my application logic.
working postgresql query:
WITH cte AS
( SELECT iostatusid, mtstrackid, io1,io2,io3, gpsdate,
(io1 <> LAG(io1) OVER (PARTITION BY mtstrackid
ORDER BY gpsdate)
) AS status_changed
FROM iostatus
WHERE mtstrackid = 'redcar' AND gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59'
)
SELECT iostatusId, mtstrackid, io1, io2, io3,gpsdate
FROM cte
WHERE status_changed
OR io1 AND status_changed IS NULL
ORDER BY gpsdate ;
How should I convert the above query to HQL or how could I retrieve the desired data with HQL?
The goal of hibernate is mapping database entities to java objects. This kind of complex queries are not entities themselves. This is against the spirit of hibernate.
If this query generates an entity in your application logic, I recommend putting the results into a table and applying Hibernate queries to that table.
If this query generates some kind of aggregation or summary, there are two possible ways:
One way is you compute this aggregation/summary in your application after retrieving entities from iostatus table with hibernate.
If this query has nothing to do with your application logic then you can use Native SQL interface of Hibernate and execute the query directly. (You can even use JPA if you are willing to manipulate two database connections.)
If you absolutely need to convert it to HQL, you need to eliminate the partition function. If the order of iostatusId is identical to the order of gpsdate, you can do it similar to
SELECT i2.*
FROM iostatus i1
INNER JOIN iostatus i2 ON i1.iostatusId = i2.iostatusId - 1
AND i1.io1 <> i2.io1
AND i1.mstrackid = i2.mstrackid
WHERE i2.mtstrackid = 'redcar' AND
i2.gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59'
If gpsdate is no way related to iostatusId then you need something like
SELECT i2.*
FROM iostatus i1
INNER JOIN iostatus i2 ON i1.gpsdate < i2.gpsdate
AND i1.io1 <> i2.io1
AND i1.mstrackid = i2.mstrackid
WHERE i2.mtstrackid = 'redcar' AND
i2.gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59' AND
NOT EXISTS (SELECT * FROM iostatus i3
WHERE i3.gpsdate > i1.gpsdate AND
i2.gpsdate > i3.gpsdate AND
i3.io1 = i1.io1 AND
i1.mstrackid = i3.mstrackid)
I guess both of the queries can be converted to HQL, but I'm not positively sure.
By the way I must warn you that, these methods might not perform better then finding the changes in your application, because they involve joining the table onto itself, which is an expensive operation; and the second query involves a nested query after the join, which is also quite expensive.

How to construct advanced Hibernate Query with OR and summarize functions

I have a rather complex query which works in SQL, but I would like to express this in HQL for portability. I'm going to fetch a user configured preference value if they exist, if not I must use a default value. This value must be subtracted from current date and the matched against a column in the table which I'm interested in:
select d.id, d.guid, d.deletetimestamp, u.id
from descriptor d, ownerkey ow, user u
where
d.parentid in
(select td.id
from descriptor td, store s
where s.type = 'Trash'
and s.descriptorid = td.id
)
and d.ownerkeyid = ow.id
and ow.ownerid = u.id
and
(
(d.deletetimestamp < CURRENT_TIMESTAMP() - INTERVAL
(select pv.value
from preferencevalue pv, userpreference up
where u.id = up.userid
and up.preferenceid = 26
and up.value = pv.id)
DAY)
or
(d.deletetimestamp < CURRENT_TIMESTAMP() - INTERVAL
(select cast(pv.value as SIGNED)
from preferencevalue pv, defaultpreference dp
where dp.preferenceid = 26
and not exists(select up.userid from userpreference up where u.id = up.userid and up.preferenceid = 26)
and dp.value = pv.id)
DAY)
)
I'm trying to construct this by using the Criteria API which seems to include most of the logical operators that I need (equals, larger than, or, isEmpty/isNull), but not sure how I would express all these parts.
Using a view is not an option at this point since we're using MySQL as the production database while the integration tests are running with H2 inmemory database. I'm not able to get find the sata substract function in H2 while MySQL do support this.
The select fields isn't important since they have only been used for testing purposes.
you can use Restrictions.disjunction() for or -and Restrictions.conjuction() for and clauses.
To reference a certain property of an entity (like pv.value) you can use Projections.property("value")
for the casting I'm not sure, perhaps using the #Formula annotation on your entity? But this is a hibernate and not a JPA annotation.
as far as I know there is no equivalent for INTERVAL in hibernate but in such cases (maybe also for the above cast) you could use Restrictions.sqlRestriction("some sql...")
It will be a challenge putting all of this together to transform your query to hibernate criteria.
greetz,
Stijn

Categories

Resources