hibernate sql query with distinct and order by - sql

I have this query:
"select distinct d from Dance d " +
"inner join d.meisterschaftDances ms " +
"inner join ms.danceRegistrations dr " +
"inner join dr.user u " +
"where (u.id = :userId " +
"or d.user.id = :userId) " +
"AND ms.meisterschaft.open = FALSE " +
"order by ms.meisterschaft.organizer.id, " +
"d.discipline, d.age, d.category, " +
"d.class"
The logic would be perfect for me but as we know distinct is not so easy with order by.
I get the same dance several times because the inner join to meisterschaftDances can have several references.
Sadly I dont know how to rewrite my query in such a way that it is working. Can somebody show me an example which would work. Thanks

As it's stated in the hibernate documentation:
For JPQL and HQL, DISTINCT has two meanings:
It can be passed to the database so that duplicates are removed from a result set
It can be used to filter out the same parent entity references when join fetching a child collection
You are interested in the second case, so, you need to add the QueryHints.HINT_PASS_DISTINCT_THROUGH hint, like below:
List<Dance> dances = entityManager.createQuery(
"select distinct d " +
"from Dance d " +
"inner join d.meisterschaftDances ms ...", Dance.class)
.setHint( QueryHints.HINT_PASS_DISTINCT_THROUGH, false )
.getResultList();
P.S. But the above approach may be not workable in some hibernate versions due to bugs (See for example HHH-13517 and HHH-13280)

Related

Getting COUNT from an exisiting resultset

I have are resultset based on this SELECT statement:
"SELECT ca.mem_no, me.mem_surname, me.mem_first_name, " +
"lcc.call_cat, ca.call_location, ca.caller_name, lsc.call_sub_cat, ca.call_id, " +
"call_date_start, ca.call_note_start, ca.call_date_end, ca.call_duration, lcs.call_status, " +
"lca.call_action, lcr.call_res, ca.call_note_res\n" +
"FROM tblCall ca\n" +
"INNER JOIN tlkpCallStatus lcs on lcs.callstatus_id = ca.callstatus_id\n" +
"INNER JOIN tlkpCallAction lca on lca.callaction_id = ca.callaction_id\n" +
"INNER JOIN tlkpCallResolution lcr on lcr.callres_id = ca.callres_id\n" +
"LEFT OUTER JOIN tlkpCallSubCategory lsc on lsc.callsubcat_id = ca.callsubcat_id\n" +
"INNER JOIN tlkpCallCategory lcc on lcc.call_cat_id = ca.call_cat_id\n" +
"LEFT OUTER JOIN tblMember me on me.mem_no = ca.mem_no\n" +
"INNER JOIN tblClient cl on cl.client_ident = me.client_ident\n" +
"WHERE me.client_ident = \'AVA\'" +
"AND ca.call_date_start BETWEEN '2017-02-01' AND '2017-02-28'\n" +
"ORDER BY date(ca.call_date_start);\n"
;
which will rst.next() to a speadsheet ... i realize i can get the count of rows by waiting for the processing to finish, however i need the row count prior to writing the report .. i am faced with writing another pre sql statement getting COUNT(*) based upon the same JOIN and WHERE conditions. But i don't want to have two copies of basically the one sql statement ..
Is there a way (JAVA, Sqlite3) i can "SELECT COUNT(*)" from the existing resultset? ... seems a waste to have to go and get them all again just to be able to count the rows. :)
There is a way to probe a JDBC ResultSet and find out how many records are in that result set. But, it might cause the entire result set to be read across the network. Therefore, I vote for just running a separate COUNT(*) query if you really need to find the count. From a network usage point of view, this is very cheap, because you are just asking for a single number to be sent across.
There is also a SQL solution here, which unfortunately is not available for SQLite, which does not support analytic functions (yet). You could use the following query:
SELECT
ca.mem_no,
me.mem_surname,
me.mem_first_name,
COUNT(*) OVER () AS total_record_count -- change here
...
FROM
That is, we can use COUNT as an analytic function to find the total record count at the same time as running the rest of your original query. Again, not available for SQLite, but might be an option for other databases.

