Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Медленный запрос, что можно сделать? / 9 сообщений из 9, страница 1 из 1
17.08.2014, 14:03:44
    #38721911
Медленный запрос, что можно сделать?
Пишу программу для зарплаты, многопользовательская. Самоучка, программирование как хобби, изучаю MySQL. В главном окне выдаётся вся информация о сотрудниках:



При переключении текущего месяца вся информация обновляется таким запросом:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
INSERT INTO accounts (Employee, Month, Year, BeforeAccruals, BeforePenalties, BeforePayments, Accruals, Penalties, Payments) 
SELECT e.idx,8,2014,
(SELECT SUM(`Sum`) FROM accruals WHERE Employee = e.idx AND ((Year = 2014 AND Month < 8) OR (Year < 2014))), 
(SELECT SUM(`Sum`) FROM penalties WHERE Employee = e.idx AND  ((Year = 2014 AND Month < 8) OR (Year < 2014))), 
(SELECT SUM(`Sum`) FROM payments WHERE Employee = e.idx AND  ((Year = 2014 AND Month < 8) OR (Year < 2014))), 
(SELECT SUM(`Sum`) FROM accruals WHERE Employee = e.idx AND (Year = 2014 AND Month = 8)), 
(SELECT SUM(`Sum`) FROM penalties WHERE Employee = e.idx AND (Year = 2014 AND Month = 8)), 
(SELECT SUM(`Sum`) FROM payments WHERE Employee = e.idx AND (Year = 2014 AND Month = 8)) 
FROM employees e 
WHERE e.idx=45 
ON DUPLICATE KEY UPDATE 
BeforeAccruals=(SELECT SUM(`Sum`) FROM accruals WHERE Employee = accounts.Employee AND ((Year = 2014 AND Month < 8) OR (Year < 2014))), 
BeforePenalties=(SELECT SUM(`Sum`) FROM penalties WHERE Employee = accounts.Employee AND  ((Year = 2014 AND Month < 8) OR (Year < 2014))), 
BeforePayments=(SELECT SUM(`Sum`) FROM payments WHERE Employee = accounts.Employee AND  ((Year = 2014 AND Month < 8) OR (Year < 2014))), 
Accruals=(SELECT SUM(`Sum`) FROM accruals WHERE Employee = accounts.Employee AND (Year = 2014 AND Month = 8)), 
Penalties=(SELECT SUM(`Sum`) FROM penalties WHERE Employee = accounts.Employee AND (Year = 2014 AND Month = 8)), 
Payments=(SELECT SUM(`Sum`) FROM payments WHERE Employee = accounts.Employee AND (Year = 2014 AND Month = 8));



Запрос очень медленный. 15 секунд выполняется. Пока меня основная идея - запускать этот запрос в фоне, после выполнения обновить втихую информацию на экране. Всё-таки без полного обновления всех сотрудников за текущий месяц возможно будут выводится неправильные общие суммы начислений и пр., т.к. параллельно предыдущий месяц может редактироваться другим бухгалтером.

