MySQL 中的 Pivot - 根据日期时间列显示第一个和最后一个值

Posted

技术标签:

【中文标题】MySQL 中的 Pivot - 根据日期时间列显示第一个和最后一个值【英文标题】:Pivot in MySQL - Show first and last values depending on datetime-column 【发布时间】:2015-05-13 03:56:47 【问题描述】:

经过数小时的研究和尝试,我终于在 *** 上注册了,它通过阅读帮助了我多年。非常感谢您提供这次学习机会!我现在希望就我无法解决的 mysql 问题寻求帮助。我被卡住了。

来源:我有一张客户订单表。

输出:我想要按电子邮件地址分组的订单,对于每个电子邮件地址,都应该有总营业额的总和,订单计数以及创建的第一个订单和最后一个订单的行创建的。 到这里为止还好,可以通过分组和 min() 和 max() 函数来完成。 我卡住的地方:我想要第一个和最后一个订单的 IP 地址,第一个名称第一个和最后一个订单等等(请参阅下面的所需输出)。

我尝试和研究的内容: Groupwise max,子选择,子选择内部有一个顺序,外部有一个组,几个连接变体。

这是一个带有随机数据的 SQLfiddle 和一个我构建的 sql 查询。它的工作方式是: http://sqlfiddle.com/#!9/0272b/6

作为 SQLfiddle 中 SQL 的替代方法,我尝试了这个,它适用于一个电子邮件地址:

SELECT
    lastentry.entity_id,
    lastentry.customer_email,
    firstentry.created_at AS FirstOrder,
    lastentry.created_at  AS LastOrder,
    COUNT(lastentry.entity_id) AS TotalOrders,
    SUM(lastentry.grand_total) AS TotalTurnover,
    firstentry.entity_id,
    firstentry.remote_ip AS FirstIP,
    lastentry.remote_ip  AS LastIP
FROM
    orders lastentry
LEFT OUTER JOIN
    (
        SELECT
            co1.entity_id,
            co1.customer_email,
            co1.remote_ip,
            co1.created_at
        FROM
            orders AS co1,
            (
                SELECT
                    customer_email,
                    remote_ip,
                    MIN(created_at) AS maxpop
                FROM
                    orders
                GROUP BY
                    customer_email) AS co2
        WHERE
            co2.customer_email = co1.customer_email
        AND co1.created_at = co2.maxpop ) AS firstentry
ON
    (
        lastentry.customer_email = firstentry.customer_email )
ORDER BY
    lastentry.created_at DESC,
    firstentry.created_at ASC
LIMIT 1

我也尝试在 where 语句中使用子选择进行子选择或加入,但没有运气:

created_at = (SELECT MAX(t2.created_at)
    FROM orders t2
    WHERE customer_email= t1.customer_email
)

我实际想要的输出如下所示:

| customer_email           | FirstOrder          | LastOrder           | TotalOrders | TotalTurnover | FirstIP       | LastIP         | FirstName | LastName |
|--------------------------|---------------------|---------------------|-------------|---------------|---------------|----------------|-----------|----------|
| darmstrong3@skype.com    | 2014-11-06 16:38:31 | 2014-11-15 11:14:42 | 2           | 116,09        | 103.132.17.9  | 153.241.73.137 | David     | David    |
| fthompson0@moonfruit.com | 2014-08-19 06:26:26 | (null)              | 1           | 1,1           | 87.217.157.91 | (null)         | Frank     | (null)   |
| jrice2@icq.com           | 2014-06-01 09:59:10 | (null)              | 1           | 95,76         | 117.4.9.206   | (null)         | Joshua    | (null)   |
| kphillips8@oracle.com    | 2015-01-30 22:49:56 | (null)              | 1           | 57,12         | 220.77.70.87  | (null)         | Kevin     | (null)   |
| lcruz5@techcrunch.com    | 2014-10-27 01:02:46 | (null)              | 1           | 90,45         | 122.38.175.17 | (null)         | Larry     | (null)   |
| scarpenter1@salon.com    | 2012-11-05 07:56:38 | 2014-06-09 21:57:20 | 3           | 163,58        | 220.75.17.164 | 203.81.207.35  | Steven    | Lousie   |

感谢任何帮助!


我问自己的问题:

这可以在 MySQL 中解决吗?在 Excel 中,我可以简单地构建一个数据透视表。 如果不是,我可以通过使用存储过程来解决它,如果订单不超过一个,则遍历所有记录并跳过字段?

【问题讨论】:

【参考方案1】:

我建议使用substring_index()/group_concat() 技巧。它看起来像这样:

SELECT o.entity_id, o.customer_email,
       min(created_at) AS FirstOrder, max(created_at)  AS LastOrder,
       count(*) AS TotalOrders, sum(o.grand_total) AS TotalTurnover,
       substring_index(group_concat(remote_ip order by created at), ',', 1) as first_ip,
       substring_index(group_concat(remote_ip order by created at desc), ',', 1) as last_ip
FROM orders o
GROUP BY o.entity_id, o.customer_email;

这应该适用于合理的数据。 group_concat() 的中间结果的长度有一个可配置的限制。

【讨论】:

解决这个问题的方法很有趣,谢谢你的提示,戈登!我会玩弄它以进一步了解这个技巧!【参考方案2】:

您还可以使用子查询来查找第一个和最后一个订单的行值。这假设第一个和最后一个订单是根据 entity_id。

SELECT 
   customer_email, 
   COUNT(*) AS total_orders, 
   SUM(grand_total) AS total_turnover,
   (SELECT created_at FROM orders WHERE 
       entity_id = MIN(t.entity_id)) AS first_created_at,
   (SELECT created_at FROM orders WHERE 
       entity_id = MAX(t.entity_id)) AS last_created_at,
   (SELECT remote_ip FROM orders WHERE 
       entity_id = MIN(t.entity_id)) AS first_remote_ip,
   (SELECT remote_ip FROM orders WHERE 
       entity_id = MAX(t.entity_id)) AS last_remote_ip,
   (SELECT customer_firstname FROM orders WHERE 
       entity_id = MIN(t.entity_id)) AS first_customer_firstname,
   (SELECT customer_firstname FROM orders WHERE 
       entity_id = MAX(t.entity_id)) AS last_customer_firstname
FROM orders AS t
GROUP BY customer_email

【讨论】:

非常感谢!昨天我一直在朝着这个方向努力,但后来因为我无法让它发挥作用而放弃了这个想法。我会进一步检查您的解决方案。再次感谢您!

以上是关于MySQL 中的 Pivot - 根据日期时间列显示第一个和最后一个值的主要内容,如果未能解决你的问题,请参考以下文章

Pivot 中的日期比较

在 Oracle SQL 中按报告日期的 PIVOT 值

计算 Pivot SQL 中的总天差

Pandas Dataframe to pivot table - 根据前两列的添加创建新列

VBA:根据先前定义的变量过滤数据透视表

mysql使用内部连接根据数据库中的日期范围显示数据