How to implement Aggregate function properly? - sql

I have two tables in my project. Students table and Lessons table. They are connected through a foreign key (Id in Students table and StudentId in Lessons table). I want to return the highest grade for each lesson with the name of the student. I know I should implement aggregate function. It works fine for returning the highest grade of all the lessons, but I have no idea how to return the highest grade for a specific lesson (Math for example).
select s.Name, l.Grade
from Students s
inner join Lessons l on s.Id = l.StudentId
where l.Grade = (select MAX(Grade) from Lessons)
public class StudentClass
{
public int Id { get; set; }
public string Name { get; set; }
public List<Lesson> Lessons { get; set; }
}
public class Lesson
public int Id { get; set; }
public string Name { get; set; }
public int Grade { get; set; }
public int StudentId { get; set; }
[ForeignKey("StudentId")]
public StudentClass Student { get; set; }
}

A bit more modern solution is to use ROW_NUMBER:
select *
from (
select s.Name, l.Grade, l.Name
, ROW_NUMBER() OVER(PARTITION BY l.Name ORDER BY Grade DESC) AS sort
from Students s
inner join Lessons l on s.Id = l.StudentId
) les
WHERE les.sort = 1
This creates a counter per each less name ordered by grade descending and then you retrieve the first result

Related

SELECT the patients that have similar disease name EF core 3.1

I have the following tables
Patients
ID PatientName PatientIDNumber
---- ------------- -----------------
1 Jo 411420607
2 Mark 206047758
Records
ID DiseaseName PatinetID
---- --------------- -----------
1 Liver Disease 1
2 Heart Disease 1
3 Liver Disease 2
4 Heart Disease 2
Each patient has many records
Patient Data model class
public class PatientEntity : BaseEntity
{
[Column("PatientName")]
[StringLength(150)]
public string Name { get; set; }
[Column("OfficialIDNumber")]
[StringLength(10), MinLength(10)]
public string IDNumber { get; set; }
[Column("SystemIDnumber")]
public int SystemID { get; set; }
[Column("PatientDateOfBirth")]
public DateTime? DateOfBirth { get; set; }
[Column("PatientEmailAdress")]
[StringLength(300)]
public string EmailAdress { get; set; }
public IEnumerable<LookupsEntity> MetaData { get; set; }
public IEnumerable<RecordEntity> Records { get; set; }
}
Record data model class
public class RecordEntity : BaseEntity
{
[StringLength(50)]
public string DiseaseName { get; set; }
public DateTime TimeOfEntry { get; set; }
[StringLength(300)]
public string Description { get; set; }
[ForeignKey("Patient")]
public int PatientId { get; set; }
public PatientEntity Patient { get; set; }
public BillEntity Bill { get; set; }
}
I want to create an API that takes a specific patient id as a parameter to get a list of other patients with similar diseases
Patient report API result (DTO)
public class PatinetReportResource
{
public PatientResource patient { get; set; }
public IList<PatientResource> SimilarPatinets { get; set; }
}
Note: *Similar diseases mean that the two patients have in common 2 or more disease
How to List patients with similar diseases in EF core 3.1?
This addresses my interpretation of the original question.
The simplest method is string aggregation:
select diseases, string_agg(patientid, ',') within group (order by patientid) as patients
from (select patientId, string_agg(diseasename, ',') within group (order by diseasename) as diseases
from t
group by patientid
) p
group by diseases;
Here is a db<>fiddle.
This is the bare bones of what you need, without relying on your disease names being in the same order.
If you need additional info like PatientName, you can just add them in by wrapping the select below in a cte and then join onto the Patient data:
As a fiddle.
declare #p table(ID int,PatientName varchar(10),PatientIDNumber int);
insert into #p values
(1,'Jo',411420607)
,(2,'Mark',206047758)
,(3,'Paul',552065834)
,(4,'Lisa',653025132);
declare #r table(ID int,DiseaseName varchar(30),PatientID int);
insert into #r values
(1,'Liver Disease',1)
,(2,'Heart Disease',1)
,(3,'Liver Disease',2)
,(4,'Heart Disease',2)
,(5,'Liver Disease',3)
,(6,'Lung Disease',3)
,(7,'Arm Disease',4)
,(8,'Lung Disease',4)
,(9,'Liver Disease',4)
,(10,'Heart Disease',4);
declare #id int = 1;
select r.PatientID
,r2.PatientID as MatchedPatientID
from #r as r
join #r as r2
on r.DiseaseName = r2.DiseaseName
and r.PatientID <> r2.PatientID
where r.PatientID = #id
group by r.PatientID
,r2.PatientID
having count(r2.PatientID) >= 2;

