Optimizing a SQL query for SQL Server 2012 - tsql

I have a database with 8 tables out of which I need to be able to search for a specific value in 6 of them.
The query, in short, should do something like this:
SELECT i.InActivity,
i.ReceivedTime,
i.ReceivedFrom,
i.DataSize,
o.FileName,
o.FileSize,
o.Status,
s1.UniqueReference,
s1.SenderCode,
s1.ReceiverCode,
s2.DetailId,
s3.UnitDescription,
t1.SenderCode,
t1.ReceiverCode,
t2.TaskDescription,
t3.TaskStatus
FROM InData i
LEFT JOIN OutData o WITH(NOLOCK) ON i.ActivityID = o.InActivity
LEFT JOIN SupplyDataLevel1 s1 WITH(NOLOCK) ON s1.InActivity = i.ActivityID
LEFT JOIN SupplyDataLevel2 s2 WITH(NOLOCK) ON s2.ParentId = s1.ActivityID
LEFT JOIN SupplyDataLevel3 s3 WITH(NOLOCK) ON s3.ParentId = s2.ActivityID
LEFT JOIN TasksDataLevel1 t1 WITH(NOLOCK) ON t1.InActivity = i.ActivityID
LEFT JOIN TasksDataLevel2 t2 WITH(NOLOCK) ON t2.ParentId = t1.ActivityID
LEFT JOIN TasksDataLevel3 t3 WITH(NOLOCK) ON t3.ParentId = t2.ActivityID
WHERE s1.IdSender = #searchParam OR
s1.IdReceiver = #searchParam OR
s2.StackNumber = #searchParam OR
s2.StackOrderNumber = #searchParam OR
s3.Reference = #searchParam OR
t1.ProcessReference = #searchParam OR
t1.ProcessStage = #searchParam OR
t1.MasterNumber = #searchParam OR
t2.ProcessStep = #searchParam OR
t3.SubProcessStep = #searchParam
So question is, there has to be a better way than this. The tables contains millions of rows and executing this query will, no doubt, take hours and hours to execute, which rhymes badly with the web site which will have the search page.
Should I create a view and query that one, would that be a better alternative, performance wise?
Any help is welcome.

Related

sql multiple join (right. left, inner) to mongodb aggregate

//I have a sql like this
SELECT
x.attr1,
p.attr2,
p2.attr3,
o.attr4,
p2.attr5,
a2.attr6
FROM TABLE_O o
RIGHT JOIN TABLE_A a ON a.id = o.id AND a.id2 IS NULL
LEFT JOIN TABLE_A a2 ON a2.id = o.id AND a2.id2 = a.id3
LEFT JOIN TABLE_P p ON a.id4 = p.id4
LEFT JOIN TABLE_P p2 ON a2.id4 = p2.id4
INNER JOIN TABLE_X x ON a.id3 = x.id3
WHERE o.type = 'someText' AND a2.id4
IN ( SELECT id3 FROM TABLE_P WHERE TYPE = "anotherText")
AND x.attr1 = "someText"
//I need an idea about how can i do this on mongo aggregates, thanks.
nosql is a non-relational database type. but The following link may be useful.
mongodb aggregation

SQL using data field twice

