具有用户定义变量的MySQL子查询

phi*_*kle 8 mysql

我正在尝试使用通过变量传递日期引用的子查询来完成需要计算列的查询.我不确定我是不是"做得对",但基本上查询永远不会完成并且旋转几分钟.这是我的查询:

select @groupdate:=date_format(order_date,'%Y-%m'), count(distinct customer_email) as num_cust,
(
  select count(distinct cev.customer_email) as num_prev
  from _pj_cust_email_view cev
  inner join _pj_cust_email_view as prev_purch on (prev_purch.order_date < @groupdate) and (cev.customer_email=prev_purch.customer_email)
  where cev.order_date > @groupdate
) as prev_cust_count
from _pj_cust_email_view
group by @groupdate;
Run Code Online (Sandbox Code Playgroud)

子查询inner join完成了自我加入,只给了我在日期之前购买的人数@groupdate.在EXPLAIN下面的是:

+----+----------------------+---------------------+------+---------------+-----------+---------+---------------------------+--------+---------------------------------+
| id | select_type          | table               | type | possible_keys | key       | key_len | ref                       | rows   | Extra                           |
+----+----------------------+---------------------+------+---------------+-----------+---------+---------------------------+--------+---------------------------------+
|  1 | PRIMARY              | _pj_cust_email_view | ALL  | NULL          | NULL      | NULL    | NULL                      | 140147 | Using temporary; Using filesort |
|  2 | UNCACHEABLE SUBQUERY | cev                 | ALL  | IDX_EMAIL     | NULL      | NULL    | NULL                      | 140147 | Using where                     |
|  2 | UNCACHEABLE SUBQUERY | prev_purch          | ref  | IDX_EMAIL     | IDX_EMAIL | 768     | cart_A.cev.customer_email |      1 | Using where                     |
+----+----------------------+---------------------+------+---------------+-----------+---------+---------------------------+--------+---------------------------------+
Run Code Online (Sandbox Code Playgroud)

表的结构_pj_cust_email_view如下:

'_pj_cust_email_view', 'CREATE TABLE `_pj_cust_email_view` (
  `order_date` varchar(10) CHARACTER SET utf8 DEFAULT NULL,
  `customer_email` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  KEY `IDX_EMAIL` (`customer_email`),
  KEY `IDX_ORDERDATE` (`order_date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1'
Run Code Online (Sandbox Code Playgroud)

同样,正如我之前所说,我不确定这是实现这一目标的最佳方法.任何批评,方向表示赞赏!

更新

我已经取得了一些进展,我现在通过在数据库中迭代所有已知月份而不是数月并且提前设置变量来进行上述程序.我还是不喜欢这个.这就是我现在所拥有的:

设置用户定义的变量

set @startdate:='2010-08', @enddate:='2010-09';
Run Code Online (Sandbox Code Playgroud)

获取给定范围内的完全不同的电子邮件

select count(distinct customer_email) as num_cust
from _pj_cust_email_view
where order_date between @startdate and @enddate;
Run Code Online (Sandbox Code Playgroud)

获取在给定范围之前购买的客户总数

select count(distinct cev.customer_email) as num_prev
  from _pj_cust_email_view cev
  inner join _pj_cust_email_view as prev_purch on (prev_purch.order_date < @startdate) and (cev.customer_email=prev_purch.customer_email)
  where cev.order_date between @startdate and @enddate;
Run Code Online (Sandbox Code Playgroud)

@startdate设置为每月的开始和@enddate意味着一个月的范围的末尾.

我真的觉得这仍然可以在一个完整的查询中完成.

Bil*_*win 8

我认为你根本不需要使用子查询,也不需要迭代数月.

相反,我建议您创建一个表来存储所有月份.即使你用100年的月份预填充它,它也只有1200行,这是微不足道的.

CREATE TABLE Months (
    start_date DATE, 
    end_date DATE, 
    PRIMARY KEY (start_date, end_date)
);
INSERT INTO Months (start_date, end_date) 
VALUES ('2011-03-01', '2011-03-31');
Run Code Online (Sandbox Code Playgroud)

存储实际的开始和结束日期,以便您可以使用DATE数据类型并正确索引两列.

编辑: 我想我更了解你的要求,我已经清理了这个答案.以下查询可能适合您:

SELECT DATE_FORMAT(m.start_date, '%Y-%m') AS month,
  COUNT(DISTINCT cev.customer_email) AS current,
  GROUP_CONCAT(DISTINCT cev.customer_email) AS current_email,
  COUNT(DISTINCT prev.customer_email) AS earlier,
  GROUP_CONCAT(DISTINCT prev.customer_email) AS earlier_email
FROM Months AS m 
LEFT OUTER JOIN _pj_cust_email_view AS cev
  ON cev.order_date BETWEEN m.start_date AND m.end_date
INNER JOIN Months AS mprev
  ON mprev.start_date <= m.start_date
LEFT OUTER JOIN _pj_cust_email_view AS prev
  ON prev.order_date BETWEEN mprev.start_date AND mprev.end_date
GROUP BY month;
Run Code Online (Sandbox Code Playgroud)

如果在表中创建以下复合索引:

CREATE INDEX order_email on _pj_cust_email_view (order_date, customer_email);
Run Code Online (Sandbox Code Playgroud)

然后查询最有可能成为仅索引查询,并且运行速度会快很多.

以下是此查询的EXPLAIN优化报告.type: index每张表的注释.

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: m
         type: index
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 6
          ref: NULL
         rows: 4
        Extra: Using index; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: mprev
         type: index
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 6
          ref: NULL
         rows: 4
        Extra: Using where; Using index; Using join buffer
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: cev
         type: index
possible_keys: order_email
          key: order_email
      key_len: 17
          ref: NULL
         rows: 10
        Extra: Using index
*************************** 4. row ***************************
           id: 1
  select_type: SIMPLE
        table: prev
         type: index
possible_keys: order_email
          key: order_email
      key_len: 17
          ref: NULL
         rows: 10
        Extra: Using index
Run Code Online (Sandbox Code Playgroud)

这是一些测试数据:

INSERT INTO Months (start_date, end_date) VALUES
('2011-03-01', '2011-03-31'),
('2011-02-01', '2011-02-28'),
('2011-01-01', '2011-01-31'),
('2010-12-01', '2010-12-31');

INSERT INTO _pj_cust_email_view VALUES
('ron', '2011-03-10'),
('hermione', '2011-03-15'),
('hermione', '2011-02-15'),
('hermione', '2011-01-15'),
('hermione', '2010-12-15'),
('neville', '2011-01-10'),
('harry', '2011-03-19'),
('harry', '2011-02-10'),
('molly', '2011-03-25'),
('molly', '2011-01-10');
Run Code Online (Sandbox Code Playgroud)

这是给定数据的结果,包括连续的电子邮件列表,以便于查看.

+---------+---------+--------------------------+---------+----------------------------------+
| month   | current | current_email            | earlier | earlier_email                    |
+---------+---------+--------------------------+---------+----------------------------------+
| 2010-12 |       1 | hermione                 |       1 | hermione                         | 
| 2011-01 |       3 | neville,hermione,molly   |       3 | hermione,molly,neville           | 
| 2011-02 |       2 | hermione,harry           |       4 | harry,hermione,molly,neville     | 
| 2011-03 |       4 | molly,ron,harry,hermione |       5 | molly,ron,hermione,neville,harry | 
+---------+---------+--------------------------+---------+----------------------------------+
Run Code Online (Sandbox Code Playgroud)