NHibernate mapping many-to-one fetch="join" formula - nhibernate-mapping

I've got the tables:
PROCESSES STEP_STATUSES STEPS
--------- ------------- -----------
PROC_ID STAT_ID STEP_ID
PROC_NAME NAME STEP_NUMBER
... STEP_NAME
...
STAT_ID
PROC_ID
Is there any way a can map the Processes table into NHibernate with a property CurrentStep (which is a mapped STEPS table row) ?
I can do almost what I need with a mapping:
<many-to-one name="CurrentStep" formula="(
select t.step_id
from STEPS t
where PROC_ID = t.PROC_ID
and STEP_NUMBER = nvl((select max(STEP_NUMBER), PROC_ID from STEPS where STAT_ID > 0 and PROC_ID = t.PROC_ID),
(select min(STEP_NUMBER), PROC_ID from STEPS where STAT_ID = 0 and PROC_ID = t.PROC_ID))
)"/>
But I can't set fetch="join" for this field, and I want to fetch all data with one query, altogether with CurrentStep property value - like this:
select t.*, st.*
from PROCESSES t
left outer join (
select STEP_ID, PROC_ID
from STEPS s1
where STEP_NUMBER = nvl((select max(STEP_NUMBER) from STEPS where STAT_ID > 0 and PROC_ID = s1.PROC_ID),
(select min(STEP_NUMBER) from STEPS where STAT_ID = 0 and PROC_ID = s1.PROC_ID))
) s on s.PROC_ID = t.PROC_ID
left outer join STEPS st on st.PROC_ID = s.PROC_ID and st.STEP_ID = s.STEP_ID
It's necessary because of performance.
So, the question id:
Is there any way to set fetch="join" for such a complex formula field?
Is there any way to join such a complex subquery?
Is there any way to create ICriteria to get such query result if it is impossible to set mapping?
I'm sorry if the question is not clear, I'll try to clarify it if any questions would appear/

an idea to query for what you want using batched reads
var lastStepWithStatus = session.CreateCriteria<Step>()
.Add(Restriction.Eq("Process.Id", processId))
.Add(Restriction.NotNull("Status"))
.AddOrder(Order.Desc("Number"))
.SetMaxResults(1)
.Future<Step>();
var firstStepWithoutStatus = session.CreateCriteria<Step>()
.Add(Restriction.Eq("Process.Id", processId))
.Add(Restriction.IsNull("Status"))
.AddOrder(Order.Asc("Number"))
.SetMaxResults(1)
.Future<Step>();
return lastStepWithStatus.Concat(firstStepWithoutStatus).Select(step => new { Process = Step.Process, CurrentStep = step }).FirstOrDefault();
another option is to use LINQ on the Steps collection to get the current step which implies that all steps are loaded first however.

Related

Query using subqueries in where statement