SQL - select 5 columns where 3 are distinct using multiple JOINs

I searched Stack Overflow before asking and looked through the 5 most relevant questions, but they did not seem to answer this.
I need to select 5 columns from a table using multiple INNER JOINs, but I only want to get the records where 3 of the 5 columns are distinct. If I use:
"select DISTINCT pos_segments.pos, ",
" pos_segments.org, ",
" pos_segments.obj, ",
" pos_segments.proja, ",
" pos_segments.eff_dt ",
" from pos_segments ",
"INNER JOIN PersonnelPositions ",
"ON pos_segments.pos = PersonnelPositions.Position ",
"AND pos_segments.eff_dt = PersonnelPositions.EffectiveDate ",
"INNER JOIN Accounts ",
"ON PersonnelPositions.PayrollGLAccountId = Accounts.Id ",
" WHERE <where clause here>
I get back over 22k records. I need to get the pos_segments columns only where the combination of the following three columns are distinct:
pos_segments.pos, pos_segments.proja, pos_segments.eff_dt
These three columns taken together serve as a unique key for this table. How can I only get back the records which are distinct based on the combination of these three columns?
P.S. - we are using MS SQL Server
Thanks!
Edit: As Sean pointed out, the ORDER BY statement is not optional. I never tried without it so I wasn't sure. And it would appear that the code block below is more of a suggestion than a copy paste so take it with a grain of salt. However, this approach should still work with some tweaking to fit your application. Since you mentioned that it may be arbitrary which distinct row is grabbed, you can put either of the other two columns after the ORDER BY statement.
I very recently needed to solve a very similar issue. I was able to accomplish what I needed by using a partition in conjunction with a where statement. You code would be modified to look like this:
"select * from ("
"select ROW_NUMBER() OVER (PARTITION BY pos_segments.pos,
pos_segments.proja, pos_segments.eff_dt ORDER BY pos_segments.org)
AS row_num_throwaway"
" pos_segments.pos, ",
" pos_segments.org, ",
" pos_segments.obj, ",
" pos_segments.proja, ",
" pos_segments.eff_dt ",
" from pos_segments ",
"INNER JOIN PersonnelPositions ",
"ON pos_segments.pos = PersonnelPositions.Position ",
"AND pos_segments.eff_dt = PersonnelPositions.EffectiveDate ",
"INNER JOIN Accounts ",
"ON PersonnelPositions.PayrollGLAccountId = Accounts.Id ",
" WHERE <where clause here>)"
"WHERE row_num_throwaway = 1"
That first row_number partition line just finds distinct versions of the three columns specified and assigns a row number that counts up as more instances of that distinct group are found. By only looking where row_num_throwaway = 1, you are only getting the first time that combo is present. To ensure you retrieve the correct entry, you can always add an ORDER BY statement where I showed with the stars.
Hope this helps!

Access VBA How to use a Union ALL statement

