如何重构,(缩短)此查询

时间:2014-03-07 13:06:38

标签: mysql sql

我有一个包含表格的数据库:申请人(或某个职位的候选人),申请人(申请某个职位的候选人),考试,selected_test(任何申请都有一套定义的考试)和test_result。

当我需要显示哪个申请人为任何申请和测试得分时,我会使用此查询:

SELECT applicant.first_name, applicant.last_name, application.job, test.name, test_result.score 
FROM applicant 
INNER JOIN application ON application.applicant_id=applicant.id
INNER JOIN selected_test ON application.id=selected_test.application_id
INNER JOIN test ON selected_test.test_id=test.id
INNER JOIN test_result ON selected_test.test_id=test_result.test_id AND applicant.id=test_result.applicant_id

我需要完成的是按照某种测试类型(test.name)和test.score

进行排序

这就是我的意思:

SELECT a.first_name, a.last_name, app.job, iq.score AS iqScore, math.score AS mathScore, personality.score AS personalityScore, logic.score AS logicScore
FROM applicant a 
INNER JOIN application app ON a.id=app.applicant_id 
LEFT JOIN 

(SELECT app.id AS appId, tr.score 
FROM applicant a 
    INNER JOIN application app ON app.applicant_id=a.id
    INNER JOIN selected_test st ON app.id=st.application_id
    INNER JOIN test t ON st.test_id=t.id AND t.name='iq'
    INNER JOIN test_result tr ON st.test_id=tr.test_id AND a.id=tr.applicant_id) AS iq ON app.id=iq.appId

LEFT JOIN
(SELECT app.id AS appId, tr.score 
FROM applicant a 
    INNER JOIN application app ON app.applicant_id=a.id
    INNER JOIN selected_test st ON app.id=st.application_id
    INNER JOIN test t ON st.test_id=t.id AND t.name='math'
    INNER JOIN test_result tr ON st.test_id=tr.test_id AND a.id=tr.applicant_id) AS math ON app.id=math.appId

LEFT JOIN
(SELECT app.id AS appId, tr.score 
FROM applicant a 
    INNER JOIN application app ON app.applicant_id=a.id
    INNER JOIN selected_test st ON app.id=st.application_id
    INNER JOIN test t ON st.test_id=t.id AND t.name='personality'
    INNER JOIN test_result tr ON st.test_id=tr.test_id AND a.id=tr.applicant_id) AS personality ON app.id=personality.appId

LEFT JOIN
(SELECT app.id AS appId, tr.score 
FROM applicant a 
    INNER JOIN application app ON app.applicant_id=a.id
    INNER JOIN selected_test st ON app.id=st.application_id
    INNER JOIN test t ON st.test_id=t.id AND t.name='logic'
    INNER JOIN test_result tr ON st.test_id=tr.test_id AND a.id=tr.applicant_id) AS logic ON app.id=logic.appId

ORDER BY mathScore DESC, iqScore DESC, logicScore DESC

查询返回一组应用程序,显示申请人数据,工作,测试名称和分数。

例如,如果我希望候选应用程序具有更高的“数学”分数,然后是“IQ”中的最高分数,然后是“逻辑”中的最高分数,则“ORDER BY”子句如上所示。 查询工作正确,但问题是在实际情况下它处理大型数据集,我需要一种方法来缩短/重构此查询。

它可以使用的示例数据库在这里:

CREATE TABLE IF NOT EXISTS `applicant` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=8 ;

--
-- Dumping data for table `applicant`
--

INSERT INTO `applicant` (`id`, `first_name`, `last_name`) VALUES
(2, 'Jack', 'Redburn'),
(4, 'Barry', 'Leon'),
(6, 'Elisabeth', 'Logan'),
(7, 'Jane', 'Doe');

-- --------------------------------------------------------

--
-- Table structure for table `application`
--

CREATE TABLE IF NOT EXISTS `application` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`applicant_id` int(11) NOT NULL,
`job` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;

--
-- Dumping data for table `application`
--

INSERT INTO `application` (`id`, `applicant_id`, `job`) VALUES
(2, 2, 'Salesman'),
(4, 4, 'Policeman'),
(6, 6, 'Journalist'),
(8, 6, 'Hostess'),
(9, 7, 'Journalist');