Структура таблиц:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
CREATE TABLE IF NOT EXISTS `accounts` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Employee` int(11) NOT NULL,
  `Month` int(11) NOT NULL,
  `Year` int(11) NOT NULL,
  `BeforeAccruals` decimal(8,2) DEFAULT NULL,
  `BeforePenalties` decimal(8,2) DEFAULT NULL,
  `BeforePayments` decimal(8,2) DEFAULT NULL,
  `Accruals` decimal(8,2) DEFAULT NULL,
  `Penalties` decimal(8,2) DEFAULT NULL,
  `Payments` decimal(8,2) DEFAULT NULL,
  PRIMARY KEY (`idx`),
  UNIQUE KEY `UNIQ_DATES` (`Employee`,`Month`,`Year`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=10608 ;

CREATE TABLE IF NOT EXISTS `accruals` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Employee` int(11) NOT NULL,
  `Month` int(11) NOT NULL,
  `Year` int(11) NOT NULL,
  `Type` int(11) NOT NULL,
  `Amount` decimal(8,2) NOT NULL,
  `Price` decimal(8,2) NOT NULL,
  `Sum` decimal(8,2) NOT NULL,
  `Company` int(11) DEFAULT NULL,
  PRIMARY KEY (`idx`),
  KEY `Employee` (`Employee`),
  KEY `Month` (`Month`),
  KEY `Year` (`Year`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3609 ;

CREATE TABLE IF NOT EXISTS `accruals_types` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Name` char(100) NOT NULL,
  PRIMARY KEY (`idx`),
  UNIQUE KEY `Name` (`Name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=31 ;

CREATE TABLE IF NOT EXISTS `companies` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Name` char(100) NOT NULL,
  `Price1H_lic` decimal(8,2) NOT NULL,
  `Price1H_nonlic` decimal(8,2) NOT NULL,
  `Firm` int(11) NOT NULL DEFAULT '1',
  PRIMARY KEY (`idx`),
  UNIQUE KEY `Name` (`Name`),
  KEY `Firm` (`Firm`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=103 ;

CREATE TABLE IF NOT EXISTS `employees` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Name` char(100) NOT NULL,
  `Firm` int(11) NOT NULL DEFAULT '1',
  `Title` int(11) NOT NULL,
  `License` tinyint(1) NOT NULL DEFAULT '0',
  `YOB` int(11) NOT NULL,
  `Children` int(11) NOT NULL DEFAULT '0',
  `InitSum` decimal(8,2) NOT NULL DEFAULT '0.00',
  `Active` tinyint(1) NOT NULL DEFAULT '1',
  `StartMonth` int(11) NOT NULL DEFAULT '1',
  `StartYear` int(11) NOT NULL DEFAULT '2013',
  `QuitMonth` int(11) NOT NULL DEFAULT '0',
  `QuitYear` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`idx`),
  UNIQUE KEY `Name` (`Name`),
  KEY `Title` (`Title`),
  KEY `StartMonth` (`StartMonth`),
  KEY `StartYear` (`StartYear`),
  KEY `QuitMonth` (`QuitMonth`),
  KEY `QuitYear` (`QuitYear`),
  KEY `Firm` (`Firm`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=703 ;

CREATE TABLE IF NOT EXISTS `firms` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Name` char(100) NOT NULL,
  PRIMARY KEY (`idx`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

CREATE TABLE IF NOT EXISTS `log` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `DateTime` datetime NOT NULL,
  `Firm` int(11) NOT NULL,
  `User` int(11) NOT NULL,
  `Action` varchar(200) NOT NULL,
  PRIMARY KEY (`idx`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=93 ;

CREATE TABLE IF NOT EXISTS `payments` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Day` int(11) NOT NULL DEFAULT '1',
  `Month` int(11) NOT NULL,
  `Year` int(11) NOT NULL,
  `Employee` int(11) NOT NULL,
  `Sum` decimal(8,2) NOT NULL,
  PRIMARY KEY (`idx`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2255 ;

CREATE TABLE IF NOT EXISTS `penalties` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Employee` int(11) NOT NULL,
  `Month` int(11) NOT NULL,
  `Year` int(11) NOT NULL,
  `Type` int(11) NOT NULL,
  `Amount` decimal(8,4) NOT NULL,
  `Price` decimal(8,2) NOT NULL,
  `Sum` decimal(8,2) NOT NULL,
  PRIMARY KEY (`idx`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1603 ;

CREATE TABLE IF NOT EXISTS `penalty_types` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Name` char(100) NOT NULL,
  PRIMARY KEY (`idx`),
  UNIQUE KEY `Name` (`Name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=56 ;

CREATE TABLE IF NOT EXISTS `titles` (
  `idx` int(11) NOT NULL AUTO_INCREMENT,
  `Name` char(100) NOT NULL,
  `SortOrder` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`idx`),
  UNIQUE KEY `Title` (`Name`),
  KEY `SortOrder` (`SortOrder`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=108 ;
...
Рейтинг: 0 / 0
17.08.2014, 14:13:03
    #38721914
Медленный запрос, что можно сделать?
Извините, правильный запрос на обновление всех расчётов всех сотрудников текущего месяца:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
INSERT INTO accounts (Employee, Month, Year, BeforeAccruals, BeforePenalties, BeforePayments, Accruals, Penalties, Payments)
  SELECT e.idx, 12, 2013,
  (SELECT SUM(`Sum`) FROM accruals WHERE Employee = e.idx AND ((Year = 2013 AND Month < 12) OR (Year < 2013))), 
  (SELECT SUM(`Sum`) FROM penalties WHERE Employee = e.idx AND  ((Year = 2013 AND Month < 12) OR (Year < 2013))), 
  (SELECT SUM(`Sum`) FROM payments WHERE Employee = e.idx AND  ((Year = 2013 AND Month < 12) OR (Year < 2013))), 
  (SELECT SUM(`Sum`) FROM accruals WHERE Employee = e.idx AND (Year = 2013 AND Month = 12)), 
  (SELECT SUM(`Sum`) FROM penalties WHERE Employee = e.idx AND (Year = 2013 AND Month = 12)), 
  (SELECT SUM(`Sum`) FROM payments WHERE Employee = e.idx AND (Year = 2013 AND Month = 12)) 
FROM employees e 
WHERE e.Firm=1 AND ((e.StartYear = 2013 AND e.StartMonth <= 12) OR (e.StartYear < 2013)) AND ((e.QuitYear = 0 AND e.QuitMonth=0) OR (e.QuitYear > 2013) OR (e.QuitYear = 2013 AND e.QuitMonth >= 12)) 
ON DUPLICATE KEY UPDATE 
  BeforeAccruals=(SELECT SUM(`Sum`) FROM accruals WHERE Employee = accounts.Employee AND ((Year = 2013 AND Month < 12) OR (Year < 2013))), 
  BeforePenalties=(SELECT SUM(`Sum`) FROM penalties WHERE Employee = accounts.Employee AND  ((Year = 2013 AND Month < 12) OR (Year < 2013))), 
  BeforePayments=(SELECT SUM(`Sum`) FROM payments WHERE Employee = accounts.Employee AND  ((Year = 2013 AND Month < 12) OR (Year < 2013))), 
  Accruals=(SELECT SUM(`Sum`) FROM accruals WHERE Employee = accounts.Employee AND (Year = 2013 AND Month = 12)),
  Penalties=(SELECT SUM(`Sum`) FROM penalties WHERE Employee = accounts.Employee AND (Year = 2013 AND Month = 12)),
  Payments=(SELECT SUM(`Sum`) FROM payments WHERE Employee = accounts.Employee AND (Year = 2013 AND Month = 12));
...
Рейтинг: 0 / 0
17.08.2014, 16:51:39
    #38721962
miksoft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Медленный запрос, что можно сделать?
Подробно не вникал, но бросается в глаза, что даты должны быть датами, а не парами год-месяц. Тогда индексы заработают с куда большей вероятностью и пользой.
...
Рейтинг: 0 / 0
17.08.2014, 17:10:27
    #38721967
Медленный запрос, что можно сделать?
miksoft, спасибо, сейчас потестирую...
...
Рейтинг: 0 / 0
29.08.2014, 13:45:38
    #38732721
Медленный запрос, что можно сделать?
miksoft, ваш совет дал ощутимый результат, всё стало быстрее раз в 10. :)
...
Рейтинг: 0 / 0
30.08.2014, 12:42:34
    #38733302
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Медленный запрос, что можно сделать?
Тьмутаракан,

Также рекомендую оформить это дело в виде хранимой процедуры,
и разделить там insert и update.

Собственно, можно просто вначале insert недостающих записей с нулями,
а потом -- только уже update нужного.
...
Рейтинг: 0 / 0
30.08.2014, 12:45:24
    #38733304
miksoft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Медленный запрос, что можно сделать?
MasterZivТьмутаракан,

Также рекомендую оформить это дело в виде хранимой процедуры,
и разделить там insert и update.

Собственно, можно просто вначале insert недостающих записей с нулями,
а потом -- только уже update нужного.А откуда тут будет ускорение?
...
Рейтинг: 0 / 0
30.08.2014, 12:59:57
    #38733309
Медленный запрос, что можно сделать?
MasterZiv, с процедурами пока не сталкивался, но на будущее запомню совет. Спасибо!
...
Рейтинг: 0 / 0
31.08.2014, 13:38:10
    #38733553
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Медленный запрос, что можно сделать?
miksoftMasterZivТьмутаракан,

Также рекомендую оформить это дело в виде хранимой процедуры,
и разделить там insert и update.

Собственно, можно просто вначале insert недостающих записей с нулями,
а потом -- только уже update нужного.А откуда тут будет ускорение?

Ускорение не обещаю,
обещаю нормализацию и упрощение кода за счёт более грамотного оформления.
...
Рейтинг: 0 / 0
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Медленный запрос, что можно сделать? / 9 сообщений из 9, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]