Using these tables:
*student {'s_id','s_name,'...} , class {'c_id','c_name',...} student2class {'s_id','c_id'}, grades {'s_id','c_id','grade'}*
Is it possible to perform a query (nested query?) put class name as subtitle and then all students (of that class) and grades, next class name as subtitle ...
The result I need is:
Maths
John .... C
Anna .... B
[...]
Biology
Anna .... C
Jack .... A
[...]
For each row from class I'll have a subquery fetching all data related with this class
No need of any sub-query. You can get your data this way:
SELECT c_name, s_name_, grade
FROM student, class, grades
WHERE student.s_id = grades.s_id and class.c_id = grades.c_id
ORDER BY c_name
The presentation of results depends on your system/tools, as others already said. This is a link to the solution for Microsoft Access:
http://office.microsoft.com/en-001/access-help/create-a-grouped-or-summary-report-HA001159160.aspx
The solution should be implemented in your client side code and not in the database. From database you should just get a simple table formatted data (subject, student, grade)
Then convert the above recordset to the format you want:
For an example in C# you could convert the recordset into lookup
var Lookup = DataSet.Tables[0].Rows.ToLookup(x => x["subjectColumn"]);
now you can loop through the lookup and format your result
foreach (var grade in Lookup)
{
subject = grade.Key;
...
}
Related
I have 2 nodes:
Students and Subjects.
I want to be able to add multiple student names to multiple subjects at the same time using cypher query.
So far I have done it by iterating through the list of names of students and subjects and executing the query for each. but is there a way to do the same in the query itself?
This is the query I use for adding 1 student to 1 subject:
MATCH
(s:Student)-[:STUDENT_BELONGS_TO]->(c:Classroom),
(u:Subjects)-[:SUBJECTS_TAUGHT_IN]->(c:Classroom)
WHERE
s.id = ${"$"}studentId
AND c.id = ${"$"}classroomId
AND u.name = ${"$"}subjectNames
AND NOT (s)-[:IN_SUBJECT]->(u)
CREATE (s)-[:IN_SUBJECT]->(u)
So I want to be able to receive multiple subjectNames and studentIds at once to create these connections. Any guidance for multi relationships in cypher ?
I think what you are looking for is UNWIND. If you have an array as parameter to your query:
studentList :
[
studentId: "sid1", classroomId: "cid1", subjectNames: ['s1','s2'] },
studentId: "sid2", classroomId: "cid2", subjectNames: ['s1','s3'] },
...
]
You can UNWIND that parameter in the beginning of your query:
UNWIND $studentList as student
MATCH
(s:Student)-[:STUDENT_BELONGS_TO]->(c:Classroom),
(u:Subjects)-[:SUBJECTS_TAUGHT_IN]->(c:Classroom)
WHERE
s.id = student.studentId
AND c.id = student.classroomId
AND u.name = in student.subjectNames
AND NOT (s)-[:IN_SUBJECT]->(u)
CREATE (s)-[:IN_SUBJECT]->(u)
You probably need to use UNWIND.
I haven't tested the code, but something like this might work:
MATCH
(s:Student)-[:STUDENT_BELONGS_TO]->(c:Classroom),
(u:Subjects)-[:SUBJECTS_TAUGHT_IN]->(c:Classroom)
WITH
s AS student, COLLECT(u) AS subjects
UNWIND subjects AS subject
CREATE (student)-[:IN_SUBJECT]->(subject)
Im trying to eliminate classes with only 1 pupil
Here is the result i have
Desired Result
I want only class c to show as it has more than one pupil
I believe its some way of using HAVING but i cant figure it out.
SELECT Pupil, class
FROM school
Using IN
select *
from school
where class in (select class
from school
group by class
having count(distinct Pupil) > 1)
Given the following schema created using the OrientDB Document API:
OClass team = getoDocDatabase().getMetadata().getSchema().createClass(TEAM);
team.createProperty(NAME, OType.STRING);
OClass driver = getoDocDatabase().getMetadata().getSchema().createClass(DRIVER);
driver.createProperty(NAME, OType.STRING);
OClass car = getoDocDatabase().getMetadata().getSchema().createClass(CAR);
car.createProperty(NAME, OType.STRING);
// Relationships
team.createProperty(CARS_HERITAGE, OType.LINKSET, car);
car.createProperty(BUILT_BY, OType.LINK, team);
car.createProperty(DRIVEN_BY, OType.LINKSET, driver);
driver.createProperty(DRIVER_OF, OType.LINKSET, car);
What's the sql query to fetch all the teams that Fernando Alonso has driven for?
In relational SQL would be as easy as
SELECT team.name FROM {the join} where driver.name = 'Fernando Alonso'
I have try with this db
create class Team
CREATE PROPERTY Team.name String
create class DRIVER
CREATE PROPERTY DRIVER.name String
create class Car
CREATE PROPERTY Car.name String
CREATE PROPERTY Team.CARS_HERITAGE LINKSET Car
CREATE PROPERTY Car.BUILT_BY LINK Team
CREATE PROPERTY Car.DRIVEN_BY LINKSET DRIVER
CREATE PROPERTY DRIVER.DRIVER_OF LINKSET Car
INSERT
INSERT INTO TEAM(name) values ("Ferrari"),("Renault") // Ferrari 12:0 Renault 12:1
insert into Driver(name) values ("Fernando Alonso"),("Giancarlo Fisichella") // Alonso 13:0 Fisichella 13:1
insert into car(name,BUILT_BY,DRIVEN_BY) values ("car ferrari",#12:0,[#13:0,#13:1])
insert into car(name,BUILT_BY,DRIVEN_BY) values ("car renault",#12:1,[#13:0])
Query
select BUILT_BY.name as TeamName from car where DRIVEN_BY.name contains "Fernando Alonso"
Hope it helps.
UPDATE 1
select distinct(BUILT_BY.name) as team from car where DRIVEN_BY.name contains "Fernando Alonso"
FROM JAVA API
UPDATE 2
FROM JAVA API
The answer from #Alessandro is correct, however I've found some weirdnesses in the way the sql is interpreted. Let me explain myself.
I've simplified the goal, let's try to find the query to fetch the teams which have had at least a car.
This first query works, is the one suggested by Alessandro. It returns a list of documents containing one property, that is the name of the team.
select distinct(team.name) as name from Car
This second query works as well, returning the list of teams (as documents).
select expand(distinct(team)) from Car
This third query works and returns exactly the same result that the previous, so it ignores the ".name" part of the select.
select expand(distinct(team)).name from Car
This last query fails. Well it doesn't fail, but it doesn't return what I expected, it returns a list of links to the teams.
select distinct(team).name from Car
Tests running the queries: Tests.
I have a simple test object model in which there are schools, and a school has a collection of students.
I would like to retrieve a school and all its students who are above a certain age.
I carry out the following query, which obtains a given school and the children which are above a certain age:
public School GetSchoolAndStudentsWithDOBAbove(int schoolid, DateTime dob)
{
var school = this.Session.CreateCriteria(typeof(School))
.CreateAlias("Students", "students")
.Add(Expression.And(Expression.Eq("SchoolId", schoolid), Expression.Gt("students.DOB", dob)))
.UniqueResult<School>();
return school;
}
This all works fine and I can see the query going to the database and returning the expected number of rows.
However, when I carry out either of the following, it gives me the total number of students in the given school (regardless of the preceding request) by running another query:
foreach (Student st in s.Students)
{
Console.WriteLine(st.FirstName);
}
Assert.AreEqual(s.Students.Count, 3);
Can anyone explain why?
You made your query on the School class and you restricted your results on it, not on the mapped related objects.
Now there are many ways to do this.
You can make a static filter as IanL said, however its not really flexible.
You can just iterate the collection like mxmissile but that is ugly and slow (especially considering lazy loading considerations)
I would provide 2 different solutions:
In the first you maintain the query you have and you fire a dynamic filter on the collection (maintaining a lazy-loaded collection) and doing a round-trip to the database:
var school = GetSchoolAndStudentsWithDOBAbove(5, dob);
IQuery qDob = nhSession.CreateFilter(school.Students, "where DOB > :dob").SetDateTime("dob", dob);
IList<Student> dobedSchoolStudents = qDob.List<Student>();
In the second solution just fetch both the school and the students in one shot:
object result = nhSession.CreateQuery(
"select ss, st from School ss, Student st
where ss.Id = st.School.Id and ss.Id = :schId and st.DOB > :dob")
.SetInt32("schId", 5).SetDateTime("dob", dob).List();
ss is a School object and st is a Student collection.
And this can definitely be done using the criteria query you use now (using Projections)
Unfortunately s.Students will not contain your "queried" results. You will have to create a separate query for Students to reach your goal.
foreach(var st in s.Students.Where(x => x.DOB > dob))
Console.WriteLine(st.FirstName);
Warning: That will still make second trip to the db depending on your mapping, and it will still retrieve all students.
I'm not sure but you could possibly use Projections to do all this in one query, but I am by no means an expert on that.
You do have the option of filtering data. If it there is a single instance of the query mxmissle option would be the better choice.
Nhibernate Filter Documentation
Filters do have there uses, but depending on the version you are using there can be issues where filtered collections are not cached correctly.
I have a Person class. A person class contains a collection of Friends (also Person objects). A person class also has a LatestLogin property which is the LatestLogin time.
For a given person, I want to return their first 10 friends ordered by descending LatestLogin.
HQL I can do no problem:
select friends from Person person inner join person.Friends friends where person = :person order by friends.LatestLogin desc
How do I write this in a Criteria Query? I don't want the containing person object, just a List of the person's friends ordered by LatestLogin.
Here it is:
var cachedPosts = Session.CreateCriteria<Person>("main")
.CreateCriteria("Friends", "f")
.Add(Restrictions.Eq("f.Id", person.ID))
.AddOrder(Order.Desc("main.LatestLogin"))
.List<Person>();