-- --------------------------------------------------------

--
-- Table structure for table `selected_test`
--

CREATE TABLE IF NOT EXISTS `selected_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`application_id` int(11) NOT NULL,
`test_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=24 ;

--
-- Dumping data for table `selected_test`
--

INSERT INTO `selected_test` (`id`, `application_id`, `test_id`) VALUES
(1, 1, 1),
(2, 1, 2),
(3, 1, 3),
(5, 2, 1),
(6, 2, 2),
(7, 2, 3),
(8, 2, 4),
(9, 3, 4),
(10, 3, 2),
(11, 4, 1),
(12, 4, 2),
(13, 4, 3),
(14, 4, 4),
(15, 5, 2),
(16, 5, 3),
(17, 6, 1),
(18, 6, 4),
(19, 7, 3),
(20, 7, 2),
(21, 7, 1),
(22, 8, 2),
(23, 8, 3);

-- --------------------------------------------------------

--
-- Table structure for table `test`
--

CREATE TABLE IF NOT EXISTS `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;

--
-- Dumping data for table `test`
--

INSERT INTO `test` (`id`, `name`) VALUES
(1, 'math'),
(2, 'logic'),
(3, 'iq'),
(4, 'personality');

-- --------------------------------------------------------

--
-- Table structure for table `test_result`
--

CREATE TABLE IF NOT EXISTS `test_result` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`applicant_id` int(11) NOT NULL,
`test_id` int(11) NOT NULL,
`score` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=24 ;

--
-- Dumping data for table `test_result`
--

INSERT INTO `test_result` (`id`, `applicant_id`, `test_id`, `score`) VALUES
(2, 2, 1, 6),
(3, 4, 1, 7),
(6, 6, 1, 3),
(7, 7, 1, 8),
(9, 2, 2, 15),
(11, 4, 2, 12),
(13, 6, 2, 11),
(14, 7, 2, 9),
(15, 7, 3, 105),
(16, 6, 3, 112),
(18, 4, 3, 108),
(20, 2, 3, 117),
(22, 4, 4, 70);

以下是结果: 第一个查询只是为了向您展示数据的相关性:

First query is just to show you how data is related

大型查询会水平显示分数数据,因此可以按测试名称和分数进行排序:

The large query, shows data horizontally so it is possible to sort by test name and score

2 个答案:

答案 0 :(得分:1)

警告我不知道mysql

Google搜索 mysql pivot 会提供此结果http://en.wikibooks.org/wiki/MySQL/Pivot_table

因此,如果我们使用test.id作为种子编号(来自Google搜索的示例中的考试)应用相同的逻辑,我们就会得到:

SQLFIDDLE

select first_name, last_name, job, 
  sum(score*(1-abs(sign(testid-1)))) as math,
  sum(score*(1-abs(sign(testid-2)))) as logic,
  sum(score*(1-abs(sign(testid-3)))) as iq,
  sum(score*(1-abs(sign(testid-4)))) as personality
from 
(
    SELECT applicant.first_name, applicant.last_name, application.job, test.name, test_result.score, test.id as testid
    FROM applicant 
    INNER JOIN application ON application.applicant_id=applicant.id
    INNER JOIN selected_test ON application.id=selected_test.application_id
    INNER JOIN test ON selected_test.test_id=test.id
    INNER JOIN test_result ON selected_test.test_id=test_result.test_id AND applicant.id=test_result.applicant_id
 ) t
group by  first_name, last_name, job

现在您已经得到了简短的查询,您可以根据需要应用排序 - 您可以在case中使用order by语句根据需要动态更改订单...

答案 1 :(得分:-1)

我注意到你只定义了主键。索引其他字段时,您应该会看到明显的性能提升。至少索引以下内容:application.applicant_id,selected_test.application_id,selected_test.test_id,test_result.applicant_id,test_result.test_id,test_result.score。

你可能会惊讶于这为你加速了多少。事实上,mysql告诉我们这是提高性能的最佳方法:https://dev.mysql.com/doc/refman/5.5/en/optimization-indexes.html