I have a query with subqueries in the where statement which works fine but I need to improve on it but I am not sure of the best way to go about this.
Basically I am getting last years results for students in this years classes. The code below works fine for classcode '10DRADRA1' but there are many more classes and I want the query to iterate through all classes for 2017 Semester 1. I need to have the classcode included as one of the output fields also. So the query will start with the first class and give all the results for that class, then do the same for the next class in the subquery. I am not sure how to include the classcode in the select statement next to all the results from the previous year.
Note that students may do a number of classes this year so there may be some repetition of the results from the previous year. Every class they do this year should include the students name and their results from the previous year. I hardcoded '10DRADRA1' in just to get things working, if I remove it it provides all students in the school only once, while I want each class they are in once and the same results for every class they are in.
Also not that vStudentReportsSemesterResults is a view that holds all results, StudentClasses is a table which holds the class codes a student is doing and SubjectClasses is a table holding the codes for all classes in the school
Is anyone able to advise on the best way of doing this? Here is my current code.
SELECT vStudentReportsSemesterResults.StudentID,
vStudentReportsSemesterResults.AssessResultsResult,
vStudentReportsSemesterResults.AssessAreaHdgAbbrev2,
vStudentReportsSemesterResults.FileSemester,
vStudentReportsSemesterResults.FileYear,
vStudentReportsSemesterResults.ClassLearningAreaCode,
vStudentReportsSemesterResults.AssessmentCode,
vStudentReportsSemesterResults.StudentNameInternal
FROM vStudentReportsSemesterResults
WHERE vStudentReportsSemesterResults.StudentID in
(
select StudentClasses.ID from StudentClasses
where StudentClasses.filesemester = 1 and
StudentClasses.fileyear = 2017 and
StudentClasses.classcode in
(
select SubjectClasses.ClassCode from SubjectClasses
where SubjectClasses.FileYear = 2017 and
SubjectClasses.FileSemester = 1 and
SubjectClasses.FileType = 'A' and
SubjectClasses.ClassCampus = 'S' and
SubjectClasses.ClassCode like '10DRADRA1'
)
)
and (vStudentReportsSemesterResults.ClassLearningAreaCode = 'ENG' OR
vStudentReportsSemesterResults.ClassLearningAreaCode = 'MAT' OR
vStudentReportsSemesterResults.ClassLearningAreaCode = 'SCI') AND
(vStudentReportsSemesterResults.AssessAreaHdgAbbrev2 = 'Grade' OR
vStudentReportsSemesterResults.AssessAreaHdgAbbrev2 = 'Level') AND
(vStudentReportsSemesterResults.AssessResultsResult <> '') AND
(vStudentReportsSemesterResults.FileYear = 2016) AND
(vStudentReportsSemesterResults.FileSemester = 4)
Join StudentClasses to vStudentReportsSemesterResults:
SELECT
sc.ClassCode,
srsr.StudentID,
srsr.AssessResultsResult,
srsr.AssessAreaHdgAbbrev2,
srsr.FileSemester,
srsr.FileYear,
srsr.ClassLearningAreaCode,
srsr.AssessmentCode,
srsr.StudentNameInternal
FROM vStudentReportsSemesterResults srsr
JOIN StudentClasses sc
ON sc.ID = srsr.StudentID
AND sc.FileSemester = 1
AND sc.FileYear = srsr.FileYear + 1
AND EXISTS
(
select *
from SubjectClasses subc
where subc.FileYear = sc.FileYear
and subc.ClassCode = sc.ClassCode
and subc.FileSemester = sc.FileSemester
and subc.FileType = 'A'
and subc.ClassCampus = 'S'
)
WHERE srsr.ClassLearningAreaCode in ('ENG', 'MAT', 'SCI')
AND srsr.AssessAreaHdgAbbrev2 in ('Grade', 'Level')
AND srsr.AssessResultsResult <> ''
AND srsr.FileYear = 2016
AND srsr.FileSemester = 4;
This is more readable:
SELECT
ClassCode,
StudentID,
AssessResultsResult,
AssessAreaHdgAbbrev2,
ClassLearningAreaCode,
AssessmentCode,
StudentNameInternal,
lr.FileYear,
lr.FileSemester,
tr.FileYear,
tr.FileSemester,
tr.FileType,
tr.ClassCampus
FROM
vStudentReportsSemesterResults lr
inner join (
select ID, sc.ClassCode, sc.FileYear, sc.filesemester, sbjc.FileType, sbjc.ClassCampus
from StudentClasses sc
INNER JOIN SubjectClasses sbjc ON SC.classcode = sbjc.ClassCode and sc.FileSemester=sbjc.FileSemester and sc.FileYear=sbjc.FileYear
) tr on lr.StudentID = tr.ID
and lr.FileYear = tr.FileYear - 1
WHERE
ClassLearningAreaCode IN ('ENG', 'MAT', 'SCI')
AND AssessAreaHdgAbbrev2 IN ('Grade', 'Level')
AND (AssessResultsResult <> '')
AND lr.FileYear = 2016
AND lr.FileSemester = 4
and tr.FileSemester = 1
and tr.FileType = 'A'
and tr.ClassCampus = 'S'
ORDER BY 2,1,5
In SELECT section I have added some fields to better understand result.
In WHERE clause you can control how to filter comparision.
Put some indexes on join and filter columns and the query will run fast

