【问题标题】:SQL get one time customers by email fieldSQL 通过电子邮件字段获取一次性客户
【发布时间】:2017-03-09 11:01:12
【问题描述】:

我有一个包含超过 100,000 条记录的数据库。我试图让所有只订购一次的客户通过客户的电子邮件字段 (OrderEmail) 进行搜索。

SQL 查询运行了 10 分钟,然后超时。

如果我使用较短的日期范围,我可以获得结果,但仍需要 3 分钟以上。

如何优化语法以使其正常工作?

SELECT 
    tblOrders.OrderID,
    tblOrders.OrderName,
    tblOrders.OrderEmail,
    tblOrders.OrderPhone,
    tblOrders.OrderCountry,
    tblOrders.OrderDate
FROM
    tblOrders
LEFT JOIN tblOrders AS orders_join ON orders_join.OrderEmail = tblOrders.OrderEmail
    AND NOT orders_join.OrderID = tblOrders.OrderID
WHERE
    orders_join.OrderID IS NULL
    AND (tblOrders.OrderDate BETWEEN '2015-01-01' AND '2017-03-01')
    AND tblOrders.OrderDelivered = - 1
ORDER BY
    tblOrders.OrderID ASC;

【问题讨论】:

    标签: mysql sql timeout


    【解决方案1】:

    我希望以下内容能够正常工作 - 但由于您不提供示例数据,因此我无法对其进行测试。好吧,我添加了一个可用于查询的临时表定义......

    但是,如果您实际上可以更改数据模型以对下订单的实体使用 INTEGER id(而不是 VARCHAR() 电子邮件地址),您会变得更快。

    CREATE TEMPORARY TABLE IF NOT EXISTS
    tblorders(orderid,ordername,orderemail,orderphone,ordercountry,orderdate) AS (
                SELECT  1,'ORD01','adent@hog.com' ,'9-991' ,'UK', DATE '2017-01-01'
      UNION ALL SELECT  2,'ORD02','tricia@hog.com','9-992' ,'UK', DATE '2017-01-02'
      UNION ALL SELECT  3,'ORD03','ford@hog.com'  ,'9-993' ,'UK', DATE '2017-01-03'
      UNION ALL SELECT  4,'ORD04','zaphod@hog.com','9-9943','UK', DATE '2017-01-04'
      UNION ALL SELECT  5,'ORD05','marvin@hog.com','9-9942','UK', DATE '2017-01-05'
      UNION ALL SELECT  6,'ORD06','ford@hog.com'  ,'9-993' ,'UK', DATE '2017-01-06'
      UNION ALL SELECT  7,'ORD07','tricia@hog.com','9-992' ,'UK', DATE '2017-01-07'
      UNION ALL SELECT  8,'ORD08','benji@hog.com' ,'9-995' ,'UK', DATE '2017-01-08'
      UNION ALL SELECT  9,'ORD09','benji@hog.com' ,'9-995' ,'UK', DATE '2017-01-09'
      UNION ALL SELECT 10,'ORD10','ford@hog.com'  ,'9-993' ,'UK', DATE '2017-01-10'
    )
    ;
    
    SELECT 
      tblOrders.OrderID
    , tblOrders.OrderName
    , tblOrders.OrderEmail
    , tblOrders.OrderPhone
    , tblOrders.OrderCountry
    , tblOrders.OrderDate
    FROM tblOrders
    JOIN (
      SELECT
          OrderEmail
        FROM tblOrders
        GROUP BY
          OrderEmail
        HAVING COUNT(*) = 1
    ) singleOrders
    ON singleOrders.OrderEmail = tblOrders.OrderEmail
        ORDER BY OrderID
    ;
    
    OrderID|OrderName|OrderEmail    |OrderPhone|OrderCountry|OrderDate
          1|ORD01    |adent@hog.com |9-991     |UK          |2017-01-01
          4|ORD04    |zaphod@hog.com|9-9943    |UK          |2017-01-04
          5|ORD05    |marvin@hog.com|9-9942    |UK          |2017-01-05
    

    如您所见,它返回 Mr. Dent、Zaphod 和 Marvin,他们都在示例数据中只出现过一次。

    【讨论】:

    • 像魅力一样工作!谢谢!
    【解决方案2】:

    另一种可能有效的方法是,您按电子邮件地址分组并只获取具有一个条目的那些。如果您想获得具有多个订单的客户,它的行为可能无法预测,但对于这种特殊情况应该没问题:

    SELECT 
        tblOrders.OrderID,
        tblOrders.OrderName,
        tblOrders.OrderEmail,
        tblOrders.OrderPhone,
        tblOrders.OrderCountry,
        tblOrders.OrderDate,
        count(tblOrders.OrderID) as OrderCount
    FROM
        tblOrders
    WHERE
        tblOrders.OrderDate BETWEEN '2015-01-01' AND '2017-03-01'
        AND tblOrders.OrderDelivered = - 1
    GROUP BY
        tblOrders.OrderEmail
    HAVING
        OrderCount = 1
    ORDER BY
        tblOrders.OrderID ASC;
    

    另外,我怀疑如果您看到这么长的查询时间只有 10 万条记录,您可能在 OrderEmail 列上没有索引 - 我建议设置它,这也可能有助于您的原始查询.

    这在 Oracle 或 SQL Server 中不起作用,但在 MySQL 和 SQLite 中起作用。因此,虽然代码不能在不同的 RDBMS 之间移植,但它适用于这种特殊情况

    【讨论】:

    • 这行不通。您在 select 中放置的所有非聚合列也必须在 group by 子句中。
    • @Conffusion 它可能不适用于正确的 SQL,但它确实适用于具有众多特质的 MySQL。我测试了一个类似的查询。它将按该列分组,但它会返回未分组列的第一条记录中的数据,这是您在这种特殊情况下所需要的。
    • pilsetnieks,感谢您的澄清;今天学到了一些新东西+1
    猜你喜欢
    • 1970-01-01
    • 2015-03-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 2018-11-28
    • 2018-11-02
    • 1970-01-01
    相关资源
    最近更新 更多