MySQL SQL 性能需要改进
MySQL SQL Performance need some improvement
我已经通过 MySQL 解决了许多挑战,我认为现在我能够构建我需要的一切,让一些东西发挥作用。但是现在,对于一个相当大的 SQL 声明 returns 大量数据,我需要第一次处理 MySQL 性能。
我希望这里有人能帮我找出为什么下面的语句如此慢。从不同的 table 中收集 740 个结果需要 3 分钟多的时间。最大的 table 是“报告”table,目前包含超过 20.000 个条目。
如果有人能给我指出正确的方向,我也可以自学。我什至不知道去哪里寻找我当前问题的答案。
好的,这就是我正在谈论的声明。也许,如果有人在 SQL 性能方面有足够的经验,一些东西就会立即跳到他们身上。我很乐意提供任何反馈。我将在代码本身之后详细说明声明:
SELECT
R_ID,
R_From,
R_To,
SUM(UR_TotalTime) AS UR_TotalTime,
R_Reported,
U_ID,
U_Lastname,
U_Firstname,
C_ID,
C_Lastname,
C_Firstname,
R_Breaks,
MAX(CR_BID) AS CR_BID,
R_Type,
R_Distance,
R_AdditionalDistance,
R_Activities,
R_Description,
R_Signature,
CT_SigReq,
MAX(I_LastIntegration) AS I_LastIntegration
FROM
reports
LEFT JOIN
userreports ON R_ID = UR_RID
LEFT JOIN
users ON R_UID = U_ID
LEFT JOIN
customers ON R_CID = C_ID
LEFT JOIN
customerterms ON CT_CID = R_CID
LEFT JOIN
integration ON R_UID = I_UID
LEFT JOIN
customerreports ON R_ID = CR_RID
WHERE
(CAST(R_From AS DATE) BETWEEN CT_From AND CT_To
OR R_CID = 0)
AND ((R_From BETWEEN '2021-02-01 00.00.00' AND '2021-02-28 23.59.59')
OR (R_To BETWEEN '2021-02-01 00.00.00' AND '2021-02-28 23.59.59')
OR (R_From <= '2021-02-01 00.00.00'
AND R_To >= '2021-02-28 23.59.59'))
GROUP BY R_ID
ORDER BY R_From ASC
所以我这里有以下内容:
reports (R_*) - 这是查询的主要table。我需要它的一些数据,但它也是过滤器,因为我只需要特定时间戳之间的结果。
CREATE TABLE `reports` (
`R_ID` int(100) NOT NULL AUTO_INCREMENT,
`R_Type` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_UID` int(6) NOT NULL,
`R_CID` int(10) NOT NULL,
`R_From` datetime(0) NOT NULL,
`R_To` datetime(0) NOT NULL,
`R_Traveltime` int(11) NOT NULL,
`R_Breaks` int(11) NOT NULL DEFAULT 0,
`R_PayoutFlextime` decimal(20, 2) NOT NULL DEFAULT 0.00,
`R_Distance` int(11) NOT NULL DEFAULT 0,
`R_AdditionalDistance` int(11) NOT NULL DEFAULT 0,
`R_Activities` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_Description` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_Signature` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0',
`R_SignatureDate` datetime(0) DEFAULT NULL,
`R_Reported` datetime(0) NOT NULL,
`R_Status` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'New',
`R_EditedBy` int(11) DEFAULT NULL,
`R_EditedDateTime` datetime(0) DEFAULT NULL,
PRIMARY KEY (`R_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
userreports (UR_*) - 提供一些根据报告中的源数据计算的数据
CREATE TABLE `userreports` (
`UR_ID` int(11) NOT NULL AUTO_INCREMENT,
`UR_RID` int(100) NOT NULL,
`UR_UID` int(6) NOT NULL,
`UR_Date` date NOT NULL,
`UR_From` time(0) NOT NULL,
`UR_To` time(0) NOT NULL,
`UR_ReportedTime` decimal(20, 5) DEFAULT NULL,
`UR_ReportedTravel` decimal(20, 5) NOT NULL,
`UR_ReportedBreaks` decimal(20, 5) DEFAULT NULL,
`UR_TotalPercentageSurcharge` decimal(20, 2) DEFAULT NULL,
`UR_TotalTime` decimal(20, 5) DEFAULT NULL,
`UR_PercentageSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`UR_Distance` decimal(20, 2) DEFAULT NULL,
`UR_AdditionalDistance` decimal(20, 2) DEFAULT NULL,
`UR_TravelCompensation` decimal(20, 2) DEFAULT NULL,
PRIMARY KEY (`UR_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customerreports (CR_*) - 与用户报告相同,但包含从客户角度计算的数据
CREATE TABLE `customerreports` (
`CR_ID` int(11) NOT NULL AUTO_INCREMENT,
`CR_RID` int(100) NOT NULL,
`CR_CID` int(6) NOT NULL,
`CR_Date` date NOT NULL,
`CR_From` time(0) NOT NULL,
`CR_To` time(0) NOT NULL,
`CR_ReportedTime` decimal(20, 2) DEFAULT NULL,
`CR_ReportedBreaks` decimal(20, 2) DEFAULT NULL,
`CR_Hourly` decimal(20, 2) DEFAULT NULL,
`CR_Salary` decimal(20, 2) DEFAULT NULL,
`CR_TotalPercentageSurcharge` decimal(20, 2) DEFAULT NULL,
`CR_TotalFixedSurcharge` decimal(20, 2) DEFAULT NULL,
`CR_TotalTime` decimal(20, 2) DEFAULT NULL,
`CR_TotalSalary` decimal(20, 2) DEFAULT NULL,
`CR_FixedSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`CR_PercentageSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`CR_Distance` decimal(20, 2) DEFAULT NULL,
`CR_AdditionalDistance` decimal(20, 2) DEFAULT NULL,
`CR_TravelCompensation` decimal(20, 2) DEFAULT NULL,
`CR_BID` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`CR_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
users (U_*) - 显然将数据传递给创建报告的用户,例如姓名,...
CREATE TABLE `users` (
`U_ID` int(6) NOT NULL AUTO_INCREMENT,
`U_PW` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_PWInitial` tinyint(1) NOT NULL,
`U_FailedAttempts` int(1) NOT NULL,
`U_Email` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Title` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`U_Firstname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Lastname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_ETC` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Street` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Housenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Code` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_City` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Birthdate` date NOT NULL,
`U_Sex` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Maritalstatus` varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Severelydisabled` tinyint(1) NOT NULL,
`U_Severelydisabledspecify` int(3) NOT NULL,
`U_Citizenship` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Education` varchar(70) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Vocationaltraining` varchar(70) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_CLID` tinyint(1) NOT NULL,
`U_CLSpecify` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_IBAN` varchar(27) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_BIC` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_INID` int(11) DEFAULT NULL,
`U_Insurancenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Insurancetype` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Taxidentificationnumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Confession` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Entry` date NOT NULL,
`U_TEntry` date NOT NULL,
`U_Exit` date NOT NULL DEFAULT '9999-12-31',
`U_Hourscarryover` decimal(20, 2) NOT NULL,
`U_TotalHolidayCarryover` int(11) NOT NULL DEFAULT 0,
`U_UsedHolidayCarryover` int(11) NOT NULL DEFAULT 0,
`U_SIN` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_RVBDone` tinyint(1) NOT NULL DEFAULT 0,
`U_ClosedMonth` date NOT NULL DEFAULT '1970-01-01',
`U_DeleteDate` date DEFAULT NULL,
PRIMARY KEY (`U_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customers (C_*) - 与用户相同,但针对用户工作的客户数据
CREATE TABLE `customers` (
`C_ID` int(10) NOT NULL AUTO_INCREMENT,
`C_MID` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Active` tinyint(1) NOT NULL,
`C_Email` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Title` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Firstname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Lastname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Birthdate` date NOT NULL,
`C_ETC` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Street` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Housenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Code` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_City` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_IBAN` varchar(27) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_BIC` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Insurancenumber` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_INID` int(11) DEFAULT NULL,
`C_Insurancetype` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Sex` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Contact1` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Contact2` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_ContactChoice` int(1) DEFAULT 0,
`C_DeleteDate` date DEFAULT NULL,
`C_DeactivationDate` date DEFAULT NULL,
`C_CreationDate` date DEFAULT NULL,
`C_DeceasedDate` date DEFAULT NULL,
PRIMARY KEY (`C_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
集成 (I_*) - 提供有关报告是否已集成(且无法再更改)的数据
CREATE TABLE `integration` (
`I_ID` int(11) NOT NULL AUTO_INCREMENT,
`I_UID` int(11) NOT NULL,
`I_LastIntegration` date NOT NULL DEFAULT '1970-01-01',
`I_SumFlextime` decimal(20, 2) NOT NULL DEFAULT 0.00,
`I_OldHolidays` int(5) NOT NULL DEFAULT 0,
PRIMARY KEY (`I_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customerterms (CT_*) - 在这种情况下仅在指定客户需要签署报告时提供
CREATE TABLE `customerterms` (
`CT_ID` int(50) NOT NULL AUTO_INCREMENT,
`CT_CID` int(10) NOT NULL,
`CT_From` date NOT NULL,
`CT_To` date NOT NULL,
`CT_Hourly` decimal(20, 2) NOT NULL,
`CT_FixedTravelCompensation` decimal(20, 2) NOT NULL,
`CT_PerKMCompensationBase` decimal(20, 2) NOT NULL,
`CT_PerKMCompensationAdditional` decimal(20, 2) NOT NULL,
`CT_MaxTravelCompensationReport` decimal(20, 2) DEFAULT NULL,
`CT_MaxTravelCompensationMonthly` decimal(20, 2) DEFAULT NULL,
`CT_FixedSaturdaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageSaturdaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_FixedSundaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageSundaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_FixedHolidaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageHolidaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_SigReq` int(1) NOT NULL,
`CT_NighttimeFrom` time(0) NOT NULL DEFAULT '00:00:00',
`CT_NighttimeTo` time(0) NOT NULL DEFAULT '00:00:00',
`CT_FixedNighttimeSurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageNighttimeSurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_StackingSurcharge` tinyint(1) NOT NULL DEFAULT 0,
`CT_MinimumTime` int(11) NOT NULL DEFAULT 1,
`CT_TimeIncrement` int(11) NOT NULL DEFAULT 1,
PRIMARY KEY (`CT_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
服务器是 运行 MySQL 5.7,有 4 个 4.6Ghz 处理器和 16GB 可用内存。
由于这是一个业余项目,我正在支持小型护理企业,让他们更轻松地管理他们的日常任务,我可以改变这里的一切。代码、数据库布局,应有尽有。只要办公室里的穷人不用等5分钟,就为了有时甚至只能得到一个超时...
我会将 EXPLAIN 的结果添加为图像,因为我无法让它看起来很好
否则...
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
| 1 | SIMPLE | reports | NULL | ALL | PRIMARY,R_From,R_To | NULL | NULL | NULL | 22249 | 29.76 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | userreports | NULL | ALL | NULL | NULL | NULL | NULL | 21359 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | users | NULL | eq_ref | PRIMARY | PRIMARY | 4 | dbs671769.reports.R_UID | 1 | 100.00 | NULL |
| 1 | SIMPLE | customers | NULL | eq_ref | PRIMARY | PRIMARY | 4 | dbs671769.reports.R_CID | 1 | 100.00 | NULL |
| 1 | SIMPLE | customerterms | NULL | ALL | NULL | NULL | NULL | NULL | 1429 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | integration | NULL | ALL | NULL | NULL | NULL | NULL | 1134 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | customerreports | NULL | ALL | NULL | NULL | NULL | NULL | 9078 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
有没有什么方法可以更快、更可靠地整合所有这些数据?
在此先感谢您对此的任何帮助或想法。
让我们首先为查询中使用的每个外键添加索引 -
ALTER TABLE `userreports`
ADD INDEX `FK_UR_RID` (`UR_RID`);
ALTER TABLE `customerterms`
ADD INDEX `FK_CT_CID` (`CT_CID`);
ALTER TABLE `integration`
ADD INDEX `FK_I_UID` (`I_UID`);
ALTER TABLE `customerreports`
ADD INDEX `FK_CR_RID` (`CR_RID`);
请添加这些索引,然后将更新后的 EXPLAIN 输出以及以下查询的结果添加到您的问题中。
-- this just retrieves some basic stats about size of each table used in your query
SELECT TABLE_NAME, ENGINE, VERSION, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH, INDEX_LENGTH
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'dbs671769'
AND TABLE_NAME IN('customerreports', 'customers', 'customerterms', 'integration', 'reports', 'userreports', 'users');
WHERE (CAST(R_From AS DATE) BETWEEN CT_From AND CT_To
OR R_CID = 0
)
OR
反应迟钝。有什么方法可以摆脱 R_CID = 0
吗?如果没有,我们可以谈谈UNION
。
重写其余部分:
R_From >= CT_From AND R_From < CT_To + INTERVAL 1 DAY
AND ((R_From BETWEEN '2021-02-01 00.00.00'
AND '2021-02-28 23.59.59')
OR (R_To BETWEEN '2021-02-01 00.00.00'
AND '2021-02-28 23.59.59')
OR (R_From <= '2021-02-01 00.00.00'
AND R_To >= '2021-02-28 23.59.59')
)
R_From
能保证是< R_To
吗?如果是这样,这种简化(或删除)是否会做同样的事情
AND R_From < '2021-03-01'
AND R_To >= '2021-02-01'
这需要对中间结果进行两次传递:
GROUP BY R_ID
ORDER BY R_From ASC
这需要一次通过,但通常会给出相同的结果,甚至可能会得到更好的结果:
GROUP BY R_From, R_ID
ORDER BY R_From, R_ID
(让人恼火的是:不要在列名前加上 table 名称(或 'R_');在 JOINing 时对所有列使用别名:SELECT R.ID ... FROM reports AS R JOIN ...
)
另一个回答提到了一些INDEXes
;这可能会给你很多加速。经过我的一些建议,可能会有更多的索引提示。
TEXT
列有一些开销;您列出的许多案例都可以用更小的东西来完成,比如 VARCHAR(100)
。例如,目前世界上最长的“城市”名称只有 91 个字符:“Poselok Uchebnogo Khozyaystva Srednego Professionalno-Tekhnicheskoye Uchilishche Nomer Odin”
你好像是运行旧版的MySQL?否则你可能会因为 GROUP BY
;比照“only_full_group_by”。
我已经通过 MySQL 解决了许多挑战,我认为现在我能够构建我需要的一切,让一些东西发挥作用。但是现在,对于一个相当大的 SQL 声明 returns 大量数据,我需要第一次处理 MySQL 性能。
我希望这里有人能帮我找出为什么下面的语句如此慢。从不同的 table 中收集 740 个结果需要 3 分钟多的时间。最大的 table 是“报告”table,目前包含超过 20.000 个条目。
如果有人能给我指出正确的方向,我也可以自学。我什至不知道去哪里寻找我当前问题的答案。
好的,这就是我正在谈论的声明。也许,如果有人在 SQL 性能方面有足够的经验,一些东西就会立即跳到他们身上。我很乐意提供任何反馈。我将在代码本身之后详细说明声明:
SELECT
R_ID,
R_From,
R_To,
SUM(UR_TotalTime) AS UR_TotalTime,
R_Reported,
U_ID,
U_Lastname,
U_Firstname,
C_ID,
C_Lastname,
C_Firstname,
R_Breaks,
MAX(CR_BID) AS CR_BID,
R_Type,
R_Distance,
R_AdditionalDistance,
R_Activities,
R_Description,
R_Signature,
CT_SigReq,
MAX(I_LastIntegration) AS I_LastIntegration
FROM
reports
LEFT JOIN
userreports ON R_ID = UR_RID
LEFT JOIN
users ON R_UID = U_ID
LEFT JOIN
customers ON R_CID = C_ID
LEFT JOIN
customerterms ON CT_CID = R_CID
LEFT JOIN
integration ON R_UID = I_UID
LEFT JOIN
customerreports ON R_ID = CR_RID
WHERE
(CAST(R_From AS DATE) BETWEEN CT_From AND CT_To
OR R_CID = 0)
AND ((R_From BETWEEN '2021-02-01 00.00.00' AND '2021-02-28 23.59.59')
OR (R_To BETWEEN '2021-02-01 00.00.00' AND '2021-02-28 23.59.59')
OR (R_From <= '2021-02-01 00.00.00'
AND R_To >= '2021-02-28 23.59.59'))
GROUP BY R_ID
ORDER BY R_From ASC
所以我这里有以下内容: reports (R_*) - 这是查询的主要table。我需要它的一些数据,但它也是过滤器,因为我只需要特定时间戳之间的结果。
CREATE TABLE `reports` (
`R_ID` int(100) NOT NULL AUTO_INCREMENT,
`R_Type` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_UID` int(6) NOT NULL,
`R_CID` int(10) NOT NULL,
`R_From` datetime(0) NOT NULL,
`R_To` datetime(0) NOT NULL,
`R_Traveltime` int(11) NOT NULL,
`R_Breaks` int(11) NOT NULL DEFAULT 0,
`R_PayoutFlextime` decimal(20, 2) NOT NULL DEFAULT 0.00,
`R_Distance` int(11) NOT NULL DEFAULT 0,
`R_AdditionalDistance` int(11) NOT NULL DEFAULT 0,
`R_Activities` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_Description` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`R_Signature` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0',
`R_SignatureDate` datetime(0) DEFAULT NULL,
`R_Reported` datetime(0) NOT NULL,
`R_Status` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'New',
`R_EditedBy` int(11) DEFAULT NULL,
`R_EditedDateTime` datetime(0) DEFAULT NULL,
PRIMARY KEY (`R_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
userreports (UR_*) - 提供一些根据报告中的源数据计算的数据
CREATE TABLE `userreports` (
`UR_ID` int(11) NOT NULL AUTO_INCREMENT,
`UR_RID` int(100) NOT NULL,
`UR_UID` int(6) NOT NULL,
`UR_Date` date NOT NULL,
`UR_From` time(0) NOT NULL,
`UR_To` time(0) NOT NULL,
`UR_ReportedTime` decimal(20, 5) DEFAULT NULL,
`UR_ReportedTravel` decimal(20, 5) NOT NULL,
`UR_ReportedBreaks` decimal(20, 5) DEFAULT NULL,
`UR_TotalPercentageSurcharge` decimal(20, 2) DEFAULT NULL,
`UR_TotalTime` decimal(20, 5) DEFAULT NULL,
`UR_PercentageSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`UR_Distance` decimal(20, 2) DEFAULT NULL,
`UR_AdditionalDistance` decimal(20, 2) DEFAULT NULL,
`UR_TravelCompensation` decimal(20, 2) DEFAULT NULL,
PRIMARY KEY (`UR_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customerreports (CR_*) - 与用户报告相同,但包含从客户角度计算的数据
CREATE TABLE `customerreports` (
`CR_ID` int(11) NOT NULL AUTO_INCREMENT,
`CR_RID` int(100) NOT NULL,
`CR_CID` int(6) NOT NULL,
`CR_Date` date NOT NULL,
`CR_From` time(0) NOT NULL,
`CR_To` time(0) NOT NULL,
`CR_ReportedTime` decimal(20, 2) DEFAULT NULL,
`CR_ReportedBreaks` decimal(20, 2) DEFAULT NULL,
`CR_Hourly` decimal(20, 2) DEFAULT NULL,
`CR_Salary` decimal(20, 2) DEFAULT NULL,
`CR_TotalPercentageSurcharge` decimal(20, 2) DEFAULT NULL,
`CR_TotalFixedSurcharge` decimal(20, 2) DEFAULT NULL,
`CR_TotalTime` decimal(20, 2) DEFAULT NULL,
`CR_TotalSalary` decimal(20, 2) DEFAULT NULL,
`CR_FixedSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`CR_PercentageSurchargeTypes` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`CR_Distance` decimal(20, 2) DEFAULT NULL,
`CR_AdditionalDistance` decimal(20, 2) DEFAULT NULL,
`CR_TravelCompensation` decimal(20, 2) DEFAULT NULL,
`CR_BID` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`CR_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
users (U_*) - 显然将数据传递给创建报告的用户,例如姓名,...
CREATE TABLE `users` (
`U_ID` int(6) NOT NULL AUTO_INCREMENT,
`U_PW` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_PWInitial` tinyint(1) NOT NULL,
`U_FailedAttempts` int(1) NOT NULL,
`U_Email` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Title` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`U_Firstname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Lastname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_ETC` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Street` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Housenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Code` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_City` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Birthdate` date NOT NULL,
`U_Sex` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Maritalstatus` varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Severelydisabled` tinyint(1) NOT NULL,
`U_Severelydisabledspecify` int(3) NOT NULL,
`U_Citizenship` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Education` varchar(70) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Vocationaltraining` varchar(70) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_CLID` tinyint(1) NOT NULL,
`U_CLSpecify` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_IBAN` varchar(27) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_BIC` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_INID` int(11) DEFAULT NULL,
`U_Insurancenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Insurancetype` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Taxidentificationnumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Confession` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_Entry` date NOT NULL,
`U_TEntry` date NOT NULL,
`U_Exit` date NOT NULL DEFAULT '9999-12-31',
`U_Hourscarryover` decimal(20, 2) NOT NULL,
`U_TotalHolidayCarryover` int(11) NOT NULL DEFAULT 0,
`U_UsedHolidayCarryover` int(11) NOT NULL DEFAULT 0,
`U_SIN` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`U_RVBDone` tinyint(1) NOT NULL DEFAULT 0,
`U_ClosedMonth` date NOT NULL DEFAULT '1970-01-01',
`U_DeleteDate` date DEFAULT NULL,
PRIMARY KEY (`U_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customers (C_*) - 与用户相同,但针对用户工作的客户数据
CREATE TABLE `customers` (
`C_ID` int(10) NOT NULL AUTO_INCREMENT,
`C_MID` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Active` tinyint(1) NOT NULL,
`C_Email` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Title` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Firstname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Lastname` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Birthdate` date NOT NULL,
`C_ETC` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Street` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Housenumber` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Code` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_City` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_IBAN` varchar(27) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_BIC` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`C_Insurancenumber` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_INID` int(11) DEFAULT NULL,
`C_Insurancetype` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Sex` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`C_Contact1` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_Contact2` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`C_ContactChoice` int(1) DEFAULT 0,
`C_DeleteDate` date DEFAULT NULL,
`C_DeactivationDate` date DEFAULT NULL,
`C_CreationDate` date DEFAULT NULL,
`C_DeceasedDate` date DEFAULT NULL,
PRIMARY KEY (`C_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
集成 (I_*) - 提供有关报告是否已集成(且无法再更改)的数据
CREATE TABLE `integration` (
`I_ID` int(11) NOT NULL AUTO_INCREMENT,
`I_UID` int(11) NOT NULL,
`I_LastIntegration` date NOT NULL DEFAULT '1970-01-01',
`I_SumFlextime` decimal(20, 2) NOT NULL DEFAULT 0.00,
`I_OldHolidays` int(5) NOT NULL DEFAULT 0,
PRIMARY KEY (`I_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
customerterms (CT_*) - 在这种情况下仅在指定客户需要签署报告时提供
CREATE TABLE `customerterms` (
`CT_ID` int(50) NOT NULL AUTO_INCREMENT,
`CT_CID` int(10) NOT NULL,
`CT_From` date NOT NULL,
`CT_To` date NOT NULL,
`CT_Hourly` decimal(20, 2) NOT NULL,
`CT_FixedTravelCompensation` decimal(20, 2) NOT NULL,
`CT_PerKMCompensationBase` decimal(20, 2) NOT NULL,
`CT_PerKMCompensationAdditional` decimal(20, 2) NOT NULL,
`CT_MaxTravelCompensationReport` decimal(20, 2) DEFAULT NULL,
`CT_MaxTravelCompensationMonthly` decimal(20, 2) DEFAULT NULL,
`CT_FixedSaturdaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageSaturdaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_FixedSundaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageSundaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_FixedHolidaySurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageHolidaySurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_SigReq` int(1) NOT NULL,
`CT_NighttimeFrom` time(0) NOT NULL DEFAULT '00:00:00',
`CT_NighttimeTo` time(0) NOT NULL DEFAULT '00:00:00',
`CT_FixedNighttimeSurcharge` decimal(20, 2) NOT NULL DEFAULT 0.00,
`CT_PercentageNighttimeSurcharge` decimal(20, 2) NOT NULL DEFAULT 1.00,
`CT_StackingSurcharge` tinyint(1) NOT NULL DEFAULT 0,
`CT_MinimumTime` int(11) NOT NULL DEFAULT 1,
`CT_TimeIncrement` int(11) NOT NULL DEFAULT 1,
PRIMARY KEY (`CT_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
服务器是 运行 MySQL 5.7,有 4 个 4.6Ghz 处理器和 16GB 可用内存。
由于这是一个业余项目,我正在支持小型护理企业,让他们更轻松地管理他们的日常任务,我可以改变这里的一切。代码、数据库布局,应有尽有。只要办公室里的穷人不用等5分钟,就为了有时甚至只能得到一个超时...
我会将 EXPLAIN 的结果添加为图像,因为我无法让它看起来很好 否则...
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
| 1 | SIMPLE | reports | NULL | ALL | PRIMARY,R_From,R_To | NULL | NULL | NULL | 22249 | 29.76 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | userreports | NULL | ALL | NULL | NULL | NULL | NULL | 21359 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | users | NULL | eq_ref | PRIMARY | PRIMARY | 4 | dbs671769.reports.R_UID | 1 | 100.00 | NULL |
| 1 | SIMPLE | customers | NULL | eq_ref | PRIMARY | PRIMARY | 4 | dbs671769.reports.R_CID | 1 | 100.00 | NULL |
| 1 | SIMPLE | customerterms | NULL | ALL | NULL | NULL | NULL | NULL | 1429 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | integration | NULL | ALL | NULL | NULL | NULL | NULL | 1134 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | customerreports | NULL | ALL | NULL | NULL | NULL | NULL | 9078 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
+─────+──────────────+──────────────────+─────────────+─────────+──────────────────────+──────────+──────────+──────────────────────────+───────+───────────+─────────────────────────────────────────────────────+
有没有什么方法可以更快、更可靠地整合所有这些数据?
在此先感谢您对此的任何帮助或想法。
让我们首先为查询中使用的每个外键添加索引 -
ALTER TABLE `userreports`
ADD INDEX `FK_UR_RID` (`UR_RID`);
ALTER TABLE `customerterms`
ADD INDEX `FK_CT_CID` (`CT_CID`);
ALTER TABLE `integration`
ADD INDEX `FK_I_UID` (`I_UID`);
ALTER TABLE `customerreports`
ADD INDEX `FK_CR_RID` (`CR_RID`);
请添加这些索引,然后将更新后的 EXPLAIN 输出以及以下查询的结果添加到您的问题中。
-- this just retrieves some basic stats about size of each table used in your query
SELECT TABLE_NAME, ENGINE, VERSION, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH, INDEX_LENGTH
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'dbs671769'
AND TABLE_NAME IN('customerreports', 'customers', 'customerterms', 'integration', 'reports', 'userreports', 'users');
WHERE (CAST(R_From AS DATE) BETWEEN CT_From AND CT_To
OR R_CID = 0
)
OR
反应迟钝。有什么方法可以摆脱 R_CID = 0
吗?如果没有,我们可以谈谈UNION
。
重写其余部分:
R_From >= CT_From AND R_From < CT_To + INTERVAL 1 DAY
AND ((R_From BETWEEN '2021-02-01 00.00.00'
AND '2021-02-28 23.59.59')
OR (R_To BETWEEN '2021-02-01 00.00.00'
AND '2021-02-28 23.59.59')
OR (R_From <= '2021-02-01 00.00.00'
AND R_To >= '2021-02-28 23.59.59')
)
R_From
能保证是< R_To
吗?如果是这样,这种简化(或删除)是否会做同样的事情
AND R_From < '2021-03-01'
AND R_To >= '2021-02-01'
这需要对中间结果进行两次传递:
GROUP BY R_ID
ORDER BY R_From ASC
这需要一次通过,但通常会给出相同的结果,甚至可能会得到更好的结果:
GROUP BY R_From, R_ID
ORDER BY R_From, R_ID
(让人恼火的是:不要在列名前加上 table 名称(或 'R_');在 JOINing 时对所有列使用别名:SELECT R.ID ... FROM reports AS R JOIN ...
)
另一个回答提到了一些INDEXes
;这可能会给你很多加速。经过我的一些建议,可能会有更多的索引提示。
TEXT
列有一些开销;您列出的许多案例都可以用更小的东西来完成,比如 VARCHAR(100)
。例如,目前世界上最长的“城市”名称只有 91 个字符:“Poselok Uchebnogo Khozyaystva Srednego Professionalno-Tekhnicheskoye Uchilishche Nomer Odin”
你好像是运行旧版的MySQL?否则你可能会因为 GROUP BY
;比照“only_full_group_by”。