Speed up SQL query with nested query

I hope you guys can help me. I have the following SQL query, I think it's not very heavy but it takes about 5 minutes to complete. Please let me know if you have another way to complete this:
update rep
set rep.StatusID= 2,
rep.Available= 1,
rep.User= #user
from dbo.MCP_Rep_Compensation_Plan rep
left join dbo.MCP_Compensation_Plan compensationplan on compensationplan.Compensation_Plan_ID = #compplan_id and compensationplan.Active = 1
left join dbo.MRRS_Cycle actualcycle on actualcycle.CycleID = compensationplan.CycleID and actualcycle.Active = 1
left join dbo.MRRS_Cycle lastcycle on lastcycle.Consecutive = actualcycle.Consecutive -1 and lastcycle.Active = 1
where rep.Active = 1 and rep.ID_Compensation_Plan = #compplan_id and exists(
select OrderID
from dbo.MCP_Orders
where Active = 1 and Order_cycle = lastcycle.CycleID and OrderRepID = rep.RepID
and Order_Status in(28,30))
I do see some places your query can be rewritten, but I'm not sure how much it will help without looking at your execution plans and ensuring you have the appropriate indices in place.
For example, make sure you include actual join criteria in your first join to the compensationplan table. Do this by joining on compensationplan.Compensation_Plan_ID and rep.ID_Compensation_Plan.
Also, I don't see the need for the OUTER JOINs since you're using some of those tables in your correlated exist subquery.
Here is the updated query:
update rep
set rep.StatusID= 2,
rep.Available= 1,
rep.User= #user
from dbo.MCP_Rep_Compensation_Plan rep
join dbo.MCP_Compensation_Plan compensationplan on
compensationplan.Compensation_Plan_ID = rep.ID_Compensation_Plan and compensationplan.Active = 1
join dbo.MRRS_Cycle actualcycle on
actualcycle.CycleID = compensationplan.CycleID and actualcycle.Active = 1
join dbo.MRRS_Cycle lastcycle on
lastcycle.Consecutive = actualcycle.Consecutive -1 and lastcycle.Active = 1
where rep.Active = 1
and rep.ID_Compensation_Plan = #compplan_id
and exists(
select OrderID
from dbo.MCP_Orders
where Active = 1
and Order_cycle = lastcycle.CycleID
and OrderRepID = rep.RepID
and Order_Status in(28,30)
)

sql query updates all rows and not only where clause

