meerkat2040
asked on
Optimizing MySQL query execution time with C# ASP.NET
I'm doing a website that hosts surveys. For some reason, the execution time of the insert/update to the mySQL database for a single survey response is nearly a half a second. This seems like an aweful long time for a small query to execute.
Below is the entire query of entering a single survey page response. I thought keeping a running total of the number of responses in the row that stores the answer would save a lot of time when it was time to generate reports. However this seems to double the execution time.
Even without the totals=totals+1 lines, the execution time is still around .265 seconds. That would be fine if only one person was taking a survey at a time, but my goal is to host lots of surveys.
Elapsed Time: 0 seconds, or 453.125 milliseconds per Survey save.
Last SQL Statement:
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,2,1,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=1;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,3,6,'This is sample text for testing purposes.',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=6;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,4,7,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=7;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,6,17,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=17;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,7,18,'This is sample text for testing purposes.',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=18;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,9,23,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=23;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,10,28,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=28;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,11,30,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=30;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,13,34,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=34;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,14,41,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=41;
INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) VALUES (1,15,47,'',1,'2008-06-24 18:04:12');
UPDATE answers SET totals=totals+1 WHERE aid=47;
UPDATE surveys SET lastactivity_date='2008-06 -24 18:04:12',totals=totals+1 WHERE sid=1;
This is the method that generates and executes the statement:
public void StoreResults(int iPid)
{
sSQL="";
string dateFormat = "yyyy-MM-dd HH:mm:ss";
string surveyDateTime = DateTime.Now.ToString(date Format);
string sSurveyID = iSurveyID.ToString();
string sPid = iPid.ToString();
for (int iQuestion = 0; iQuestion < myQuestions.Length; iQuestion++)
{
if (myQuestions[iQuestion].An swers != null)
{
for (int iAnswer = 0; iAnswer < myQuestions[iQuestion].Ans wers.Lengt h; iAnswer++)
{
if (myQuestions[iQuestion].An swers[iAns wer].Selec ted)
{
//insert results table row into database
sSQL += "INSERT INTO results(sid,qid,aid,abody, pid,date_t aken) ";
sSQL += "VALUES (" + sSurveyID + "," + myQuestions[iQuestion].Que stionID.To String();
sSQL += "," + myQuestions[iQuestion].Ans wers[iAnsw er].Answer ID.ToStrin g() + ",'";
sSQL += SQLFix(myQuestions[iQuesti on].Answer s[iAnswer] .Response. ToString() ) + "',";
sSQL += sPid + ",'" + surveyDateTime + "');";
sSQL += "UPDATE answers SET totals=totals+1 WHERE aid=";
sSQL += myQuestions[iQuestion].Ans wers[iAnsw er].Answer ID.ToStrin g() + ";";
}
}
}
}
//update survey lastactivity_date and totals in database
sSQL += "UPDATE surveys SET lastactivity_date='" + surveyDateTime;
sSQL += "',totals=totals+1 WHERE sid=" + iSurveyID.ToString() + ";";
dsPollsDatabase.UpdateComm and = sSQL;
dsPollsDatabase.Update();
}
Below is the entire query of entering a single survey page response. I thought keeping a running total of the number of responses in the row that stores the answer would save a lot of time when it was time to generate reports. However this seems to double the execution time.
Even without the totals=totals+1 lines, the execution time is still around .265 seconds. That would be fine if only one person was taking a survey at a time, but my goal is to host lots of surveys.
Elapsed Time: 0 seconds, or 453.125 milliseconds per Survey save.
Last SQL Statement:
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=1;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=6;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=7;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=17;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=18;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=23;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=28;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=30;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=34;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=41;
INSERT INTO results(sid,qid,aid,abody,
UPDATE answers SET totals=totals+1 WHERE aid=47;
UPDATE surveys SET lastactivity_date='2008-06
This is the method that generates and executes the statement:
public void StoreResults(int iPid)
{
sSQL="";
string dateFormat = "yyyy-MM-dd HH:mm:ss";
string surveyDateTime = DateTime.Now.ToString(date
string sSurveyID = iSurveyID.ToString();
string sPid = iPid.ToString();
for (int iQuestion = 0; iQuestion < myQuestions.Length; iQuestion++)
{
if (myQuestions[iQuestion].An
{
for (int iAnswer = 0; iAnswer < myQuestions[iQuestion].Ans
{
if (myQuestions[iQuestion].An
{
//insert results table row into database
sSQL += "INSERT INTO results(sid,qid,aid,abody,
sSQL += "VALUES (" + sSurveyID + "," + myQuestions[iQuestion].Que
sSQL += "," + myQuestions[iQuestion].Ans
sSQL += SQLFix(myQuestions[iQuesti
sSQL += sPid + ",'" + surveyDateTime + "');";
sSQL += "UPDATE answers SET totals=totals+1 WHERE aid=";
sSQL += myQuestions[iQuestion].Ans
}
}
}
}
//update survey lastactivity_date and totals in database
sSQL += "UPDATE surveys SET lastactivity_date='" + surveyDateTime;
sSQL += "',totals=totals+1 WHERE sid=" + iSurveyID.ToString() + ";";
dsPollsDatabase.UpdateComm
dsPollsDatabase.Update();
}
How often are reports generated? If insert is more frequent than report generation, actually you don't need to keep on adding the total as you can just compute the total when you are about to generate report. You can do that by using "select count(*)" based on certain "where" condition.
ASKER
Yeah. I may have to do that.
I have a logging application that keeps track of my daily web hits. Every day I get around 30,000 hits, but most are from search engines going through my site (each word in a dictionary has it's own web page, and there are 200,000 words). It takes a long time to generate the totals for a whole month, which charts how many unique ip addresses visit per day. Since that's my experience with reporting, I figured it would save a lot of time to keep a running tally in advance.
Even if I exclude those statements that set totals=totals+1 (which cuts the time in half), the rest of the inserts still take a quarter of a second. I certainly want to be able to support more than 4 users/second on my web app.
I have a logging application that keeps track of my daily web hits. Every day I get around 30,000 hits, but most are from search engines going through my site (each word in a dictionary has it's own web page, and there are 200,000 words). It takes a long time to generate the totals for a whole month, which charts how many unique ip addresses visit per day. Since that's my experience with reporting, I figured it would save a lot of time to keep a running tally in advance.
Even if I exclude those statements that set totals=totals+1 (which cuts the time in half), the rest of the inserts still take a quarter of a second. I certainly want to be able to support more than 4 users/second on my web app.
Any index in results table? What are the data type of those columns that you index? Index causes insert to be smaller, especially with bigger data type
ASKER
CREATE TABLE `answers` (
`aid` int(11) NOT NULL auto_increment,
`sid` int(11) NOT NULL default '0',
`qid` int(11) NOT NULL default '0',
`anumber` int(11) NOT NULL default '0',
`abody` text,
`atype` int(11) NOT NULL,
`width` smallint(6) NOT NULL default '0',
`height` smallint(6) NOT NULL default '0',
`totals` int(11) NOT NULL default '0',
PRIMARY KEY (`aid`),
KEY `questionid` (`qid`),
KEY `surveyid` (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2749 ;
CREATE TABLE `surveys` (
`sid` int(11) NOT NULL auto_increment,
`guid` varchar(16) NOT NULL COMMENT 'This is the survey unique identifier',
`title` varchar(128) NOT NULL,
`ownerid` int(11) NOT NULL,
`creation_date` datetime NOT NULL,
`modified_date` datetime NOT NULL,
`open_date` datetime default NULL,
`close_date` datetime default NULL,
`lastactivity_date` datetime default NULL,
`active` tinyint(1) NOT NULL default '1',
`shared` tinyint(1) NOT NULL default '0',
`questions` smallint(6) NOT NULL default '0',
`description` text,
`totals` int(11) NOT NULL default '0',
PRIMARY KEY (`sid`),
KEY `GUID` (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=28 ;
`aid` int(11) NOT NULL auto_increment,
`sid` int(11) NOT NULL default '0',
`qid` int(11) NOT NULL default '0',
`anumber` int(11) NOT NULL default '0',
`abody` text,
`atype` int(11) NOT NULL,
`width` smallint(6) NOT NULL default '0',
`height` smallint(6) NOT NULL default '0',
`totals` int(11) NOT NULL default '0',
PRIMARY KEY (`aid`),
KEY `questionid` (`qid`),
KEY `surveyid` (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2749 ;
CREATE TABLE `surveys` (
`sid` int(11) NOT NULL auto_increment,
`guid` varchar(16) NOT NULL COMMENT 'This is the survey unique identifier',
`title` varchar(128) NOT NULL,
`ownerid` int(11) NOT NULL,
`creation_date` datetime NOT NULL,
`modified_date` datetime NOT NULL,
`open_date` datetime default NULL,
`close_date` datetime default NULL,
`lastactivity_date` datetime default NULL,
`active` tinyint(1) NOT NULL default '1',
`shared` tinyint(1) NOT NULL default '0',
`questions` smallint(6) NOT NULL default '0',
`description` text,
`totals` int(11) NOT NULL default '0',
PRIMARY KEY (`sid`),
KEY `GUID` (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=28 ;
ASKER
CREATE TABLE `results` (
`rid` int(11) NOT NULL auto_increment,
`sid` int(11) NOT NULL default '0',
`qid` int(11) default NULL,
`aid` int(11) NOT NULL default '0',
`abody` text,
`pid` int(11) NOT NULL default '0',
`date_taken` datetime NOT NULL,
PRIMARY KEY (`rid`),
KEY `pid` (`pid`),
KEY `qid` (`qid`),
KEY `aid` (`aid`),
KEY `sid` (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=18947 ;
`rid` int(11) NOT NULL auto_increment,
`sid` int(11) NOT NULL default '0',
`qid` int(11) default NULL,
`aid` int(11) NOT NULL default '0',
`abody` text,
`pid` int(11) NOT NULL default '0',
`date_taken` datetime NOT NULL,
PRIMARY KEY (`rid`),
KEY `pid` (`pid`),
KEY `qid` (`qid`),
KEY `aid` (`aid`),
KEY `sid` (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=18947 ;
typo...
Index causes insert to be slower, especially with bigger data type
Index causes insert to be slower, especially with bigger data type
ASKER
pid is participant id. we want to be able to lookup responses by participant.
qid is question id. We need to look up answers based on questionid.
aid is answer id. We used to need that to look up total number of a given answer, but I'm not using it now since I've got the totals column.
sid is surveyid. We need to look up based on surveyid.
qid is question id. We need to look up answers based on questionid.
aid is answer id. We used to need that to look up total number of a given answer, but I'm not using it now since I've got the totals column.
sid is surveyid. We need to look up based on surveyid.
Well, the only thing i can think of is surveys.guid, as your survey table gets bigger, it sure will slow down writes to that table.
ASKER
We only write the guid to the surveys table once. That's just so when someone's taking a survey we are not puting the actual surveyid in the request URL.
ASKER
Well, after some further testing, it appears that it's a problem with my test machine.
On the godaddy server that's hosting the future website, it only takes around 17ms for each survey save. So I've got some work to do on my development machine.
On the godaddy server that's hosting the future website, it only takes around 17ms for each survey save. So I've got some work to do on my development machine.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.