I'm trying to SUM some data via query of queries. Its a fairly complicated sql query over complicated relationships that I'd like to translate into HQL.
I'll use a simplified version of the data relationships to make discussion easier.
So how could I translate this into HQL? Is query of queries even possible in HQL?
Example:
Suppose we have a Movie Critic that reviews movies online, and we want to return totals for the number of movies he's reviewed, the number of movies he loved, & the number of movies he hates.
Tables:
Critic
Movie
Review (link table between Critic & Movie with LoveFlag, if LoveFlag is false he hates the movie)
SQL Query:
(this is a made up scenario, the solution i'm working on is for facility management. I wrote this query on stack overflow, so there very well could be flaws in it.)
SELECT criticSummary.id
, COUNT(criticSummary.reviewId) as totalReviews
, SUM(criticSummary.isLoved) as totalLoved
, SUM(criticSummary.isHated) as totalHated
FROM (
SELECT DISTINCT critic.id AS id,
review.id AS reviewId,
review.isLoved AS isLoved,
CASE WHEN review.isLoved = 1 THEN 0 ELSE 1 END AS isHated
FROM [critic] critic
INNER JOIN [review] review
ON (
review.criticId = critic.id
AND review.active = 1
)
WHERE critic.active = 1
) AS criticSummary
GROUP BY criticSummary.id
Do you really need to use HQL? You have some options to simplify things:
1 Hibernate Formula Fields in Critic Entity
#Formula("select count(*) from reviews where id = id and loved = 1")
public int totalMoviesLoved;
#Formula("select count(*) from reviews where id = id and loved = 0")
public int totalMoviesHated;
public int getTotalMoviesReviewed(){
return get totalMoviesHated + totalMoviesLoved;
}
2 Create a Database View
Create a Database view, say critic_summary_data
Create an entity mapped to this view (works just the same as a table)
Map this in Critic Entity
#OneToOne
private CriticSummaryData summaryData;
Related
I'm learning SQL, and I'm having trouble writing a query to find games that have multiple genres at the same time.
I have a table like this:
game
genre
Void City
horror
Void City
survival
Paranoid
survival
Paranoid
thriller
Circle beat
action
Circle beat
horror
So how to write a query that returns list of action + horror games?, I expect to get only Circle beat in this case
select game
from game_table
where genre in ('action')
intersect
select game
from game_table
where genre in ('horror');
One way to achieve this, with a single pass of the table, is to use a having clause like so:
select
game
from game_table
where genre in ('action','horror')
group by
game
having ( max(case when genre ='action' then 1 else 0 end)
+ max(case when genre ='horror' then 1 else 0 end) ) = 2
note, a having clause can filter the results of an aggregation function such as max() (maximum) - but a where clause cannot do that. But, you can use both clauses in a single query as you can see above, the where clause reduces the total number of rows to be considered and the having clause considers some aggregated data to filter out rows that don't meet the wanted aggregated data conditions.
I am new in MVC. Linq is the system of making queries in database. but I am having difficulties right now with Linq. Anyone know how to convert my sql statement to Linq?
My entities, seperate context
localDB.summaries
accountDB.account
SELECT * FROM summary
WHERE studentID =
(SELECT studentID FROM accounts WHERE username = 'username123')
FROM user IN localDB.summaries
WHERE -------- please guide my linq--------
SELECT user
At a guess without knowing your entities:
var query = from user in localDB.summaries
join account in localDB.accounts on user.studentID equals account.studentID
where account.username == "username123"
select user;
I am assuming that accounts table has been mapped with student on the basis of student id I mean there is a foreign key relationship so by assuming that:
var data = localDB.summaries.where(c=>c.Students.Account.FirstorDefault().username == "username123")
The database is Oracle XE .
Let me explain the scenario first ,
Two tables Movie and UserInfo are in a relationship many to many using the junction table Rating.
Rating ( MovieID (FK) , UserName(FK) , Rating)
MovieID and UserName are both respectively the primary keys in the respected tables.
What I am trying to do is make a select statement to select the MovieNames from the Movie table where UserName is not equal to the given input. As the MoveID was the FK, but I need to retrieve MovieName if the movie is not already been rated by the GIVEN user, so I guess I may need to make a rather complex joining operation - which I can't figure out or maybe joining two or more different query using where.
Thanks in advance and please if possible give an explanation about the solution.
This seems like a classic usecase for the not exists operator:
SELECT *
FROM movie m
WHERE NOT EXISTS (SELECT *
FROM rating r
WHERE r.movideid = m.moveid AND
r.username = 'given username here')
I have searched high and low and have tried for hours to manipulate the various other queries that seemed to fit but I've had no joy.
I have several Tables in Microsoft SQL Server 2005 that I'm trying to join, an example of which is:
Company Table (Comp_CompanyId, Comp_Name)
GroupCode_Link Table (gcl_c_groupcodelinkid, gcl_c_groupcodeid, gcl_c_companyid)
GroupCode Table (grp_c_groupcodeid, grp_c_groupcode, grp_c_name)
ItemCode Table (itm_c_itemcodeid, itm_c_name, itm_c_itemcode, itm_c_group)
ItemCode_Link Table (icl_c_itemcodelinkid, icl_c_companyid, icl_c_groupcodeid, icl_c_itemcodeid)
I'm using Link Tables to associate a Group to a Company, and an Item to a Group, so a Company could have multiple groups, with multiple items in each group.
Now, I'm trying to create an Advanced Find Function that will allow a user to enter, for example, an Item Code and the result should display those companies that have that item, sounds nice and simple!
However, I haven't done something right, if I use the following query ' if the company has this item OR this item, display it's name', I get the company appearing twice in the result set, once for each item.
What I need is to be able to say is:
"Show me a list of companies that have these items (displaying each company only once!)"
I've had a go at using COUNT, DISTINCT and HAVING but have failed on each as my query knowledge isn't up to it!
First, from your description it sounds like you might have a problem with your E-R (entity-relationship) model. Your description tells me that your E-R model looks something like this:
Associative entities (CompanyGroup, GroupItem) exist to implement many-to-many relationships (since many-to-many isn't supported directly by relational databases).
Nothing wrong with that if a group can exist within multiple companies or an item across multiple groups. It would seem more likely that, at least, each group is specific to a company (I can see items existing across multiple companies and/or groups: more than one company retails, for instance, Cuisinart food processors). If that is the case, a better E-R model would be to make each group a dependent entity with a CompanyID that is a component of its primary key. It's a dependent entity because the group doesn't have an independent existence: it's created by/on behalf of and exists for its parent company. If the company goes away, the group(s) tied to it go away. No your E-R model looks like this:
From that, we can write the query you need:
select *
from Company c
where exists ( select *
from GroupItem gi
where gi.ItemID in ( desired-itemid-1 , ... , desired-itemid-n )
and gi.CompanyID = c.CompanyID
)
As you can see, dependent entities are a powerful thing. Because of the key propagation, queries tend to get simpler. With the original data model, the query would be somewhat more complex:
select *
from Company c
where exists ( select *
from CompanyGroup cg
join GroupItem gi on gi.GroupId = cg.GroupID
where gi.ItemID in ( desired-itemid-1 , ... , desired-itemid-n )
and cg.CompanyID = c.CompanyID
)
Cheers!
SELECT *
FROM company c
WHERE (
SELECT COUNT(DISTINCT icl_c_itemcodeid)
FROM GroupCode_Link gl
JOIN ItemCode_Link il
ON il.icl_c_groupcodeid = gcl_c_groupcodeid
WHERE gl.gcl_c_companyid = c.Comp_CompanyId
AND icl_c_companyid = c.Comp_CompanyId
AND icl_c_itemcodeid IN (#Item1, #Item2)
) >= 2
Replace >= 2 with >= 1 if you want "any item" instead of "all items".
If you need to show companies that have item1 AND item2, you can use Quassnoi's answer.
If you need to show companies that have item1 OR item2, then you can use this:
SELECT
*
FROM
company
WHERE EXISTS
(
SELECT
icl_c_itemcodeid
FROM
GroupCode_Link
INNER JOIN
ItemCode_Link
ON icl_c_groupcodeid = gcl_c_groupcodeid
AND icl_c_itemcodeid IN (#item1, #item2)
WHERE
gcl_c_companyid = company.Comp_CompanyId
AND
icl_c_companyid = company.Comp_CompanyId
)
I would write something like the code below:
SELECT
c.Comp_Name
FROM
Company AS c
WHERE
EXISTS (
SELECT
1
FROM
GroupCode_Link AS gcl
JOIN
ItemCode_Link AS icl
ON
gcl.gcl_c_groupcodeid = icl.icl_c_groupcodeid
JOIN
ItemCode AS itm
ON
icl.icl_c_itemcodeid = itm.itm_c_itemcodeid
WHERE
c.Comp_CompanyId = gcl.gcl_c_companyid
AND
itm.itm_c_itemcode IN (...) /* here provide list of one or more Item Codes to look for */
);
but I see there's a icl_c_companyid column in the ItemCode_Link so using GroupCode_Link table is not necessary?
I have these tables:
media
id (int primary key)
uri (varchar).
media_to_people
media_id (int primary key)
people_id (int primary key)
people
id (int primary key)
name (varchar)
role (int) -- role specifies whether the person is an artist, publisher, writer, actor,
etc relative to the media and has range(1-10)
This is a many to many relation
I want to fetch a media and all its associated people in a select. So if a media has 10 people associated with it, all 10 must come.
Further more, if multiple people with the same role exist for a given media, they must come as comma separated values under a column for that role.
Result headings must look like: media.id, media.uri, people.name(actor), people.name(artist), people.name(publisher) and so on.
I'm using sqlite.
SQLite doesn't have the "pivot" functionality you'd need for starters, and the "comma separated values" part is definitely a presentation issue that it would be absurd (and possibly unfeasible) to try to push into any database layer, whatever dialect of SQL may be involved -- it's definitely a part of the job you'd do in the client, e.g. a reporting facility or programming language.
Use SQL for data access, and leave presentation to other layers.
How you get your data is
SELECT media.id, media.uri, people.name, people.role
FROM media
JOIN media_to_people ON (media.id = media_to_people.media_id)
JOIN people ON (media_to_people.people_id = people.id)
WHERE media.id = ?
ORDER BY people.role, people.name
(the ? is one way to indicate a parameter in SQLite, to be bound to the specific media id you're looking for in ways that depend on your client); the data will come from the DB to your client code in several rows, and your client code can easily put them into the single column form that you want.
It's hard for us to say how to code the client-side part w/o knowing anything about the environment or language you're using as the client. But in Python for example:
def showit(dataset):
by_role = collections.defaultdict(list)
for mediaid, mediauri, name, role in dataset:
by_role[role].append(name)
headers = ['mediaid', 'mediauri']
result = [mediaid, mediauri]
for role in sorted(by_role):
headers.append('people(%s)' % role)
result.append(','.join(by_role[role]))
return ' '.join(headers) + '\n' + ' '.join(result)
even this won't quite match your spec -- you ask for headers such as 'people(artist)' while you also specify that the role's encoded as an int, and mention no way to go from the int to the string 'artist', so it's obviously impossible to match your spec exactly... but it's as close as my ingenuity can get;-).
I agree with Alex Martelli's answer, that you should get the data in multiple rows and do some processing in your application.
If you try to do this with just joins, you need to join to the people table for each role type, and if there are multiple people in each role, your query will have Cartesian products between these roles.
So you need to do this with GROUP_CONCAT() and produce a scalar subquery in your select-list for each role:
SELECT m.id, m.uri,
(SELECT GROUP_CONCAT(name)
FROM media_to_people JOIN people ON (people_id = id)
WHERE media_id = m.id AND role = 1) AS Actors,
(SELECT GROUP_CONCAT(name)
FROM media_to_people JOIN people ON (people_id = id)
WHERE media_id = m.id AND role = 2) AS Artists,
(SELECT GROUP_CONCAT(name)
FROM media_to_people JOIN people ON (people_id = id)
WHERE media_id = m.id AND role = 3) AS Publishers
FROM media m;
This is truly ugly! Don't try this at home!
Take our advice, and don't try to format the pivot table using only SQL.