The Situation:
I have a database of 6000 students on a live site, and on a test site, there is a database of 400 test-students. Searching the students using the search function (below) works fine on the test site, but on the live site the search function takes 1-2 full minutes to return (Even needed to increase the script timeout by doing RequestTimeout=180). Both sites use the same search function (below).The Question:
My question is do any of you have any tips on how to make this search any faster? It's just so slow.The Search Function:
<cffunction nam开发者_StackOverflow社区e="getStudentsByKeyword" access="public" output="no" returntype="struct">
<cfargument name="keyword" type="string" required="yes">
<cfargument name="pageNum" type="numeric" default="1">
<cfargument name="startIndex" type="numeric" default="1">
<cfargument name="numItemsPerPage" type="numeric" default="20">
<cfset var resultStruct = StructNew()>
<cfset resultStruct.numAllItems=0>
<cfset resultStruct.numDisplayedItems=0>
<cfset resultStruct.courses=QueryNew("studentID")>
<cfif Arguments.pageNum GT 1>
<cfset Arguments.startIndex=(Arguments.pageNum - 1) * Arguments.numItemsPerPage + 1>
</cfif>
<cfquery name="qNumStudents" datasource="#this.datasource#">
SELECT DISTINCT COUNT(cl_student.studentID) AS numItems
FROM cl_student LEFT JOIN cl_ordersummary ON cl_student.studentID=cl_ordersummary.studentID
WHERE cl_student.email LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%"> OR
cl_ordersummary.contactFirstName LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%"> OR
cl_ordersummary.contactLastName LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%">
</cfquery>
<cfset resultStruct.numAllItems = qNumStudents.numItems>
<cfquery name="qStudents" datasource="#this.datasource#">
SELECT DISTINCT cl_student.studentID, cl_student.email, cl_student.password, cl_student.studentType,
cl_ordersummary.contactFirstName, cl_ordersummary.contactLastName
FROM cl_student LEFT JOIN cl_ordersummary ON cl_student.studentID=cl_ordersummary.studentID
WHERE cl_student.email LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%"> OR
cl_ordersummary.contactFirstName LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%"> OR
cl_ordersummary.contactLastName LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%">
ORDER BY cl_student.email, cl_ordersummary.contactFirstName, cl_ordersummary.contactLastName
LIMIT #Arguments.startIndex-1#, #Arguments.numItemsPerPage#
</cfquery>
<cfset resultStruct.numDisplayedItems=qStudents.recordcount>
<cfset resultStruct.students = qStudents>
<cfreturn resultStruct>
</cffunction>
Table Descriptions:
(Simple representations of each table used in search)Table cl_student
================
studentID, email, password, studentType, sendReminderEmail, firstName, middleName, lastName, address, city, state, zip, daytimePhone, dateCreated, dateLastModified
----------------
studentID INT UNSIGNED(10) (PRIMARY) (AI)
email VARCHAR(100)
password VARCHAR(20)
studentType ENUM('A','B','C','D','F')
sendRemiderEmail TINYINT(4)
firstName VARCHAR(30)
middleName VARCHAR(30)
lastName VARCHAR(30)
address VARCHAR(100)
city VARCHAR(30)
state VARCHAR(30)
zip VARCHAR(10)
daytimePhone VARCHAR(20)
dateCreated DATETIME
dateLastModified DATETIME
Table cl_ordersummary
=====================
orderID, studentID, orderDate, status, donationAmount, total, contactFirstName, contactLastName, contactAddress1, contactAddress2, contactCity, contactState, contactZIP, daytimePhone, cellPhone, billingFirstName, billingLastName, billingAddress1, billingAddress2, billingCity, billingState, billingZIP, payWithCash, authCode, remark, dateLastModified
---------------------
orderID VARCHAR(20) (PRIMARY)
studentID INT(11)
orderDate DATETIME
status CHAR(1)
donationAmount FLOAT
total FLOAT
contactFirstName VARCHAR(50)
contactLastName VARCHAR(50)
contactAddress1 VARCHAR(100)
contactAddress2 VARCHAR(100)
contactCity VARCHAR(50)
contactState VARCHAR(50)
contactZIP VARCHAR(10)
daytimePhone VARCHAR(30)
cellPhone VARCHAR(30)
billingFirstName VARCHAR(50)
billingLastName VARCHAR(50)
billingAddress1 VARCHAR(100)
billingAddress2 VARCHAR(100)
billingCity VARCHAR(50)
billingState VARCHAR(50)
billingZIP VARCHAR(10)
payWithCash TINYINT(4)
authCode VARCHAR(20)
remark TEXT
dateLastModified DATETIME
Is it expected and wanted that you'll have multiple contact names in the recordset for each student? That setup, where you have contact names in the order table, as opposed to the student table looks to be creating the necessity for a query that is less than optimal. I understand that there may be good reason for that, of course, but worth checking. So, do you want/expect this?
studentId|email|password|studentType|contactFirstName|contactLastName
999 b@b.b|p455w0rd|slacker |billy |bob
999 b@b.b|p455w0rd|slacker |bill |bob
...
If so, not sure how the second query could be any faster. However, the first query might benefit from changing to the following that will always only return a single row and not require a left join
or a select distinct
:
SELECT count(s.*) as nAllResults
FROM cl_student s
WHERE s.email LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%">
OR EXISTS (
SELECT 1
FROM cl_ordersummary o
WHERE o.studentId = s.studentId
AND ( o.contactFirstName LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%">
OR o.contactLastName LIKE <cfqueryparam cfsqltype="cf_sql_char" value="%#Arguments.keyword#%"> )
)
Another thing to look at is indexes. The indexes won't be used in your LIKE
clauses, but will be on your JOINS
and your ORDER BY
clause. Make sure you have appropriate indexes on the tables and that your Pimary and Foreign Keys are defined.
Can this coldfusion9 SQL search query be made faster?
Yes. You want to configure indexes for affected columns.
Coldfusion does not execute your query. It's passed to your database server for execution and the result set is then passed back.
You can take your query and use a query analyzer to generate an execution plan to see where your query is taking the most time. You most likely need to add some indexes on the columns being used in your WHERE criteria. I believe in MySQL you can use EXPLAIN to show you where indexes are missing http://dev.mysql.com/doc/refman/5.5/en/using-explain.html
Try CREATE INDEX cl_ordersummary_studentID ON cl_ordersummary(studentID)
Other indexes can help too but you have to to use prefix search for this optimization, SQL databases usually convert prefix search to index range scan. For example you can index email and use prefix as parameter :
- Full text search index is the best option to optimize, see docs for your RDBMS capabilities, most databases support full text search it via special functions or syntax. You will find other useful features in full text search for example to sort by best match. You won't need option 2 if you change your queries to use full text search.
MySQL http://dev.mysql.com/doc/refman/5.0/en/fulltext-natural-language.html (All major databases have similar search capabilities )
精彩评论