Convert SQL-query with subselect in select section to HQL - sql

I have trouble with converting native SQL query to HQL.
Query is something like follows:
select count(*)
, sum(select count(*) from employee e where e.company_id=c.id))
from company c where c.id = someID
First returned value is count of companies, second - amount of employees for specified company.
I.e. I have to get this two values for company with id=someID.
The trouble is hibernate doesn't support subselects in SELECT section, only in WHERE - by specification.
Actually I can:
1) use native query to run this through EntityManager
2) do the same dividing this "complex" query to two simpler SQL queries
But may be there are exist more convenient methods to realize initial query in HQL? - this is a question.
Thank you.

this might work:
select count(c.*) as count_companies
, c as count_emp
from company c
inner join
(
select company_id, count(*) as c
from employee
where employee.company_id = someID
group by company_id
) e
on e.company_id=c.id
group by c.id

Related

Issues with Counting Records with SQL across multiple tables

There are three tables: Cases, Calls, and SubEvents.
The table schema is a Case can have multiple Calls, and each Call can have multiple SubEvents.
I'd like to use a query to get a count of all calls and a count of all sub-events (if there are any) for each case after a certain date.
For example, a case named John has 3 calls... the first call has 2 sub-events, the second call has 1 sub-event, and the third call has zero. So the query should return this result:
Case
Call Total
SubEvent Total
John
3
3
I've tried writing the query multiple ways, with subqueries etc, but I can't get it to work properly. The closest I've come is the query below, but this provides the incorrect count for Calls. It gives me 4 when it should give me 3. Another example I tried had 4 calls with 10 sub-events, but the query returned 11 total calls instead of 4.
I'd appreciate any help. My SQL has gotten rusty after a period of disuse, and this is in someone's Access database, which is pretty fickle when it comes to writing SQL queries.
SELECT c.casename, Count(e.callid) AS EventTotal, Count(s.id) AS SubEventTotal
FROM (cases AS c INNER JOIN calls AS e ON c.contactid = e.contactid) left JOIN tblSubEvents AS s ON e.callid = s.callid
WHERE e.calldate > #1/1/2022#
GROUP BY c.casename
Try this:
SELECT
c.casename
,Count(e.callid) AS EventTotal
,sum(s.id) AS SubEventTotal
FROM (cases AS c INNER JOIN calls AS e ON c.contactid = e.contactid)
left JOIN (select callid, count(s.id) as id from tblSubEvents s group by callid) AS s ON e.callid = s.callid
WHERE e.calldate > #1/1/2022#
GROUP BY c.casename
Basically, pushing the responsibility for the sub-event count to the join query.
Consider joining two aggregate derived tables:
SELECT case.casename, c_agg.EventTotal, e_agg.SubEventTotal
FROM (case
INNER JOIN (
SELECT contactid, COUNT(callid) AS EventTotal
FROM calls
WHERE calldate > CDate('2022-01-01')
GROUP BY contactid
) c_agg
ON case.contactid = c_agg.contactid)
LEFT JOIN (
SELECT callid, COUNT(id) AS SubEventTotal
FROM tblSubEvents
GROUP BY callid
) e_agg
ON c_agg.callid = e_agg.callid

SQL Server 2016 Sub Query Guidance

