Group and count query in HQL - hql

I have HQL query that is giving me a headache and am hoping a friendly HQL wizard is out there who can help.
I think I'm trying to do something really simple but can't fathom it all.
I want to group and count some data to present it in a table and think I need a nested query of some sort but can't figure out the way to do it.
Basically I have a table (clients) which has relations to other tables that are used as lookups (clienttype) and (clientsex). The relationships is 1-m. I want to group the clients by type and then have a column counting males, and another counting females. e.g
Type | males | females
type A | x | x
type B | x | x
I can do the query just to get males or females but can't figure out how to get a second column.
Hope this makes sense, is possible and that someone is able to help.
Many thanks,
Craig
Query to get just males is:
SELECT a.clientStatus, COUNT(c.sex)
FROM
Tblclientstatus AS a RIGHT OUTER JOIN a.tblclients AS b
LEFT OUTER JOIN b.tblsex AS c
WHERE c.sex = 'male'
GROUP BY a.clientStatus

You can have a case when to add up "1" for each sex.
Something like this :
SELECT a.clientStatus,
COUNT(
case when c.sex = 'male' then 1
else 0 end
) as males,
COUNT(
case when c.sex = 'female' then 1
else 0 end
) as females,
FROM
Tblclientstatus AS a RIGHT OUTER JOIN a.tblclients AS b
LEFT OUTER JOIN b.tblsex AS c
GROUP BY a.clientStatus

Related

Optimizing Left Join With Group By and Order By (MariaDb)

I am attempting to optimize a query in MariaDb that is really bogged down by its ORDER BY clause. I can run it in under a tenth of a second without the ORDER BY clause, but it takes over 25 seconds with it. Here is the gist of the query:
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM users u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE u.is_deleted = 0
GROUP BY u.id
ORDER BY u.display_name
LIMIT 0, 10;
I need it to be a left join because I want to include users that aren't linked to a vehicle.
I need the group by because I want only 1 result per user (and display_name is not guaranteed to be unique).
users table has about 130K rows, while user_vehicles has about 230K rows.
Here is the EXPLAIN of the query:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE u index dms_cust_idx PRIMARY 4 null 124825 Using where; Using temporary; Using filesort
1 SIMPLE uv ref user_idx user_idx 4 awscheduler.u.id 1 Using where
I have tried these two indices to speed things up, but they don't seem to do much.
CREATE INDEX idx_display_speedy ON users(display_name);
CREATE INDEX idx_display_speedy2 ON users(id, display_name, is_deleted, dms_cust_id);
I am looking for ideas on how to speed this up. I attempted using nested queries, but since the order by is the bottleneck & order within the nested query is ignored, I believe that attempt was in vain.
how about:
WITH a AS (
SELECT u.id, u.display_name, u.cell_phone, u.email
FROM users u
WHERE u.is_deleted = 0
GROUP BY u.id
LIMIT 0, 10
)
SELECT a.id, a.display_name, a.cell_phone, a.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM a LEFT JOIN user_vehicles uv ON uv.user_id = a.id AND uv.current_owner=1
ORDER BY a.display_name;
The intention is we take a subset of users before joining it with user_vehicles.
Disclaimer: I haven't verified if its faster or not, but have similar experience in the past where this helps.
with a as (
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM users u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE u.is_deleted = 0
GROUP BY u.id
)
select * from a
ORDER BY u.display_name;
)
I suspect it's not actually the ordering that is causing the problem... If you remove the limit, I bet the ordered and un-ordered versions will end up performing pretty close to the same.
Depending on if your actual query is as simple as the one you posted, you may be able to get good performance in a single query by using RowNum() as described here:
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM (
SELECT iu.id, iu.display_name, iu.cell_phone, iu.email
FROM users iu
WHERE iu.is_deleted = 0
ORDER BY iu.display_name) as u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE ROWNUM() < 10
GROUP BY u.id
ORDER BY u.display_name
If that doesn't work, you probably need to select the users in one select and then select their vehicles in a second Select

How to fix this Sql code to give the names of student and their grades who got better grades than the highest grade by gray in classes taught by KEEN

I have 4 tables which are class, student, faculty, and grade.
I want SQL to give the names of students and their grades who got better grades than the highest grade by GRAY in classes taught by KEEN.
Here is a sample of what I did
SELECT S_NAME, GRADE
FROM GRADE1
WHERE GRADE >(SELECT GRADE FROM GRADE1 G
INNER JOIN CLASS1 C ON G.C_NAME=C.C_NAME
INNER JOIN aggarwal.FACULTY1 F ON F.F_NAME
WHERE G.S_NAME='GRAY' AND F.F_NAME='KEEN');
ERROR at line 6:
ORA-00920: invalid relational operator
Any help as to how?
The last INNER JOIN is a culprit; its ON clause doesn't contain everything it should - what are you joining F.F_NAME to?
Besides, a few more objections:
always use table aliases, especially when working with subqueries because you might find yourself puzzled getting result you didn't expect
subquery misses the MAX function; generally speaking, you should take care that it returns a single value, otherwise you'll get TOO-MANY-ROWS, unless you apply ALL (or ANY), e.g.
where r.grade > any (select g.grade from grade1 g ...)
why did you precede table name with its ownere, here: aggarwal.FACULTY1? Don't all of your tables belong to the same schema? If so, remove it (or, for consistency, use it with all other tables as well).
Finally, your query might look like this:
select r.s_name, r.grade
from grade1 r --> missing alias(es)
where r.grade > (select max(g.grade) --> missing MAX
from grade1 g
inner join class1 c on g.c_name = c.c_name
inner join faculty1 f on f.f_name = ... --> missing value here
where g.s_name = 'GRAY'
and f.f_name = 'KEEN'
);