I currently have the following:
SELECT TblSchoolManagementForms.txtForm, TblPupilManagementPupils.txtSchoolID, TblPupilManagementPupils.txtSurname, TblPupilManagementPupils.txtForename,
TblStaff.Fullname AS TutorFullName, TblStaff.SchoolEmailAddress, TblSchoolManagementYears.txtYearTutor, TblSchoolManagementYears.txtAsstYearTutor
FROM TblPupilManagementPupils INNER JOIN
TblSchoolManagementForms ON TblPupilManagementPupils.txtForm = TblSchoolManagementForms.txtForm INNER JOIN
TblStaff ON TblSchoolManagementForms.txtFormTutor = TblStaff.User_Code INNER JOIN
TblSchoolManagementYears ON TblPupilManagementPupils.intNCYear = TblSchoolManagementYears.intNCYear AND
TblSchoolManagementForms.intNCYear = TblSchoolManagementYears.intNCYear
WHERE TblSchoolManagementYears.intNCYear > 6
UNION
SELECT TblSchoolManagementForms.txtForm, TblPupilManagementPupils.txtSchoolID, TblPupilManagementPupils.txtSurname, TblPupilManagementPupils.txtForename,
TblStaff.Fullname AS TutorFullName, TblStaff.SchoolEmailAddress, TblSchoolManagementYears.txtYearTutor, TblSchoolManagementYears.txtAsstYearTutor
FROM TblPupilManagementPupils INNER JOIN
TblSchoolManagementForms ON TblPupilManagementPupils.txtForm = TblSchoolManagementForms.txtForm INNER JOIN
TblStaff ON TblSchoolManagementForms.txtAsstFormTutor = TblStaff.User_Code INNER JOIN
TblSchoolManagementYears ON TblPupilManagementPupils.intNCYear = TblSchoolManagementYears.intNCYear AND
TblSchoolManagementForms.intNCYear = TblSchoolManagementYears.intNCYear
WHERE TblSchoolManagementYears.intNCYear > 6
This is working beautifully, but I need to add in some additional columns that also link back to TblStaff.User_Code My design currently looks like this, the highlighted fields are the ones I need to link in my query:
What I need to do is add 2 additional columns, 1 for HoYEmail and one for AsstHoYEmail using txtYearTutor linked by User_Code as TblStaff.SchoolEmailAddress AS HoYEmail and the AsstYearTutor linked by User_Code as TblStaff.SchoolEmailAddress AS AsstHoYEmail all grouped by TblPupilManagementPupils.txtSchoolID
Producing something like this:
Any tips gratefully received.
After a bit of research, I ended up completely re-writing this query to get it to work using correlation names for the staff table.
SELECT TblSchoolManagementForms.txtForm, TblPupilManagementPupils.txtSchoolID, TblPupilManagementPupils.txtSurname, TblPupilManagementPupils.txtForename,
TblStaff.SchoolEmailAddress AS TutorEmail, TblStaff_1.SchoolEmailAddress AS HoYEmail, TblStaff_2.SchoolEmailAddress AS AsstHoYEmail
FROM TblStaff INNER JOIN
TblPupilManagementPupils INNER JOIN
TblSchoolManagementForms ON TblPupilManagementPupils.txtForm = TblSchoolManagementForms.txtForm ON
TblStaff.User_Code = TblSchoolManagementForms.txtFormTutor INNER JOIN
TblSchoolManagementYears ON TblPupilManagementPupils.intNCYear = TblSchoolManagementYears.intNCYear INNER JOIN
TblStaff AS TblStaff_1 ON TblSchoolManagementYears.txtYearTutor = TblStaff_1.User_Code INNER JOIN
TblStaff AS TblStaff_2 ON TblSchoolManagementYears.txtAsstYearTutor = TblStaff_2.User_Code
WHERE (TblSchoolManagementYears.intNCYear > 6) AND (TblPupilManagementPupils.intSystemStatus = 1)
UNION
SELECT TblSchoolManagementForms.txtForm, TblPupilManagementPupils.txtSchoolID, TblPupilManagementPupils.txtSurname, TblPupilManagementPupils.txtForename,
TblStaff.SchoolEmailAddress AS TutorEmail, TblStaff_1.SchoolEmailAddress AS HoYEmail, TblStaff_2.SchoolEmailAddress AS AsstHoYEmail
FROM TblStaff INNER JOIN
TblPupilManagementPupils INNER JOIN
TblSchoolManagementForms ON TblPupilManagementPupils.txtForm = TblSchoolManagementForms.txtForm ON
TblStaff.User_Code = TblSchoolManagementForms.txtAsstFormTutor INNER JOIN
TblSchoolManagementYears ON TblPupilManagementPupils.intNCYear = TblSchoolManagementYears.intNCYear INNER JOIN
TblStaff AS TblStaff_1 ON TblSchoolManagementYears.txtYearTutor = TblStaff_1.User_Code INNER JOIN
TblStaff AS TblStaff_2 ON TblSchoolManagementYears.txtAsstYearTutor = TblStaff_2.User_Code
WHERE (TblSchoolManagementYears.intNCYear > 6) AND (TblPupilManagementPupils.intSystemStatus = 1)

PostgreSQL - weird query planner behavior

