【发布时间】:2013-11-22 13:42:34
【问题描述】:
我有一张表格,其中包含 17 个问题和几个文本块,用于接受患者满意度调查(在底部创建声明)。当调查被输入数据库时,它会设置一个日期时间字段。我必须能够运行平均时间段(周/月/年)的报告。目前,我在每周切片中运行查询。
我的查询最多需要 16 秒才能运行,并在运行期间最大限度地利用 CPU。然后,我必须再查询 13 周才能获得全球平均值,然后我对每个选定的医生进行 14 更多次查询(可以在 0 到 22 次之间)。
我尝试过使用WHERE date >= 'date low' AND date <= 'date high'、WHERE date BETWEEN 'date low' AND 'date high',也尝试过CAST('date' as datetime),但无济于事。在分析查询时,它似乎将大部分时间花在statistics 上,而EXPLAIN 似乎在说它没有使用 datescanned 索引,但我不知道为什么。
目前大约有 1000 行,但是当有大约一半的行数时,查询运行良好。据此,我了解到我在创建表的方式或如何形成查询方面存在一个非常糟糕的问题。
注意:在 VMWare ESXi 5.1 上的 Debian 7.2 上运行,具有 4GB 内存和 1 个虚拟进程
创建表:
CREATE TABLE IF NOT EXISTS `survey` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`datescanned` datetime NOT NULL,
`physician_fk` int(5) NOT NULL,
`procedure` varchar(255) DEFAULT NULL,
`gender` varchar(12) NOT NULL,
`patientage` varchar(50) NOT NULL DEFAULT 'Not marked',
`question01_fk` int(1) NOT NULL,
`question02_fk` int(1) NOT NULL,
`question03_fk` int(1) NOT NULL,
`question04_fk` int(1) NOT NULL,
`question05_fk` int(1) NOT NULL,
`question06_fk` int(1) NOT NULL,
`question07_fk` int(1) NOT NULL,
`question08_fk` int(1) NOT NULL,
`question09_fk` int(1) NOT NULL,
`question10_fk` int(1) NOT NULL,
`question11_fk` int(1) NOT NULL,
`question12_fk` int(1) NOT NULL,
`question13_fk` int(1) NOT NULL,
`question14_fk` int(1) NOT NULL,
`question15_fk` int(1) NOT NULL,
`question16_fk` int(1) NOT NULL,
`question17_fk` int(1) NOT NULL,
`notes` text,
`email` varchar(256) DEFAULT NULL,
`name` varchar(256) DEFAULT NULL,
`qanotes` text,
`referredby` varchar(255) DEFAULT NULL,
`edited` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`editedby_fk` int(5) NOT NULL DEFAULT '1',
`viewed` tinyint(1) NOT NULL DEFAULT '0',
`handled` int(1) NOT NULL DEFAULT '0',
`archived` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `fk_physicianid` (`physician_fk`),
KEY `fk_question01id` (`question01_fk`),
KEY `fk_question02id` (`question02_fk`),
KEY `fk_question03id` (`question03_fk`),
KEY `fk_question04id` (`question04_fk`),
KEY `fk_question05id` (`question05_fk`),
KEY `fk_question06id` (`question06_fk`),
KEY `fk_question07id` (`question07_fk`),
KEY `fk_question08id` (`question08_fk`),
KEY `fk_question09id` (`question09_fk`),
KEY `fk_question10id` (`question10_fk`),
KEY `fk_question11id` (`question11_fk`),
KEY `fk_question12id` (`question12_fk`),
KEY `fk_question13id` (`question13_fk`),
KEY `fk_question14id` (`question14_fk`),
KEY `fk_question15id` (`question15_fk`),
KEY `fk_question16id` (`question16_fk`),
KEY `fk_question17id` (`question17_fk`),
KEY `fk_editedbyid` (`editedby_fk`),
KEY `handled` (`handled`),
KEY `scanned_index` (`datescanned`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=991 ;
注意:所有问题都指向同一张表
选择查询
SELECT q01.worth, q02.worth, q03.worth, q04.worth, q05.worth, q06.worth, q07.worth,
q08.worth, q09.worth, q10.worth, q11.worth, q12.worth, q13.worth, q14.worth, q15.worth,
q16.worth, q17.worth
FROM survey s, answer q01, answer q02, answer q03, answer q04,
answer q05, answer q06, answer q07, answer q08, answer q09, answer q10, answer q11,
answer q12, answer q13, answer q14, answer q15, answer q16, answer q17
WHERE s.archived !=1 AND q01.id = s.question01_fk AND q02.id = s.question02_fk
AND q03.id = s.question03_fk AND q04.id = s.question04_fk AND q05.id = s.question05_fk
AND q06.id = s.question06_fk AND q07.id = s.question07_fk AND q08.id = s.question08_fk
AND q09.id = s.question09_fk AND q10.id = s.question10_fk AND q11.id = s.question11_fk
AND q12.id = s.question12_fk AND q13.id = s.question13_fk AND q14.id = s.question14_fk
AND q15.id = s.question15_fk AND q16.id = s.question16_fk AND q17.id = s.question17_fk
AND s.datescanned >= '2013-11-18 00:00:00' AND s.datescanned <= '2013-11-25 23:59:59';
注意:对于 question_fk,我尝试过使用和不使用 INNER JOIN
编辑:我注意到我需要重新考虑我的结构。我会努力解决这个问题,如果可以修复它,我会更新或关闭这篇文章。感谢到目前为止发表评论的人。
编辑 2:这是一个结构问题。拆分问题并映射到它们将完整的报告从 3 分钟以上缩短到 20 秒以下。感谢所有提供指导的人。
【问题讨论】:
-
1.规范化您的数据。数据库表不是电子表格。
-
调查和问题之间存在隐藏的多对多关系。这意味着您应该添加一个额外的表来处理这些关系
-
@MostyMostacho 那么,某种形式的 id、survey_id、question_01、...、question_17?
-
一个表
surveys,主键id,一个表questions,外键survey_id。每次你有foo1、foo2、fooN都表明你需要两个表,其中foo表有一个外键。 -
@deceze 谢谢你的解释,我现在就去做。