Oracle Inner Table Query

I am trying to write a query in Oracle to give totals of active legal entities and inactive legal entities.
The query so far I have is:
select le.Name, b.LE_ID, count(*) As TOTAL, dead.LE_ID as DEAD
from BOOK b
left join Legal_Entity le on le.LE_ID = b.LE_ID
left join
(
select count(LE_ID) as LE_ID
from BOOK
where (Name like '%DUMMY%' or name like '%TEST%' or name like '%DEAD%' or name like '%DO NOT%' or status <> 'Active')
) dead on dead.LE_ID = b.LE_ID
where b.LE_ID = 1234
group by le.Name, b.LE_ID, dead.LE_ID
order by b.LE_ID;
The results I am expecting are:
Name EntityID Total Dead
Entity A 1234 500 200
i.e. for Book.LE_ID = 1234 I would like one row in the result set and a column with the Total number i.e. select * from Book where LE_ID = 1234 and a column with the number of dead books i.e. the inner query
But at the moment, my query is returning NULL for the number of dead rows.
The inner query is working without issue, but I'm clearly missing something here.
I am not sure, but shouldn't it be something like this?
select le.Name, b.LE_ID, count(*) As TOTAL, dead.DEAD as DEAD
from BOOK b
left join Legal_Entity le on le.LE_ID = b.LE_ID
left join
(
select LE_ID, count(LE_ID) as DEAD
from BOOK
where (Name like '%DUMMY%' or name like '%TEST%' or name like '%DEAD%' or name like '%DO NOT%' or status <> 'Active')
group by LE_ID
) dead on dead.LE_ID = b.LE_ID
where b.LE_ID = 1234
group by le.Name, b.LE_ID, dead.LE_ID
order by b.LE_ID;
It is a bit hard to figure out exactly what you are trying to do. After all, you are trying to join between a count and an id, something that is not likely to lead to success.
Your example has a name 'Entity A'. By the rules specified in the query for "dead" entities, there should be none. The name doesn't match this rule.
In all likelihood, you want conditional aggregation. Here is a query that I think gets the totals over all entities:
select count(*) As TOTAL,
sum(case when Name like '%DUMMY%' or name like '%TEST%' or name like '%DEAD%' or name like '%DO NOT%' or status <> 'Active'
then 1 else 0
end) as dead
from BOOK b left join
Legal_Entity le
on le.LE_ID = b.LE_ID;

How do I write a LINQ query to combine multiple rows into one row?

I have one table, 'a', with id and timestamp. Another table, 'b', has N multiple rows referring to id, and each row has 'type', and "some other data".
I want a LINQ query to produce a single row with id, timestamp, and "some other data" x N. Like this:
1 | 4671 | 46.5 | 56.5
where 46.5 is from one row of 'b', and 56.5 is from another row; both with the same id.
I have a working query in SQLite, but I am new to LINQ. I dont know where to start - I don't think this is a JOIN at all.
SELECT
a.id as id,
a.seconds,
COALESCE(
(SELECT b.some_data FROM
b WHERE
b.id=a.id AND b.type=1), '') AS 'data_one',
COALESCE(
(SELECT b.some_data FROM
b WHERE
b.id=a.id AND b.type=2), '') AS 'data_two'
FROM a first
WHERE first.id=1
GROUP BY first.ID
you didn't mention if you are using Linq to sql or linq to entities. However following query should get u there
(from x in a
join y in b on x.id equals y.id
select new{x.id, x.seconds, y.some_data, y.type}).GroupBy(x=>new{x.id,x.seconds}).
Select(x=>new{
id = x.key.id,
seconds = x.Key.seconds,
data_one = x.Where(z=>z.type == 1).Select(g=>g.some_data).FirstOrDefault(),
data_two = x.Where(z=>z.type == 2).Select(g=>g.some_data).FirstOrDefault()
});
Obviously, you have to prefix your table names with datacontext or Objectcontext depending upon the underlying provider.
What you want to do is similar to pivoting, see Is it possible to Pivot data using LINQ?. The difference here is that you don't really need to aggregate (like a standard pivot), so you'll need to use Max or some similar method that can simulate selecting a single varchar field.

Best way to exclude records from multiple tables

I got the following tables (just an example): vehicles, vehicle_descriptions, vehicle_parts
vehicles have 1 to many with vehicle_descriptions and vehicle_parts. There may not be a corresponding vehicle_description/part for a given vehicle.
SELECT * FROM vehicles
LEFT OUTER JOIN vehicles d ON vehicles.vin = d.vin AND d.summary NOT LIKE 'honda'
LEFT OUTER JOIN
(SELECT SUM(desc_total) FROM vehicle_descriptions WHERE NOT LIKE desc 'honda' GROUP BY vin) b
ON vehicles.vin = vehicle_b.vin
LEFT OUTER JOIN
(SELECT SUM(part_count) FROM vehicle_parts WHERE part_for NOT LIKE 'honda' GROUP BY vin) c ON vehicles.vin = c.vin
If either vehicle_desc, vehicles, or part contains the exclusion term, the whole record should not show up in the result set. The query above will return a record even if one of the tables contain the exclusion term Honda. How would I fix the above query?
You're not using any of the information in either sum() as part of what you show, just to decide whether to include the vehicle. And you're doing an unnecessary self join in your first clause. Generally in situations like this, the "exists" and "not exists" clauses work well. So what about this? I'll use Oracle syntax, you can convert to ANSI of course.
SELECT * FROM vehicles v where summary <> 'honda'
and not exists (select 1 from vehicle_descriptions d where d.vin = v.vin and d.desc <> 'honda')
and not exists (select 1 from vehicle_parts p where p.vin = v.vin and p.part_for <> 'honda')

Resources