My UNION ALL statement is not returning what I hoped it would. I am putting products into a location (73) and taking them out of the same location. I would like to know how many are remaining in that location. I am trying to figure this out by adding the amount in and subtracting the amount out. I am storing my transactions in tblWarehouseTransfer.
I would like to have one line for each product with the total. What I am getting is one line with the sum of the amount put into the location and one line with the sum of the amount taken out (as a negative number).
I am using a list box to display the list of all my products.
Me.lstCutWipers.RowSource = "SELECT tblProducts.ProductID, tblProducts.ProductName, Sum(tblWarehouseTransfer.Qty) AS SumOfQty " _
& " FROM tblWarehouseTransfer INNER JOIN tblProducts ON tblWarehouseTransfer.ProductID = tblProducts.ProductID " _
& " GROUP BY tblProducts.Productid, tblProducts.ProductName, tblWarehouseTransfer.LocationTo " _
& " HAVING (((tblWarehouseTransfer.LocationTo) = 73)) " _
& " UNION ALL SELECT tblProducts.ProductID, tblProducts.ProductName, -Sum(tblWarehouseTransfer.Qty) AS SumOfQty " _
& " FROM tblWarehouseTransfer INNER JOIN tblProducts ON tblWarehouseTransfer.ProductID = tblProducts.ProductID " _
& " GROUP BY tblProducts.Productid, tblProducts.ProductName, tblWarehouseTransfer.LocationFrom " _
& " HAVING (((tblWarehouseTransfer.LocationFrom)= 73))"
Can someone help me to join the 'in' and the 'out' as one total.
This example joins two subqueries which allows your two different sums to be added together, whereas a UNION only lists rows of the two queries together.
One downside to having subqueries is that it cannot be fully edited in query Design View... it requires the SQL View to edit the whole thing. BUT, you could save each subquery separately and then join those queries together in a third query. Then you could edit each part separately in Design View.
Also notice that I changed the HAVING clause to a WHERE clause. WHERE clauses can be more efficient if you are applying criteria to source values before they are aggregated (i.e. grouped and summed). HAVING applies the criteria after aggregating the data. If the criteria involves aggregate expressions, then they must appear in HAVING clause.
By changing to a WHERE clause it also means that you don't have to group on that field. The difference in speed may be negligible and it should return the same information, but just not necessary since every row contributing to that query will only be for the value in the WHERE clause. Just be aware that if you change the query at all, you need to consider the proper clause to apply criteria.
EDIT: Changed to LEFT JOIN and handled NULL in TotalSum with call to nz().
SELECT ToQuery.ProductID, ToQuery.ProductName, (ToQuery.SumOfQty + nz(FromQuery.SumOfQty, 0.0)) As TotalSum
FROM
(SELECT tblProducts.ProductID, tblProducts.ProductName, Sum(tblWarehouseTransfer.Qty) AS SumOfQty
FROM tblWarehouseTransfer INNER JOIN tblProducts ON tblWarehouseTransfer.ProductID = tblProducts.ProductID
WHERE tblWarehouseTransfer.LocationTo = 73
GROUP BY tblProducts.Productid, tblProducts.ProductName) AS ToQuery
LEFT JOIN
(SELECT tblProducts.ProductID, tblProducts.ProductName, -Sum(tblWarehouseTransfer.Qty) AS SumOfQty
FROM tblWarehouseTransfer INNER JOIN tblProducts ON tblWarehouseTransfer.ProductID = tblProducts.ProductID
WHERE tblWarehouseTransfer.LocationFrom = 73
GROUP BY tblProducts.Productid, tblProducts.ProductName) AS FromQuery
ON ToQuery.ProductID = FromQuery.ProductID
To be complete, this assumes that ProductID is a primary key and that ProductName is unique to each ProductID. If that is not true, you will need to change the outer query ON expression to match ProductName values as well (i.e. add AND ToQuery.ProductName = FromQuery.ProductName).

How to sort records in a form in decending order from joined query