Assume I have a query like this:
SELECT *
FROM clients c
INNER JOIN clients_balances cb ON cb.id_clients = c.id
LEFT JOIN clients com ON com.id = c.id_companies
LEFT JOIN clients com_real ON com_real.id = c.id_companies_real
LEFT JOIN rate_tables rt_orig ON rt_orig.id = c.orig_rate_table
LEFT JOIN rate_tables rt_term ON rt_term.id = c.term_rate_table
LEFT JOIN payment_terms pt ON pt.id = c.id_payment_terms
LEFT JOIN paygw_clients_profiles cpgw ON (cpgw.id_clients = c.id AND cpgw.id_companies = c.id_companies_real)
WHERE
EXISTS (SELECT * FROM accounts WHERE (name LIKE 'x' OR accname LIKE 'x' OR ani LIKE 'x') AND id_clients = c.id)
AND c."type" = '0'
AND c."id" > 0
ORDER BY c."name";
This query takes around 35 seconds to run when used in the production environment ("clients" has about 1 million records). However, if I take out ANY join - the query will take only about 300 ms to execute.
I've played around with the query planner settings, but to no avail.
Here are a few explain analyze outputs:
http://explain.depesz.com/s/hzy (slow - 48049.574 ms)
http://explain.depesz.com/s/FWCd (fast - 286.234 ms, rate_tables JOIN removed)
http://explain.depesz.com/s/MyRf (fast - 539.733 ms, paygw_clients_profiles JOIN removed)
It looks like in the fast case the planner starts from the EXISTS statement and has to perform join for only two rows in total. However, in the slow case it will first join all the tables and then filter by EXISTS.
What I need to do is to make this query run in a reasonable time with all seven join in place.
Postgres version is 9.3.10 on CentOS 6.3.
Thanks.
UPDATE
Rewriting the query like this:
SELECT *
FROM clients c
INNER JOIN clients_balances cb ON cb.id_clients = c.id
INNER JOIN accounts a ON a.id_clients = c.id AND (a.name = 'x' OR a.accname = 'x' OR a.ani = 'x')
LEFT JOIN clients com ON com.id = c.id_companies
LEFT JOIN clients com_real ON com_real.id = c.id_companies_real
LEFT JOIN rate_tables rt_orig ON rt_orig.id = c.orig_rate_table
LEFT JOIN rate_tables rt_term ON rt_term.id = c.term_rate_table
LEFT JOIN payment_terms pt ON pt.id = c.id_payment_terms
LEFT JOIN paygw_clients_profiles cpgw ON (cpgw.id_clients = c.id AND cpgw.id_companies = c.id_companies_real)
WHERE
c."type" = '0' AND c.id > 0
ORDER BY c."name";
makes it run fast, however, this is not acceptable, as account filtration parameters are optional, and I still need the result if there are no matches in that table. Using "LEFT JOIN accounts" instead of "INNER JOIN accounts" kills the performance again.
As suggested by Tome Lane, I've changed the following two parameters: join_collapse_limit and from_collapse_limit to 10 instead of the default 8, and this solved the issue.

Moodle - Report to determine how long it takes before a student's assignment is graded

I am using Moodle LMS and I need to run a report to find out how long it takes between the time a student submits any sort of human-gradable assignment and the time it is graded so that we can monitor our staff's efficiency. Right now I am going for a query similar to the one below. I still need to add in the actual assignments but I am not sure which items in moodle could possibly fit into the "submitted but waiting to be graded" category.
Which tables am I missing for this query and is there anything I am obviously doing wrong here?
SELECT c.shortname AS course_name
, cm.id AS cmid
, cm.course AS courseid
, md.name AS modname
, gi.itemname AS itemname
, u.firstname AS student_first
, u.lastname AS student_last
, f.firstname AS grader_first
, f.lastname AS grader_last
, NULLIF(GREATEST(IFNULL(g.overridden, 0)
,IFNULL(g.timecreated, 0)
,IFNULL(g.timemodified, 0))
,0) AS graded_unixtimestamp
FROM mdl_user u
JOIN mdl_user_enrolments ue
ON (u.id = ue.userid)
JOIN mdl_enrol e
ON (e.id = ue.enrolid)
JOIN mdl_course c
ON (c.id = e.courseid)
JOIN mdl_course_modules cm
ON (c.id = cm.course)
JOIN mdl_modules md
ON (md.id = cm.module)
JOIN mdl_grade_items gi
ON (
gi.itemmodule = md.name
AND gi.iteminstance = cm.instance
AND gi.courseid = cm.course)
LEFT
JOIN mdl_grade_grades g
ON (gi.id = g.itemid AND g.userid = u.id)
LEFT
JOIN mdl_user f
ON (g.usermodified = u.id);

Twist on Master-Detail

