SQL 通过电子邮件字段获得一次性客户

SQL get one time customers by email field

我有一个包含超过 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;

我希望下面的方法能起作用——但我无法测试它,因为你没有提供样本数据。好吧,我添加了一个可用于查询的临时 table 定义....

但是,如果您实际上可以更改数据模型以对下订单的实体使用 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

如您所见,它 returns Dent、Zaphod 和 Marvin 先生,他们在示例数据中都只出现了一次。

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

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 服务器中不起作用,但它在 MySQL 和 SQLite 中起作用。因此,虽然代码不能在不同的 RDBMS 之间移植,但它适用于 这种特殊情况