The below code is suppose to create a query and feed form 'frmstaticdatadepartments08' I am trying to sort the records in descending order based on the field tblContacts.TotalTeamSkills. Unfortunately the sort is not working.
sql_get = "SELECT tblContacts.Group, tblTeams.ID as TeamID, tblValueChain01.MacroProcess, tblTeams.Team, tblContacts.CompleteName, tblContacts.Foto, tblContacts.CurrentPosition, tblContacts.Level, tblContacts.ContractType, tblContacts.Beginner, tblContacts.SemiSkilled, tblContacts.HighlySkilled, tblContacts.Expert, tblContacts.totalskills, tblContacts.TeamBeginner, tblContacts.TeamSemiSkilled, tblContacts.TeamHighlySkilled, tblContacts.TeamExpert, tblContacts.TeamTotalSkills, tblContacts.DeptBeginner, tblContacts.DeptSemiSkilled, tblContacts.DeptHighlySkilled, tblContacts.DeptExpert, tblContacts.DeptTotalSkills, tblContacts.TotalActiveSkills, tblContacts.TotalTeamSkills, tblContacts.TotalDeptSkills, tblcontacts.flag1, tblcontacts.flag2, " & _
"tblcontacts.JobTitle FROM tblJobTitles INNER JOIN ((tblContacts INNER JOIN tblTeams ON tblContacts.Team = tblTeams.ID) INNER JOIN tblValueChain01 ON tblContacts.Group = tblValueChain01.IDMacroProcesso) ON tblJobTitles.jobtitle = tblContacts.JobTitle WHERE ([tblvaluechain01].[macroprocess]= '" & cboTeams.Value & "') or ([tblcontacts].[group]= 38 and [tblteams].[team]= '" & cboDepartments.Value & "') ORDER BY tblContacts.TotalTeamSkills DESC;"
Me.frmstaticdatadepartments08.Form.RecordSource = sql_get
If you aren't getting any sort of an error message my money is on the fact that your frmstaticdatadepartments08 already has a Order By field. Put it into design view and look at the forms Properties pane which you can open using Alt + Enter if it isn't open. If there is anything set to be sorted in there delete it.
Also, that naming scheme... please fix it for future you.

Changing the format from numerical value to text value in an SQL select query

In the string below, I am trying to convert the value 'tblContacts.JobTitle from a numerical value to a text value without having to link my query to the table where the original record ('tblJobTitles.JobTitle') sits. The field 'JobTitle' in the table 'tblContacts' pulls it's value from the table 'tblJobTitles'. When I use the query below, unfortunately the query extracts the unique ID number of the job title instead of the text value. Is there to reformat the select query to do so? The below is my code where I thought that you could format the value as text by adding '.text' but unfortunately this doesn't work.
sql_get = "SELECT [tblContacts].[CompleteName], [tblContacts].[CurrentPosition], [tblContacts].[Level], [tblContacts].[ContractType], [tblContacts].[ID], [tblContacts].[Foto], [tblTeams].[Team], [tblJobTitles].[JobTitle] FROM [tblContacts] INNER JOIN [tblTeams] ON [tblContacts].[Team] = [tblTeams].[ID] LEFT JOIN [tblJobTitles] ON [tblJobTitles].[ID] = [tblContacts].[JobTitle] WHERE [tblTeams].[team] = '" & cboDepartments.Value & "'"
Me.frmstaticdatadepartments08.Form.RecordSource = sql_get
As I understand it tblContacts.JobTitle is a foreign key. The actual job title sits in another table, tblJobTitles. The only way to get it is to ask for it, either by a sub-select or by joining in the other table.
SELECT tblContacts.CompleteName, tblContacts.CurrentPosition, tblContacts.Level,
tblContacts.ContractType, tblContacts.ID, tblContacts.Foto,
tblTeams.Team, tblJobTitles.JobTitle
FROM tblContacts
LEFT JOIN tblJobTitles ON tblContacts.JobTitle = tblJobTitles.Id
INNER JOIN tblTeams ON tblContacts.Team = tblTeams.ID
WHERE [tblTeams].[team] = '" & cboDepartments.Value & "'"
I had to guess on the some column names, tblJobTitles.Id and tblJobTitles.JobTitle, but I hope you get my meaning.
The change I did was to join in tblJobTitles and then ask for the title instead of the reference id, tblJobTitles.JobTitle.
Edit:
Apparently Access requires parentheses for multiple joins - see msdn:
In cases where you need to join more than one table, you can nest the INNER JOIN clauses.
So do this instead:
SELECT tblContacts.CompleteName, tblContacts.CurrentPosition, tblContacts.Level,
tblContacts.ContractType, tblContacts.ID, tblContacts.Foto,
tblTeams.Team, tblJobTitles.JobTitle
FROM (tblContacts
INNER JOIN tblTeams ON tblContacts.Team = tblTeams.ID)
LEFT OUTER JOIN tblJobTitles ON tblContacts.JobTitle = tblJobTitles.Id
WHERE [tblTeams].[team] = '" & cboDepartments.Value & "'"