I am currently working on an assignment for my SQL class and I am stuck. I'm not looking for full code to answer the question, just a little nudge in the right direction. If you do provide full code would you mind a small explanation as to why you did it that way (so I can actually learn something.)
Here is the question:
Write a SELECT statement that returns three columns: EmailAddress, ShipmentId, and the order total for each Client. To do this, you can group the result set by the EmailAddress and ShipmentId columns. In addition, you must calculate the order total from the columns in the ShipItems table.
Write a second SELECT statement that uses the first SELECT statement in its FROM clause. The main query should return two columns: the Client’s email address and the largest order for that Client. To do this, you can group the result set by the EmailAddress column.
I am confused on how to pull in the EmailAddress column from the Clients table, as in order to join it I have to bring in other tables that aren't being used. I am assuming there is an easier way to do this using sub Queries as that is what we are working on at the time.
Think of SQL as working with sets of data as opposed to just tables. Tables are merely a set of data. So when you view data this way you immediately see that the query below returns a set of data consisting of the entirety of another set, being a table:
SELECT * FROM MyTable1
Now, if you were to only get the first two columns from MyTable1 you would return a different set that consisted only of columns 1 and 2:
SELECT col1, col2 FROM MyTable1
Now you can treat this second set, a subset of data as a "table" as well and query it like this:
SELECT
*
FROM (
SELECT
col1,
col2
FROM
MyTable1
)
This will return all the columns from the two columns provided in the inner set.
So, your inner query, which I won't write for you since you appear to be a student, and that wouldn't be right for me to give you the entire answer, would be a query consisting of a GROUP BY clause and a SUM of the order value field. But the key thing you need to understand is this set thinking: you can just wrap the ENTIRE query inside brackets and treat it as a table the way I have done above. Hopefully this helps.
You need a subquery, like this:
select emailaddress, max(OrderTotal) as MaxOrder
from
( -- Open the subquery
select Cl.emailaddress,
Sh.ShipmentID,
sum(SI.Value) as OrderTotal -- Use the line item value column in here
from Client Cl -- First table
inner join Shipments Sh -- Join the shipments
on Sh.ClientID = Cl.ClientID
inner join ShipItem SI -- Now the items
on SI.ShipmentID = Sh.ShipmentID
group by C1.emailaddress, Sh.ShipmentID -- here's your grouping for the sum() aggregation
) -- Close subquery
group by emailaddress -- group for the max()
For the first query you can join the Clients to Shipments (on ClientId).
And Shipments to the ShipItems table (on ShipmentId).
Then group the results, and count or sum the total you need.
Using aliases for the tables is usefull, certainly when you select fields from the joined tables that have the same column name.
select
c.EmailAddress,
i.ShipmentId,
SUM((i.ShipItemPrice - i.ShipItemDiscountAmount) * i.Quantity) as TotalPriceDiscounted
from ShipItems i
join Shipments s on (s.ShipmentId = i.ShipmentId)
left join Clients c on (c.ClientId = s.ClientId)
group by i.ShipmentId, c.EmailAddress
order by i.ShipmentId, c.EmailAddress;
Using that grouped query in a subquery, you can get the Maximum total per EmailAddress.
select EmailAddress,
-- max(TotalShipItems) as MaxTotalShipItems,
max(TotalPriceDiscounted) as MaxTotalPriceDiscounted
from (
select
c.EmailAddress,
-- i.ShipmentId,
-- count(*) as TotalShipItems,
SUM((i.ShipItemPrice - i.ShipItemDiscountAmount) * i.Quantity) as TotalPriceDiscounted
from ShipItems i
join Shipments s on (s.ShipmentId = i.ShipmentId)
left join Clients c on (c.ClientId = s.ClientId)
group by i.ShipmentId, c.EmailAddress
) q
group by EmailAddress
order by EmailAddress
Note that an ORDER BY is mostly meaningless inside a subquery if you don't use TOP.

Use result of multiple rows to do arithmetic operation

I'm writing a query to multiply the count that I receive from subquery to fees amount, But I don't know how to do that. Any help/suggestion?
Oracle query is:
select courseid,coursename,fees*tmp
from course c join registration r on
r.courseid=c.courseid
and tmp IN (select count(*)
from course c join registration r on
r.courseid=c.courseid group by coursename);
I tried to use like a variable tmp ,But i don't think it works in oracle query. Is there an alternative way to do so?
You can't do that, because you can only select data from tables that appeared between FROM and WHERE. The IN operator is a quick way to save having to write a bunch of OR statements, it is not something that can establish a variable in the outer query.
Instead do something like:
select courseid,coursename,fees * COUNT(r.courseID) OVER(PARTITION BY c.coursename)
from course c join registration r on
r.courseid=c.courseid
Edit/update: you noted that this query produces too many rows and you only want to see distinct course names. In that case it would be better to just use the registrations table to count the number of people on the course and then multiply the fees:
SELECT
c.courseid, c.coursename, c.fees * COALESCE(r.numberOfstudents, 0) as courseWorth
FROM
course c
LEFT OUTER JOIN
(select courseid, COUNT(*) as numberofstudents FROM registration GROUP BY courseid) r
ON c.courseID = r.courseid
You can use a windowing function like Caius or you can use a join like this:
select courseid,coursename, fees * COALESCE(sub.cnt,0)
from course c
join registration r on r.courseid=c.courseid
left join (
select coursename, count(*) as cnt
from course c2
join registration r2 on r2.courseid=c2.courseid
group by coursename
) as sub;
note: I make no claim your joins are correct -- I'm basing this query off of your example not on any knowledge of your data model.