I am having trouble executing this query:
update public.fortune_companies
set industry_id = (select id
from public.industries
where name = 'Agriculture, Forestry and Fishing')
from Temp_Sic_Fortune_Companies as temp
left join public.fortune_companies as fc on fc.account_name = temp.account_name
where temp.sic between '0' and '499';
I think this is supposed to set the industry_id for only ones that have a sic number of 0-499 but it actually sets every record to the same id. No matter if the sic number is between 0-499 or not.
Why is this.
DECLARE #id INT;
SELECT #id = id
FROM public.industries
WHERE name = 'Agriculture, Forestry and Fishing';
UPDATE fc
SET industry_id = #id
FROM public.fortune_companies AS fc
WHERE EXISTS
(
SELECT 1
FROM dbo.Temp_Sic_Fortune_Companies
WHERE account_name = fc.account_name
AND sic BETWEEN '0' and '499'
);
Of course, if temp.sic = '3000', it will be part of the set. This is one of the dangers of using the wrong data type (or the wrong operator). You can fix that by saying:
AND sic BETWEEN '0' and '499'
AND LEN(sic) <= 3
Or by saying:
AND CASE WHEN ISNUMERIC(sic) = 1 THEN
CONVERT(INT, sic) ELSE -1 END BETWEEN 0 AND 499
(This avoids errors if - since you've let them - someone enters a non-numeric value into the column.)
Or by using the right data type in the first place.
Change the left join to inner join

Django raw sql produces a queryset I can't iterate - CLOSED

I've got a sql select query which returns two rows:
SELECT contacts_patientcontact.contact_id, patient_firstname, recent_mailouts
FROM contacts_patientcontact
INNER JOIN patients_patientcore
ON contacts_patientcontact.patient_id = patients_patientcore.patient_id
LEFT JOIN (SELECT contact_id, COUNT(*) as recent_mailouts
FROM contacts_communicationinstance
WHERE communication_type = 'questionnaire mailout'
GROUP BY contact_id) mail_outs
ON contacts_patientcontact.contact_id = mail_outs.contact_id
WHERE contact_date BETWEEN '2012/03/05' AND '2012/03/12'
AND contact_type = 'Postal Questionnaire'
AND patient_dead != 1
AND consent_withdrawn IS NULL
AND lost_follow_up != 1
AND (key = 'A' OR key = 'C')
AND (recent_mailouts < 1
OR recent_mailouts IS NULL);
However when I add it into django using the raw method the queryset doesn't seem to be iterable.
def weekly_questionnaire_mailout_query(monday):
"""
Returns a query set of PatientContact objects for patients
due a mailout in the week following the parameter 'monday'.
"""
nxt_monday = monday + datetime.timedelta(weeks=1)
nxt_monday_str = nxt_monday.strftime('%Y/%m/%d')
monday_str = monday.strftime('%Y/%m/%d')
contacts = PatientContact.objects.raw("""
SELECT contacts_patientcontact.contact_id
FROM contacts_patientcontact
INNER JOIN patients_patientcore
ON contacts_patientcontact.patient_id = patients_patientcore.patient_id
LEFT JOIN (SELECT contact_id, COUNT(*) as recent_mailouts
FROM contacts_communicationinstance
WHERE communication_type = 'questionnaire mailout'
GROUP BY contact_id) mail_outs
ON contacts_patientcontact.contact_id = mail_outs.contact_id
WHERE contact_date BETWEEN '%s' AND '%s'
AND contact_type = 'Postal Questionnaire'
AND patient_dead != 1
AND consent_withdrawn IS NULL
AND lost_follow_up != 1
AND (cora = 'A' OR cora = 'C')
AND (recent_mailouts < 1
OR recent_mailouts IS NULL);
""" % (monday_str, nxt_monday_str)
)
return contacts
contacts = weekly_questionnaire_mailout_query(monday)
for contact in contacts:
patients.add(contact.patient_id)
That last line is never reached. (I've checked the dates are correct, and I've included the PatientContact model below).
class PatientContact(models.Model):
contact_id = models.AutoField(primary_key=True)
patient_id = models.ForeignKey(PatientCore, db_column="patient_id",
verbose_name="patient")
# additional fields..
I'm at a loss with this - instead of showing the items in a queryset my (pydevd) debugger shows a RawQuerySet object. The same function (with the same parameter) is returning an object that djangotables2 handles fine (producing the table I'd expect from the sql output).
EDIT
That's embarrassing - it was the dates after all - I wasn't actually running the same SQL query (I thought I'd checked and rechecked them last week). Apologies to anyone who's spent any time on this.
This was a mistake. I was running the wrong piece of code.
The solution is to be more carefull!

Need to create a report for contacts that have missed workflow activities

We had an issue where our workflows have not been creating activities.
I now need to report which accounts have not had their workflows invoked.
I've tried advanced find and then moved to sql.
My question is can someone provide a simple starter query to pull which 'entity' has NOT had a specific activity associated with it?
Please let me know if the question is not clear enough or more info, is needed.
Below is a solution using SQL where I step through my thought process, and below that is a solution that gets started with the C# API (edit: just realized this is for a report, so that part can be disregarded). I've commented in most places, so I hope my methods are fairly straightforward.
SQL
1.
--get all the entities that aren't activities and aren't intersect entities (N:N tables)
--Put in your own where conditions to further filter this list,
--which is still probably far too expansive
SELECT
A.name EntityName
FROM MetadataSchema.Entity A
WHERE
A.IsActivity = 0
AND A.IsIntersect = 0
2.
--CROSS JOIN the non-activity entities with the activity entities
--to get a list of all possible entity/activity pairings
SELECT DISTINCT
A.name EntityName
, B.Name ActivityName
FROM MetadataSchema.Entity A
CROSS JOIN MetadataSchema.Entity B
WHERE
A.IsActivity = 0
AND A.IsIntersect = 0
AND B.IsActivity = 1
3.
--LEFT JOIN the partial cartesian join above against the Activity table,
--making a note of which entities actually have activity records.
--This will provide a complete list of which entity/activity pairings
--exist and don't exist
SELECT
A.name EntityName
, B.Name ActivityName
--if there is a matching activity, the unique key,
--ActivityTypeCode (int), will be positive.
--So, if there is a positive sum for an entity/activity
--pairing, you know there is a valid pair; otherwise
--no pair
, CAST(CASE WHEN sum(coalesce(C.ActivityTypeCode, 0)) > 0
THEN 1
ELSE 0
END AS BIT) EntityOwnsActivity
FROM MetadataSchema.Entity A
CROSS JOIN MetadataSchema.Entity B
LEFT JOIN dbo.ActivityPointer C ON
--ObjectTypeCode is a unique identifier for Entities;
--RegardingObjectTypeCode is the code for the entity type
--associated with a particular activity
A.ObjectTypeCode = C.RegardingObjectTypeCode
--ActivityTypeCode is the code for the particular activity
AND B.ObjectTypeCode = C.ActivityTypeCode
WHERE
A.IsActivity = 0
AND A.IsIntersect = 0
AND B.IsActivity = 1
GROUP BY
A.name
, B.Name
4.
--Putting it all together, using the above master table,
--filter out the entities/activities you're interested in
--(in this case, all entities that aren't associated with
--any emails)
SELECT
EntityName
FROM
(
SELECT
A.name EntityName
, B.Name ActivityName
, CAST(CASE WHEN sum(coalesce(C.ActivityTypeCode, 0)) > 0
THEN 1
ELSE 0
END AS BIT) EntityOwnsActivity
FROM MetadataSchema.Entity A
CROSS JOIN MetadataSchema.Entity B
LEFT JOIN dbo.ActivityPointer C ON
A.ObjectTypeCode = C.RegardingObjectTypeCode
AND B.ObjectTypeCode = C.ActivityTypeCode
WHERE
A.IsActivity = 0
AND A.IsIntersect = 0
AND B.IsActivity = 1
GROUP BY
A.name
, B.Name
) EntityActivities
WHERE ActivityName = 'Email'
AND EntityOwnsActivity = 0
ORDER BY
EntityName
C#.NET API
using (OrganizationServiceProxy _serviceProxy =
new OrganizationServiceProxy(
new Uri(".../XRMServices/2011/Organization.svc"), null, null, null))
{
_serviceProxy.EnableProxyTypes();
RetrieveAllEntitiesRequest request = new RetrieveAllEntitiesRequest()
{
EntityFilters = EntityFilters.Entity,
RetrieveAsIfPublished = true
};
// Retrieve the MetaData.
EntityMetadata[] entities =
((RetrieveAllEntitiesResponse)_serviceProxy.Execute(request)).EntityMetadata;
var ents = from e1 in entities.Where(x => x.IsActivity != true)
.Where(x => x.IsIntersect != true)
from e2 in entities.Where(x => x.IsActivity == true)
select new
{
entityName = e1.SchemaName
,
activityName = e2.SchemaName
};
//at this point, because of the limited nature of the Linq provider for left joins
//and sums, probably the best approach is to do a fetch query on each entity/activity
//combo, do some sort of sum and find out which combos have matches
// in the activity pointer table
//API = very inefficient; maybe improved in next CRM release? Let's hope so!
}