How do I do a multi-table SQL join with grouping

I have three tables that are represented here by objects and that convert to SQLite tables. The CategoryGroup table has multiple Categories and each Category has multiple Phrases.
public class CategoryGroup
{
[PrimaryKey, NotNull]
public int Id { get; set; }
public string Name { get; set; }
public bool Selected { get; set; }
}
public class Category : ICategory
{
[PrimaryKey, NotNull]
public int Id { get; set; }
public int CategoryGroupId { get; set; }
public string Name { get; set; }
public bool Selected { get; set; }
}
public class Phrase : IPhrase
{
public int CategoryId { get; set; }
[PrimaryKey, NotNull]
public string PhraseId { get; set; }
public string English { get; set; }
public string Romaji { get; set; }
public string Kana { get; set; }
public string Kanji { get; set; }
public int Modified { get; set; }
}
What I would like to do is to join these three tables and group them by CategoryGroup and then report on the Selected count.
I created this SQL but I am not sure about the three table join.
SELECT CG.Id, CG.Name, COUNT(*) AS PhraseCount
FROM CategoryGroup AS CG
FROM Category AS C
JOIN CG.Id = P.CategoryId
JOIN Phrase AS P ON C.Id = P.CategoryId
GROUP BY CG.Id
ORDER BY CG.Name
Can someone give me advice on how I can do the join?
You've got the syntax wrong. You'd select from one table, join another on some criteria so that records match, then join the third on some other criteria linking this table.
SELECT CG.Id, CG.Name, COUNT(*) AS PhraseCount
FROM CategoryGroup AS CG
JOIN Category AS C ON C.CategoryGroupId = CG.Id
JOIN Phrase AS P ON P.CategoryId = C.Id
GROUP BY CG.Id
ORDER BY CG.Name;
You may have to GROUP BY CG.Id, CG.Name instead. This is not necessary by the SQL Standard, because the category group name is functionally dependent on the category group ID, but some DBMS demand this though (because they find it too hard to determine functional dependence). I don't know about SQLite in this respect.

Join nested query NHibernate

How do you do this query in NHibernate, with criteria or QueryOver?
select r.relationshipidentifier, r.publicrelationshipid
from relationship r
inner join (
select max(asofdate) as asofdate, s.relationshipidentifier
from relationship s
group by s.relationshipidentifier
) m
on r.asofdate = m.asofdate and r.relationshipidentifier = m.relationshipidentifier
Assume
public class Relationship {
public virtual Guid Id { get; set; }
public virtual DateTime AsOfDate { get; set; }
public virtual Guid RelationshipIdentifier { get; set; }
public virtual Guid PublicRelationshpId { get; set; }
}
the inner join can be described by a subquery.
something to the effect of -
QueryOver.Of<Relationship>()
.SelectList(list =>
list.SelectMax(r => r.AsOfDate),
list.GroupProperty(r=> r.RelationshipIdentifier)
)
and you can use the WithSubquery method of QueryOver to inner join to that subquery.
see this article for further details on QueryOver API.

NHibernate left join select count in one-to-many relationship

