I am trying to load the child collection of a child of the entity I am selecting. I am trying to mimic this way of doing it, basically creating two future queries and then enumerating one of them. This should lead to two queries to the database:
var idd = session.CreateQuery("from ItemDeliveryDetail idd " +
"join fetch idd.ItemDelivery " +
"left join fetch idd.SupplierInvoice " +
"where idd.Id = 21931828")
.Future<ItemDeliveryDetail>();
var spc = session.CreateQuery("from SpecialCondition spc " +
"where spc.ItemDelivery " +
"in (select idd.ItemDelivery " +
"from ItemDeliveryDetail idd " +
"where idd.Id = 21931828)")
.Future<SpecialCondition>();
var result = idd.ToList();
The last line indeed results in two queries to the database. The queries are exactly what I expect (They are rather lengthy and I don't think they are relevant to the question, but if you would like to see them, I pasted them here).
Problem is, the results of those two queries are not combined, i.e. the following enumeration will still query the database for the SpecialConditions of each ItemDelivery:
foreach (var itemDeliveryDetail in result)
{
foreach (var specialCondition in itemDeliveryDetail.ItemDelivery
.SpecialConditions)
{
// Do something
}
}
How to fix that?
Your second query does not load the ItemDelivery.SpecialConditions collections; only an unused list of SpecialConditions.
I agree with Rippo in that using batch-size is usually cleaner and more performant, even if it results in one or two more roundtrips.
That said, your second query should be:
var spc = session.CreateQuery("from ItemDelivery id " +
"join fetch id.SpecialCondition "
"where id in (select idd.ItemDelivery " +
"from ItemDeliveryDetail idd " +
"where idd.Id = 21931828)"
.Future<ItemDelivery>();
One quick win might be to add batch-size=50 to the bag mapping between ItemDelivery and SpecialConditions.
However I suggest you read this blog "Eagerly loading entity associations efficiently with NHibernate" post from Ayende as it might provide you with the answer you are looking for.
You are facing the classic select n + 1 problem here. I would rather have 1 or maybe 2 more trips to the database rather than a big cartesian product resultset. I am sure this will be the most performant route.
You need to use the same base query twice which I think is your problem.
var idd = session.CreateQuery("from ItemDeliveryDetail idd " +
"join fetch idd.ItemDelivery " +
"left join fetch idd.SupplierInvoice " +
"where idd.Id = 21931828")
.Future<ItemDeliveryDetail>();
var spc = session.CreateQuery("from ItemDeliveryDetail idd " +
"join fetch idd.ItemDelivery id " +
"join fetch id.SpecialCondition spc " +
"where idd.Id = 21931828")
.Future<ItemDeliveryDetail>();
var result = idd.ToList();
Yes I realise this may cause a cartesian product for you but I have had good success using this technique.
Related
In my database i have the following rows and columns: https://i.imgur.com/ktUZY9d.jpg
My problem is the same employee has 3 different departments, but he is currently only active in 1. How do I change this SQL statement to only include the latest department he is in, which started in 2018 and ends in 2100, as seen by the ALLOCATION_START and ALLOCATION_END?
Query
string agentIdSubQuery = "SELECT DISTINCT " +
"AGENT_ID " +
"FROM " +
"KS_DRIFT.V_AGENT_ALLOCATION " +
"WHERE " +
"LENGTH(AGENT_INITIALS) < 5";
if(queryParams.SnapshotDate.HasValue)
agentIdSubQuery += " AND " + OracleConversion.ToOracleDate(queryParams.SnapshotDate) + " BETWEEN ALLOCATION_START AND ALLOCATION_END";
Update:
Tried alot of different solutions, but it crashes everytime, when i run through the debugger, further Down in this method this Query is causing me to crash:
string sql = "SELECT " +
"age1.* " +
"FROM " +
"KS_DRIFT.V_AGENT_ALLOCATION age1 " +
"INNER JOIN " +
"(" + agentIdSubQuery + ") age2 ON age1.AGENT_ID = age2.AGENT_ID " +
"ORDER BY " +
"AGENT_INITIALS";
Error Message:
{"Error occured during execution of SQL query: SELECT age1.* FROM KS_DRIFT.V_AGENT_ALLOCATION age1 INNER JOIN (SELECT max(DISTINCT AGENT_ID FROM KS_DRIFT.V_AGENT_ALLOCATION WHERE LENGTH(AGENT_INITIALS) < 5 AND '2018-08-15' BETWEEN ALLOCATION_START AND ALLOCATION_END AND (UPPER(AGENT_INITIALS) = 'JKKA')) age2 ON age1.AGENT_ID = age2.AGENT_ID ORDER BY AGENT_INITIALS."}
Also giving me an inner exception:
{"ORA-00904: \"AGE2\".\"AGENT_ID\": ugyldig identifikator"}
Debugging error screeenshot
Order it by the newest start date (descending) and select Top 1!
string agentIdSubQuery = "AGENT_ID " +
"FROM " +
"KS_DRIFT.V_AGENT_ALLOCATION " +
"WHERE " +
"LENGTH(AGENT_INITIALS) < 5 " +
" AND ROWNUM = 1 " +
" ORDER BY ALLOCATION_START DESC";
EDIT, changed Top 1 to Rownum = 1, for Oracle syntax
The table V_AGENT_ALLOCATION contains various departments per agent along with the dates the agent worked there. You want an agent's last department, which you get with Oracle's KEEP LAST. You haven't given us much information on your table, though. Let's say that the department is referenced by an allocation_id:
select
agent_id,
max(id_allocation) keep (dense_rank last order by allocation_start)
as id_current_allocation
from v_agent_allocation
group by agent_id
order by agent_id;
Your error message shows the final generated SQL:
{"Error occurred during execution of SQL query: SELECT age1.* FROM KS_DRIFT.V_AGENT_ALLOCATION age1 INNER JOIN (SELECT max(DISTINCT AGENT_ID FROM KS_DRIFT.V_AGENT_ALLOCATION WHERE LENGTH(AGENT_INITIALS) < 5 AND '2018-08-15' BETWEEN ALLOCATION_START AND ALLOCATION_END AND (UPPER(AGENT_INITIALS) = 'JKKA')) age2 ON age1.AGENT_ID = age2.AGENT_ID ORDER BY AGENT_INITIALS."}
If you format that so that it's readable, you get:
select age1.*
from ks_drift.v_agent_allocation age1
inner join
( select max(distinct agent_id
from ks_drift.v_agent_allocation
where length(agent_initials) < 5
and '2018-08-15' between allocation_start and allocation_end
and (upper(agent_initials) = 'JKKA') ) age2
on age1.agent_id = age2.agent_id
order by agent_initials
Two syntax issues should jump out:
There is a missing closing bracket after max(distinct agent_id (the distinct is also redundant)
The date literal is missing its date keyword - it should be date '2018-08-15' (or better still, a bind variable).
The brackets around (upper(agent_initials) = 'JKKA') are redundant but perhaps they arise from your generator logic and it's easiest to keep them.
I'm not sure how that relates to your 'newest allocated department' requirement, though. Some sample data (not a screenshot) would help.
If you're looking for the current department, you should compare the allocation dates with the current date.
Your query already has this WHERE clause, so it should work just fine for the example provided. But if you might want to specify that you need only ONE row using the ROWNUM special variable clause (assuming it's Oracle).
SELECT DISTINCT AGENT_ID
FROM KS_DRIFT.V_AGENT_ALLOCATION
WHERE LENGTH(AGENT_INITIALS) < 5
AND ALLOCATION_START < CURRENT_DATE AND CURRENT_DATE < ALLOCATION_END
AND ROWNUM = 1
var db = new SQLiteConnection(dbPath);
try
{
var results = db.Query<Xclass>("SELECT X.COL1, X.COL2, " +
" X.COL3, X.Col4, X.Col5, " +
" A.Col2, A.COL3, B.Col2, C.Col2 " +
"FROM left join Xam X " +
"left join TABLE1 A on X.COL1 = A.COL1 " +
"left join TABLE2 B on X.COL2 = B.COL1 " +
"left join TABLE3 C on X.COL3 = C.COL1 " +
"WHERE X.COL1 ='"+ somevalue +"'");
}
catch (Exception e)
{
// display message
}
XCLASS Contains all the getters and setters.
But it only pulls in the Xam table and not the A, B, or C table values.
I originally had the table name in the Class but took it out to see if that would help.
Any assistance is appreciated. I am trying to avoid linq but if is necessary I will try it.
The instance(s) of Xclass object is created from the results of your query via reflection.
The columns in query's result set are translated to properties of the class
and the setters of hese properties are used to assign their values.
Therefore you must supply some hints to SQlite so it will know which column corresponds to which property. This is done via as keyword.
So, if you want assign the value of A.COL2 column in the result to property named MyCol2Property inside Xclass, you must retrieve it in the query using the syntax
A.Col2 as MyCol2Property
I need to return paginated results i.e the second five records from a table called properties. This works alone with this query:
SELECT
Property_ID,
Property_Type,
Address_Line,
Area,
Postcode,
Weekly_Rate
FROM
dbo.Properties
WHERE
Area LIKE '%" + value + "%'
ORDER BY
Property_ID
OFFSET " + (start*end) + " ROWS
FETCH NEXT " + end + " ROWS ONLY"
The above code is written with C# added.
But I also need the first Image ID from the Images table that references each record returned in the paginated subset.
I tried a FULL OUTER JOIN and it didn't return what was expected, maybe because there is more than one image per property.
Any solutions to this would be great!
Thanks
This seems to nearly work but it's returning an error:
SELECT
dbo.Properties.Property_ID, Property_Type, dbo.Images_Table.[URL]
FROM
dbo.Properties p
OUTER APPLY
(SELECT TOP 1 i.*
FROM dbo.Images_Table i
WHERE i.Property_ID = p.Property_ID
ORDER BY i.Image_ID) i
WHERE
p.Area LIKE '%po%'
ORDER BY
p.Property_ID
OFFSET 0 ROWS
FETCH NEXT 5 ROWS ONLY
This is the error returned :
Msg 4104, Level 16, State 1, Line 1
The multi-part identifier "dbo.Properties.Property_ID" could not be bound.
Msg 4104, Level 16, State 1, Line 1
The multi-part identifier "dbo.Images_Table.URL" could not be bound.
You can use outer apply:
SELECT . . . , i.??
FROM dbo.Properties p OUTER APPLY
(SELECT TOP 1 i.*
FROM images i
WHERE i.Property_ID = p.Property_ID
ORDER BY i.Image_ID
) i
WHERE p.Area LIKE '%" + value + "%'
ORDER BY p.Property_ID
OFFSET " + (start*end) + " ROWS
FETCH NEXT " + end + " ROWS ONLY";
This is a bit speculative on how you are ordering the images. You also need to fill in the columns you want from the images table.
I have got two tables named "studentBio" and "subjects". Fields of both tables are being given below(with some values): On the form i have got something like this:
AND in checkedlistbox1 i am showing corresponding subjects in the form like A+B+C. Which after retrieving from user would be split on the basis of '+' and be added into subjects table all at once.
Fields of studentBio table are given as follows: WHERE (RollNo, RegYear,program, and faculty combine to make composite primary key):
RollNo RegYear stuName program faculty phoneNuber Address
1 2010 John Intermediate Pre-Engineering 343483834 London
2 2011 Leonard Intermediate Pre-Medical 454545445 NewYork
3 2012 Henry Graduation B.A 565656565 Oslo
Similary fields of subjects table are as follows: WHERE
(RollNo, RegYear,program, and faculty combine to make composite
primary key):
RollNo RegYear program faculty subjectName
1 2010 Intermediate Pre-Engineering A
1 2010 Intermediate Pre-Engineering B
1 2010 Intermediate Pre-Engineering C
2 2011 Intermediate Pre-Medical D
2 2011 Intermediate Pre-Medical E
2 2011 Intermediate Pre-Medical F
and so on. Now lets come to the problem. What i am doing is UPDATING (faculty and program) in both tables and subjectN in subjects table. What i have done so far is that i have updated only studentBio table which is quite easy but i can't figure-out as how could i structure my update query to update subjects table? Can somebody please help me structuring the query?
If I understood well, please try this restructured version of your query:
string update_Personal = "UPDATE studentBio INNER JOIN subjects ON studentBio.RollNo = subjects.RollNo AND studentBio.RegYear = subjects.RegYear AND studentBio.program = subjects.program AND studentBio.faculty = subjects.faculty SET studentBio.program = ProgramU, studentBio.faculty = FacultyU, subjects.subjectName = SubjectNameU WHERE studentBio.RollNo = " + Convert.ToInt32(this.rollNumber7_combo.SelectedItem.ToString()) + " AND studentBio.RegYear = " + Convert.ToInt32(this.comboBox3.SelectedItem.ToString()) + " AND studentBio.program = '" + this.comboBox1.SelectedItem.ToString() + "' AND studentBio.faculty = '" + this.comboBox2.SelectedItem.ToString() + "'";
I've added the following parts to make it update subjects table:
studentBio INNER JOIN subjects ON studentBio.RollNo = subjects.RollNo AND studentBio.RegYear = subjects.RegYear AND studentBio.program = subjects.program AND studentBio.faculty = subjects.faculty - this part joins the subjects table making it's update possible
, subjects.subjectName = SubjectNameU - fragment in the SET block updating subjectName column in subjects table
WHERE studentBio.RollNo = " + Convert.ToInt32(this.rollNumber7_combo.SelectedItem.ToString()) + " AND studentBio.RegYear = " + Convert.ToInt32(this.comboBox3.SelectedItem.ToString()) + " AND studentBio.program = '" + this.comboBox1.SelectedItem.ToString() + "' AND studentBio.faculty = '" + this.comboBox2.SelectedItem.ToString() + "'" - the last part is the WHERE block in which prefix studentBio. was added to each key column to precise to which table the filtering should be applied (because both tables ha the same key column names)
I hope it could help you in some way.
I want to retrieve back Student names and Student ID that don't exist in the Submission table, but exist in the Students table, and bind it into the GridView. For example Class A has 40 students, but only 38 students submitted the project. I want the 2 students who don't submit to appear in the GridView where the Teacher can view it
SELECT *
FROM Students
LEFT JOIN dbo.Submission ON Students.Student_Id = Submission.Student_Id
AND Students.Subject_Id = Submission.Subject_Id
WHERE Students.Subject_Class='" + Session["Subject_Class"].ToString() + "'
AND Students.Subject_Id = '" + Session["Subject_Id"].ToString() + "'
AND Submission.Proj_Sub = '" + Session["Proj_Sub"].ToString() + "'
And Submission.Student_Id IS NULL
When I use the above-mentioned statement, the GridView appears to be empty.
You probably want something like this.
SELECT *
FROM Students
WHERE Students.Subject_Class='" + Session["Subject_Class"].ToString() + "'
AND Students.Subject_Id = '" + Session["Subject_Id"].ToString() + "'
AND NOT EXISTS (SELECT *
FROM Submissions
WHERE Submissions.Student_Id = Students.Student_Id
AND Submissions.Subject_Id = Students.Subject_Id
AND Submissions.Proj_Sub = '" + Session["Proj_Sub"].ToString() + "')
There are a number of approaches you could take, but this seems like the cleanest. Most importantly, though, you should place emphasis on moving away from your currently-SQL-injection prone approach. Please parameterize your queries.