在子查询(数据透视表)中重复的同一查询中避免多次表扫描

时间:2017-10-10 18:09:45

标签: mysql sql pivot-table business-intelligence

我有一个包含日期和状态的普通表(select result.reference_date ,sum(case when result.funnel_phase = 'signup_date' then cont else 0 end) as signup_date ,sum(case when result.funnel_phase = '01_approval_requested' then cont else 0 end) as 01_approval_requested ,sum(case when result.funnel_phase = '02_approval_allowed' then cont else 0 end) as 02_approval_allowed ,sum(case when result.funnel_phase = '03_profile_sent_documents' then cont else 0 end) as 03_profile_sent_documents ,sum(case when result.funnel_phase = '04_profile_approved' then cont else 0 end) as 04_profile_approved ,sum(case when result.funnel_phase = '05_loan_request' then cont else 0 end) as 05_loan_request ,sum(case when result.funnel_phase = '06_boleto_confirmed' then cont else 0 end) as 06_boleto_confirmed ,sum(case when result.funnel_phase = '07_loan_issued' then cont else 0 end) as 07_loan_issued from ( select 'signup_date' funnel_phase, month(signup_date) as reference_date, count(signup_date) as cont from db.mkt_parceiros_2 group by 1, 2 union all select '01_approval_requested' funnel_phase, month(01_approval_requested) as reference_date, count(01_approval_requested) as cont from db.mkt_parceiros_2 group by 1, 2 union all select '02_approval_allowed' funnel_phase, month(02_approval_allowed) as reference_date, count(02_approval_allowed) as cont from db.mkt_parceiros_2 group by 1, 2 union all select '03_profile_sent_documents' funnel_phase, month(03_profile_sent_documents) as reference_date, count(03_profile_sent_documents) as cont from db.mkt_parceiros_2 group by 1, 2 union all select '04_profile_approved' funnel_phase, month(04_profile_approved) as reference_date, count(04_profile_approved) as cont from db.mkt_parceiros_2 group by 1, 2 union all select '05_loan_request' funnel_phase, month(05_loan_request) as reference_date, count(05_loan_request) as cont from db.mkt_parceiros_2 group by 1, 2 union all select '06_boleto_confirmed' funnel_phase, month(06_boleto_confirmed) as reference_date, count(06_boleto_confirmed) as cont from db.mkt_parceiros_2 group by 1, 2 union all select '07_loan_issued' funnel_phase, month(07_loan_issued) as reference_date, count(07_loan_issued) as cont from db.mkt_parceiros_2 group by 1, 2 ) result group by result.reference_date ),我必须从中创建一个数据透视表(Y轴为MM-YYYY,X轴为状态,然后在表格中为其对应的值)。 我提取同一个表七次,因为它是状态数,然后我按日期和状态对其进行分组。

它在同一张桌子上读了7次,花了很长时间。 我想知道是否有任何方法可以改善其性能。 我尝试如下:

{{1}}

2 个答案:

答案 0 :(得分:1)

reference_date子查询选择的所有union字段都会返回一个月份数。

您可以使用数字表构建一个等效查询,其中包含所有12个月的值并交叉加入db.mkt_parceiros_2

SELECT t1.m AS reference_date,
       COUNT(CASE 
                WHEN MONTH(signup_date) = t1.m THEN 1 
             END) AS signup_date,
       COUNT(CASE 
                WHEN MONTH(01_approval_requested) = t1.m THEN 1 
             END) AS 01_approval_requested,
       ... etc
FROM (
   SELECT 1 AS m UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL
   SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL
   SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL
   SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12) AS t1
CROSS JOIN db.mkt_parceiros_2 AS t2
GROUP BY t1.m

答案 1 :(得分:0)

我的答案与Giorgos类似,但避免 CROSS 加入;是否昂贵/复杂的连接条件比CROSS JOIN快,我不能肯定地说。

SELECT theMonths.m AS reference_date
   , COUNT(CASE WHEN MONTH(mpt.signup_date) = theMonths.m THEN 1 END) AS signup_date
   , COUNT(CASE WHEN MONTH(mpt.`01_approval_requested`) = theMonths.m THEN 1 END) AS `01_approval_requested`
   , ...
FROM (
   SELECT 1 AS m 
   UNION SELECT 2 
   UNION SELECT 3
   ...) AS theMonths
LEFT JOIN db.mkt_parceiros_2 AS mpt
ON theMonths.m IN (
         MONTH(mpt.signup_date) 
         , MONTH(mpt.`01_approval_requested`)
         , ...
         )
GROUP BY theMonths.m
;

如果您只参加特定的一年,可能会从此次更改中获得一些性能提升 ...但不太可能,因为MySQL不会/无法利用OR条件列表中的索引。

SELECT theMonths.m AS reference_date
   , COUNT(CASE WHEN mpt.signup_date BETWEEN theMonths.mBegin AND theMonths.mEnd THEN 1 END) AS signup_date
   , COUNT(CASE WHEN mpt.`01_approval_requested` BETWEEN theMonths.mBegin AND theMonths.mEnd THEN 1 END) AS `01_approval_requested`
   , ...
FROM (
   SELECT 1 AS m, '2017-01-01' AS mBegin, '2017-01-31' AS mEnd 
   UNION SELECT 2 , '2017-02-01', '2017-02-28' /* careful of leap year */
   ...) AS theMonths
LEFT JOIN db.mkt_parceiros_2 AS mpt
   ON mpt.signup_date BETWEEN theMonths.mBegin AND theMonths.mEnd
   OR mpt.`01_approval_requested` BETWEEN theMonths.mBegin AND theMonths.mEnd
   OR ...
GROUP BY theMonths.m
;