SQL Beginner - How to get items from Table1 that are associated with at least 10 items in Table2

This is probably very easy, but I'm confused after looking at things online. Each item in my Contract table has multiple Envelopes. I want to find Contracts that have at least 10 envelopes. How do I go about this?
I've tried the following
select c.*, COUNT(e.ID)
from [Contract] c
INNER JOIN Envelope e ON e.ContractID = c.ID
Group By c.ID
HAVING Count(e.ID) > 10
And I get
Column 'Contract.PresenterUserID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I haven't dealt with aggregate or group by clauses before, so I'm not sure what it means.
By default, MySQL will accept your query. So, I am assuming that you are not using MySQL or the system has full group by turned on.
Here is another approach that will work in any database:
select c.*, e.cnt
from [Contract] c inner join
(select e.ContractId, count(*) as cnt
from Envelope e
group by e.ContractId
having count(*) >= 10
) e
on e.ContractID = c.ID;
This moves the aggregation to a subquery, before the join. You can then take all the columns from the contract table.
You are pretty close. You must include all fileds in the selectt either an aggregate function or the GROUP BY clause. Try this:
select c.id, c.PresenterUserID, COUNT(e.ID)
from [Contract] c
INNER JOIN Envelope e ON e.ContractID = c.ID
Group By c.ID, c.PresenterUserID
HAVING Count(e.ID) >= 10

PostgreSQL - Query with aggregate functions

I need some help for a PostgreSQL query.
I have 4 tables involved on it: customer, organization_complete, entity and address. I retrieve some data from everyone and with this query:
SELECT distinct ON (c.customer_number, trim(lower(o.name)), a.street, a.zipcode, a.area, a.country)
c.xid AS customer_xid, o.xid AS entity_xid, c.customer_number, c.deleted, o.name, o.vat, 'organisation' AS customer_type, a.street, a.zipcode, a.city, a.country
FROM customer c
INNER JOIN organisation_complete o ON (c.xid = o.customer_xid AND c.deleted = 'FALSE')
INNER JOIN entity e ON e.customer_xid = o.customer_xid
INNER JOIN address a ON (a.contact_info_xid = e.contact_info_xid and a.address_type = 'delivery')
WHERE c.account_xid = "<value>"
I get a distinct of all the customers splitted by customer_number, name, street, zipcode, area and country (what's specified after the DISTINCT ON statement).
What I need to retrieve now is a distinct of all customers having a doubled row on DB but I also need to retrieve the customer_xid and the entity_xid, that are primary keys of the respective tables and so are unique. For this reason they can't be included into an aggregate function. All I need is to count how many rows with the same customer_number, name, street, zipcode, area and country I have for each distinct tuple and to select only tuples with a count bigger than 1.
For each selected tuple I need also to take a customer_xid and an entity_xid, at random, like MySQL would do with a_key in a query like this:
SELECT COUNT(*), tab.a_key, tab.b, tab.c from tab
WHERE 1
GROUP BY tab.b
I know MySQL is quite an exception regarding this, I just want to know if may be possible to obtain the same result on PostgreSQL.
Thanks,
L.
This query in MySql is using a nonstandard (see note below) "MySql group by extension": http://dev.mysql.com/doc/refman/5.0/en/group-by-extensions.html
SELECT COUNT(*), tab.a_key, tab.b, tab.c
from tab
WHERE 1
GROUP BY tab.b
Note: This is a feature definied in SQL:2003 Standard as T301 Functional dependencies, it is not required by the standard, and many RDBMS don't support it, including PostgreSql (see this link for version 9.3 - unsupported features: http://www.postgresql.org/docs/9.3/static/unsupported-features-sql-standard.html ).
The above query could be expressed in PostgreSQL in this way:
SELECT tab.a_key, tab.b, tab.c,
q.cnt
FROM (
SELECT tab.b,
COUNT(*) As cnt,
MIN(tab.unique_id) As unique_id /* could be also MAX */
from tab
WHERE 1
GROUP BY tab.b
) q
JOIN tab ON tab.unique_id = q.unique_id
where unique_id is a column that uniquely identifies each row in tab (usually a primary key).
Min or Max functions choose one row from the table in a pseudo-random manner.