开发者

Can this coldfusion9 SQL search query be made faster?

开发者 https://www.devze.com 2023-04-12 19:12 出处:网络
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 t

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


  1. Try CREATE INDEX cl_ordersummary_studentID ON cl_ordersummary(studentID)

  2. 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 :

  1. 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 )

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号