I've been looking for a week after a correct synthax whithout success.
I have 2 classes :
public class ArtworkData
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<CommentData> Comments { get; set; }
}
public class CommentData
{
public virtual Guid Id { get; set; }
public virtual string Text { get; set; }
public virtual ProfileData Profile { get; set; }
public virtual ArtworkData Artwork { get; set; }
public virtual DateTime Created { get; set; }
}
I want to do this query :
SELECT this_.ArtworkId as ArtworkId3_3_,
this_.Name as Name3_3_,
this_.Description as Descript3_3_3_,
FROM Artwork this_
LEFT outer JOIN
(SELECT c.ArtworkIdFk, count(1) Cnt
FROM Comment c
GROUP BY c.ArtworkIdFk) as com
on com.ArtworkIdFk = this_.ArtworkId
ORDER BY 1 desc
But I don't find the way to.
At this moment I just have something like this :
ICriteria c = this.Session.CreateCriteria(typeof(ArtworkData));
if(filter.Category !=null)
{
c.CreateAlias("Categories", "cat")
.Add(Restrictions.Eq("cat.Id", filter.Category.Id));
}
DetachedCriteria crit = DetachedCriteria.For(typeof(CommentData), "comment")
.SetProjection(Projections.ProjectionList()
.Add(Projections.Count("comment.Id").As("cnt"))
.Add(Projections.GroupProperty("comment.Artwork.Id")));
c.Add(Expression.Gt(Projections.SubQuery(crit), 0));
c.AddOrder(Order.Desc(Projections.SubQuery(crit)));
But it's not what I want.
I want to get all Artworks order by the number of comments (but I don't need to get this number).
Please help me! I'm going crazy!
I don't understand what are you trying to do with this weird SQL but if you need to get all Artworks with number of comments you can try this query:
<query name="ArtworkWithCommentsCount">
SELECT artwork.Name, artwork.Comments.size
FROM Artwork artwork
</query>
If you use NHibernate 3 you could use this code:
var artworks = Session.Query<Artwork>().OrderBy(a => Comments.Count);
Or you could use HQL:
Session.CreateQuery("from Artwork a order by size(a.Comments)")
Try Detached Criteria. Take a look at this blogpost.

Nhibernate/Hibernate, lookup tables and object design

I've got two tables. Invoice with columns CustomerID, InvoiceDate, Value, InvoiceTypeID (CustomerID and InvoiceDate make up a composite key) and InvoiceType with InvoiceTypeID and InvoiceTypeName columns.
I know I can create my objects like:
public class Invoice
{
public virtual int CustomerID { get; set; }
public virtual DateTime InvoiceDate { get; set; }
public virtual decimal Value { get; set; }
public virtual InvoiceType InvoiceType { get; set; }
}
public class InvoiceType
{
public virtual InvoiceTypeID { get; set; }
public virtual InvoiceTypeName { get; set; }
}
So the generated sql would look something like:
SELECT CustomerID, InvoiceDate, Value, InvoiceTypeID FROM Invoice WHERE CustomerID = x AND InvoiceDate = y
SELECT InvoiceTypeID, InvoiceTypeName FROM InvoiceType WHERE InvoiceTypeID = z
But rather that having two select queries executed to retrieve the data I would rather have one. I would also like to avoid using child object for simple lookup lists. So my object would look something like:
public class Invoice
{
public virtual int CustomerID { get; set; }
public virtual DateTime InvoiceDate { get; set; }
public virtual decimal Value { get; set; }
public virtual InvoiceTypeID { get; set; }
public virtual InvoiceTypeName { get; set; }
}
And my sql would look something like:
SELECT CustomerID, InvoiceDate, Value, InvoiceTypeID
FROM Invoice INNER JOIN InvoiceType ON Invoice.InvoiceTypeID = InvoiceType.InvoiceTypeID
WHERE CustomerID = x AND InvoiceDate = y
My question is how do I create the mapping for this?
I've tried using join but this tried to join using CustomerID and InvoiceDate, am I missing something obvious?
Thanks
If your goal is (as you said) to avoid two queries, you can retrieve the data using a single HQL statement:
select i, it from Invoice i fetch join i.type it where ...
...as documented in the hibernate docs. This should execute only one SQL select statement and retrieve everything without any mapping changes.
This is a regular HQL query and is executed as follows:
IQuery q = s.CreateQuery("select i, it from Invoice i fetch join i.type it where ...");
IList invoices = q.List();
More information is available on the hibernate query language page.