How do I sort, group a query properly that returns a tuple of an orm object and a custom column? - sorting

I am looking for a way to have a query that returns a tuple first sorted by a column, then grouped by another (in that order). Simply .sort_by().group_by() didn't appear to work. Now I tried the following, which made the return value go wrong (I just got the orm object, not the initial tuple), but read for yourself in detail:
Base scenario:
There is a query which queries for test orm objects linked from the test3 table through foreign keys.
This query also returns a column named linked that either contains true or false. It is originally ungrouped.
my_query = session.query(test_orm_object)
... lots of stuff like joining various things ...
add_column(..condition that either puts 'true' or 'false' into the column..)
So the original return value is a tuple (the orm object, and additionally the true/false column).
Now this query should be grouped for the test orm objects (so the test.id column), but before that, sorted by the linked column so entries with true are preferred during the grouping.
Assuming the current unsorted, ungrouped query is stored in my_query, my approach to achieve this was this:
# Get a sorted subquery
tmpquery = my_query.order_by(desc('linked')).subquery()
# Read the column out of the sub query
my_query = session.query(tmpquery).add_columns(getattr(tmpquery.c,'linked').label('linked'))
my_query = my_query.group_by(getattr(tmpquery.c, 'id')) # Group objects
The resulting SQL query when running this is (it looks fine to me btw - the subquery 'anon_1' is inside itself properly sorted, then fetched and its id aswell as the 'linked' column is extracted (amongst a few other columns SQLAlchemy wants to have apparently), and the result is properly grouped):
SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name, anon_1.fk_test3 AS anon_1_fk_test3, anon_1.linked AS anon_1_linked, anon_1.linked AS linked
FROM (
SELECT test.id AS id, test.name AS name, test.fk_test3 AS fk_test3, CASE WHEN (anon_2.id = 87799534) THEN 'true' ELSE 'false' END AS linked
FROM test LEFT OUTER JOIN (SELECT test3.id AS id, test3.fk_testvalue AS fk_testvalue
FROM test3)
AS anon_2 ON anon_2.fk_testvalue = test.id ORDER BY linked DESC
)
AS anon_1 GROUP BY anon_1.id
I tested it in phpmyadmin, where it gave me, as expected, the id column (for the orm object id), then the additional columns SQL_Alchemy seems to want there, and the linked column. So far, so good.
Now my expected return values would be, as they were from the original unsorted, ungrouped query:
A tuple: 'test' orm object (anon_1.id column), 'true'/'false' value (linked column)
The actual return value of the new sorted/grouped query is however (the original query DOES indeed return a touple before the code above is applied):
'test' orm object only
Why is that so and how can I fix it?
Excuse me if that approach turns out to be somewhat flawed.
What I actually want is, have the original query simply sorted, then grouped without touching the return values. As you can see above, my attempt was to 'restore' the additional return value again, but that didn't work. What should I do instead, if this approach is fundamentally wrong?
Explanation for the subquery use:
The point of the whole subquery is to force SQLAlchemy to execute this query separately as a first step.
I want to order the results first, and then group the ordered results. That seems to be hard to do properly in one step (when trying manually with SQL I had issues combining order and group by in one step as I wanted).
Therefore I don't simply order, group, but I order first, then subquery it to enforce that the order step is actually completed first, and then I group it.
Judging from manual PHPMyAdmin tests with the generated SQL, this seems to work fine. The actual problem is that the original query (which is now wrapped as the subquery you were confused about) had an added column, and now by wrapping it up as a subquery, that column is gone from the overall result. And my attempt to readd it to the outer wrapping failed.

It would be much better if you provided examples. I don't know if these columns are in separate tables or what not. Just looking at your first paragraph, I would do something like this:
a = session.query(Table1, Table2.column).\
join(Table2, Table1.foreign_key == Table2.id).\
filter(...).group_by(Table2.id).order_by(Table1.property.desc()).all()
I don't know exactly what you're trying to do since I need to look at your actual model, but it should look something like this with maybe the tables/objs flipped around or more filters.

Related

JPA #Query count AND select