I have a rather difficult requirement, for me at least.
I have a Master table with multiple Detail tables. All these tables return just null or one record with the exception of one table that would return null or more [than one].
Joining and Left joining the tables preceding the Multi-detail table (didn't know what else to call it) is easy, straight forward stuff, but how would I retrieve multiple records? Have I finally found a reason to use a right join?
Without posting too many table scripts, here's the SP that pulls all the data:
SELECT
Asclepius.GetFullPatientName(P.FirstNameAr,P.SecondNameAr, P.ThirdNameAr, P.FourthNameAr) AS PatientName,
PA.AdmittanceID,
PInvoice.InvoiceSerialNo,
PInvoice.InvoiceTotal,
PInvoice.InvoiceID,
--EmergencyWard
EW.Wages, EW.Bandages,EW.ECG, EW.Equipment,EW.FirstAid,EW.MedicalSupplies,EW.Medicine,EW.Other,
--Maternity
M.Wage,M.Graphing,M.Equipment,M.Medicine,M.MedicalSupplies,M.MaternityOperatingRoom,M.MaternityOpeartionFees,M.Other,
--RoomService
RS.Stay,RS.Cohabitant,RS.Medicine,RS.MedicalSupplies,RS.Oxygen,RS.MonitorUsage,RS.ECG,RS.Telephone,RS.Cafeteria,RS.Other,
--Nursery
N.Bed,N.Incubator,N.LightIncubator,N.Medicine,N.MedicalSupplies,N.Equipment,N.Other,
--Lab And Radiology
LR.LabTests,LR.BloodBank,LR.Radiology,LR.Ultrasound,LR.CTScan,LR.Endoscopy,LR.MRI,LR.Catheter,LR.Other,
--Surgery
S.ORFees,S.MedicalSupplies,S.Medicine,S.Equipment,S.SurgeryShifts,S.Other,
--Doctors
D.DoctorID,D.Wage,CD.DoctorNameAr
FROM
--PatientInvoice
Accounting.PatientInvoice AS PInvoice
INNER JOIN EMR.Patient AS P ON PInvoice.PatientID = P.PatientID
INNER JOIN EMR.PatientAdmittance AS PA ON P.PatientID = PA.PatientID AND PInvoice.AdmittanceID = PA.AdmittanceID
--EmergencyWard
LEFT JOIN Accounting.PatientInvoice_EmergencyWard AS EW ON PInvoice.PatientID = EW.PatientID AND PInvoice.AdmittanceID = EW.AdmittanceID AND PInvoice.InvoiceSerialNo = EW.InvoiceSerialNo
--Maternity
LEFT JOIN Accounting.PatientInvoice_Maternity AS M ON PInvoice.PatientID = M.PatientID AND PInvoice.AdmittanceID = M.AdmittanceID AND PInvoice.InvoiceSerialNo = M.InvoiceSerialNo
--RoomService
LEFT JOIN Accounting.PatientInvoice_RoomService AS RS ON PInvoice.PatientID = RS.PatientID AND PInvoice.AdmittanceID = RS.AdmittanceID AND PInvoice.InvoiceSerialNo = RS.InvoiceSerialNo
--Nursery
LEFT JOIN Accounting.PatientInvoice_Nursery AS N ON PInvoice.PatientID = N.PatientID AND PInvoice.AdmittanceID = N.AdmittanceID AND PInvoice.InvoiceSerialNo = N.InvoiceSerialNo
--Lab And Radiology
LEFT JOIN Accounting.PatientInvoice_LabAndRadiology AS LR ON PInvoice.PatientID = LR.PatientID AND PInvoice.AdmittanceID = LR.AdmittanceID AND PInvoice.InvoiceSerialNo = LR.InvoiceSerialNo
--Surgery
LEFT JOIN Accounting.PatientInvoice_Surgery AS S ON PInvoice.PatientID = S.PatientID AND PInvoice.AdmittanceID = S.AdmittanceID AND PInvoice.InvoiceSerialNo = S.InvoiceSerialNo
--Doctors
LEFT JOIN Accounting.PatientInvoice_Doctors AS D ON PInvoice.PatientID = D.PatientID AND PInvoice.AdmittanceID = D.AdmittanceID AND PInvoice.InvoiceSerialNo = D.InvoiceSerialNo
LEFT JOIN EMR.ConsultantDoctors AS CD ON D.DoctorID = CD.DoctorID
The culprit being the last two join under the comment --Doctor. All the previous tables return just 0 or 1 record, that last table could provide 0 or n.
Or do Simply use MARS and retrieve that one independently?
TIA
Do you know for a fact that it is failing to return n rows as that is what a join is supposed to do? If the columns types match up you can use UNION.