I have a somewhat complicated #Query in a JpaRepository.
I need to get the results of this query in two forms (but not at the same time!):
First, the client asks for a count of the number of results: SELECT COUNT(x.*) FROM my_table x ...
Then later (maybe), they want to see the actual data: SELECT x.* FROM my_table x ...
What follows (the ...) is identical for both queries. Is there any way to combine these so that I don't repeat myself?
I know I could just use the second method, and count the number of elements in the resulting List. However, this adds the overhead of actually fetching all those elements from the database.
I could put the ... in a String constant somewhere, but that kind of separates it from its context (I'd lose IntelliJ's syntax highlighting/error checking)
I can't convert it to a Criteria or Example query, because I need to use PostGIS's geography type. (And these are less readable anyway...)
Any other ideas?
If your worries is about some developer change the COUNT query and forgot to change the SELECT query too, you can create a repository integration test to guarantee the expected result between the two queries.
Another alternative is create a unit test to read the annotation content and verify if the final of these two queries are equal.

Concatenating a LINQ query and LINQ sort statement

I'm having a problem joining two LINQ queries.
Currently, my (original) code looks like this
s.AnimalTypes.Sort((x, y) => string.Compare(x.Type, y.Type));
What I'm needing to do is change this based on a date, then select all data past that date, so I have
s.AnimalTypes.Select(t=>t.DateChanged > dateIn).ToList()
s.AnimalTypes.Sort((…
This doesn't look right as it's not sorting the data selected, rather sorting everything in s.AnimalTypes.
Is there a way to concatenate the two LINQ lines? I've tried
s.AnimalTypes.Select(t=>t.DateChanged > dateIn).ToList().Sort((…
but this gives me an error on the Sort section.
Is there a simple way to do this? I've looked around and Grouo and OrderBy keep cropping up, but I'm not sure these are what I need here.
Thanks
From your description, I believe you want something like:
var results = s.AnimalTypes.Where(t => t.DateChanged > dateIn).OrderBy(t => t.Type);
You can call ToList() to convert to a List<T> at the end if required.
There are a couple of fundamental concepts I believe you are missing here -
First, unlike List<T>.Sort, the LINQ extension methods don't change the original collections, but rather return a new IEnumerable<T> with the filtered or sorted results. This means you always need to assign something to the return value (hence my var results = above).
Second, Select performs a mapping operation - transforming the data from one form to another. For example, you could use it to extract out the DateChanged (Select(t => t.DateChanged)), but this would give you an enumeration of dates, not the original animal types. In order to filter or restrict the list with a predicate (criteria), you'd use Where instead.
Finally, you can use OrderBy to reorder the resulting enumerable.
You are using Select when you actually want to use Where.
Select is a projection from one a collection of one type into another type - you won't increase or reduce the number of items in a collection using Select, but you can instead select each object's name or some other property.
Where is what you would use to filter a collection based on a boolean predicate.

hibernate - using 'having' without group by clause in hql

im trying to run an hql query which aggragets (sum) number of transactions made on a specific account, i dont need a group by since my where clause has a specific account filter (where account = :account)
i do, however, want to return the aggregated value only if it is smaller/bigger than some given value.
when im adding 'having' after the where clause without 'group by' im getting an error -
unexpected token: having
in native sql i succeeded adding 'having' without group by
any ideas on how to make it work with hql?
thanks alot
The reason why databases don't let you mix grouped columns with non-grouped and non-aggregated ones is, that for non-grouped/non-aggregated columns it would have to choose one row's value per group, but doesn't know how to pick one.
If you don't care, then you could just leave it away and if it doesn't matter because they're all the same, you could group by them, too.
It is not hql, but if you have native query, then run it like:
Query query = session.createSQLQuery("select, *** ,... blah blah")
//set If you need
query.setParameter("myparam", "val");
List result = query.list();
In my eyes this is nonsense. 'having' is done for conditions on a 'group by' result. If you don't group, then it does not make much sense.
I would say HQL can't do it. Probably the Hibernate programmers didn't think of this case because they considered it as not important.
And anyway, you don't need it.
If it is a simple query, then you can decide in your java code if you want the result or if you don't need it.
If it is in a subselect, then you can solve the problem with a where condition in the main select.
If you think it is really necessary then your invited to give a more concrete example.

Seam EntityQuery Many-to-Many Joins, Distinct, and Oracle

I'm a Seam newbie in an already established project, so a lot of code I use is borrowed and I'm not always fully sure how things work. My problem is that I am using a query object extended from EntityQuery to back a list page with search and sort capabilities that needs to search across a many-to-many relationship and a separate many-to-one relationship which must also be used to sort. Because the many-to-many relationship has to be joined in to allow for the search capability, the query returns duplicate records for each assignment. That's not a big deal because I just added "distinct" to the ejbql and that worked fine. However, when I try to order by the other many-to-one relationship, Oracle throws an error. It appears that Oracle will not accept an order by column that is not in the select clause when using the distinct keyword http://ora-01791.ora-code.com/, and http://oraclequirks.blogspot.com/2009/04/ora-01791-not-selected-expression.html.
Here are the relationships as they are defined in the entities: [Subject m:m JobFunction] (obviously through an assignment table [Subject o:m Subject_JobFunction m:o JobFunction]), and [Subject m:o Type]. Because I need to search Subject by JobFunction, it is joined in in the ejbql which requires the distinct keyword to only return distinct Subjects to the list page. When I try to order by the Type.name (through the many-to-one relationship), the resulting query makes Oracle angry and throws the "ORA-01791: not a SELECTed expression" error. SubjectQuery code:
#Override
public String getEjbql() {
return "select subject from Subject subject left outer join subject.jobFunctions as jobFunction";
}
#Override
#SuppressWarnings("rawtypes")
public List<ValueExpression> getRestrictions() {
ValueExpression[] RESTRICTIONS = {
createValueExpression("lower(subject.name) like #{subjectQuery.prepRestriction(subjectQuery.subject.name)}"),
createValueExpression("subject.active = #{subjectQuery.active}"),
createValueExpression("subject.type.name = #{subjectQuery.typeName}"),
createValueExpression("jobFunction.name = #{subjectQuery.jobFunctionName}")
};
return Arrays.asList(RESTRICTIONS);
}
When I set the query order when a user sorts by the Type name through the front end:
"#{subjectQuery.order=='UPPER(subject.type.name) asc'}"
I get the Oracle error. If I take the distinct out of the ejbql, the sort works fine, but I get duplicate Subject records. When I add the distinct keyword the list works fine without duplicate records, but the sort throws an error. Does anyone have any suggestions about how I can restructure the ejbql to return distinct records without the distinct keyword to make the sort happy, or how to do the sort without making Oracle angry that the sort column referenced in the query is not in the select clause? I have read several places that my answer might be in the the Hibernate Criteria API, but I have no idea how to leverage it in the context of an extended EntityQuery class with what I am trying to accomplish. Please Help!
If you are adding a DISTINCT, then something is broken.
"Because the many-to-many relationship has to be joined in to allow for the search capability, the query returns duplicate records for each assignment. "
Consider the case that a person can work on many projects and a project can have many persons. There is a uniqueness of a 'person/project'. If you want a list of people that work in either project A or B (or both) then you may get
FRED/PROJ_A
BILL/PROJ_A
FRED/PROJ_B
TOM/PROJ_B
BILL/PROJ_C
If you only show the names (not the projects), you can still order by project, but you will see
FRED
BILL
FRED
TOM
BILL
If you do a DISTINCT, you can no longer order by project, because you don't know whether the FRED is the one from PROJ_A or PROJ_B or whether BILL comes before TOM (based on PROJ_A) or after TOM (based on PROJ_C).
So remove the DISTINCT and always show the column on which you are ordering (because then you'll see why the duplicates aren't actually duplicates).
I'm not sure how the generated query(ies) is(are) different, but I found an answer. I wasn't aware of the fetch command for hibernate, which fixes the need for the distinct keyword (again, not sure exactly how, maybe by subquerying?). After changing the ejbql to:
#Override
public String getEjbql() {
return "select subject from Subject subject left join fetch subject.jobFunctions jobFunction";
}
the distinct is no longer needed and therefore, Oracle does not complain about the order by column not being in the select clause. The list works as expected and the sort column works! Yay!
Predictably, I found the answer here on stackoverflow. The question was not exactly the same, but the hql syntax worked for me: HQL order by within a collection

Linq-to-sql Not Contains or Not in?

I'm building a poll widget. I've 2 tables, call them Polls and PollsCompleted. I need to do a linq query to get all the Polls that do not exist for a given user in PollsCompleted.
I have the following sets:
For Polls
Where Active == True
For PollsCompleted
Where UserId == ThisUserId
Where PollId = Polls.Id
Now I need to get all Polls that do not exist in PollsCompleted. I need an example for this using either a single or multiple queries. I've tried to break it down into 2 queries.
Basically, I've 2 IQueryables of type T and T1. I want to take all T's where T.ID does not exist in T1.ParentId.
T.Where(x => ! T1.Select(y => y.ParentID).Contains(x.ID))
In Linq you often work from the bottom up. Here we first get a collection of all the parentIDs in T1 -- the T1.Select(...) part. Then we create a where clause that selects all of the Ts whose IDs are not contained in that set.
Note that the result is a query. To materialize it, use ToList() or similar on the statement above.
Use Except. That will work in this case.
For your reference Enumerable.Except Method

Resources