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 之间移植,但它适用于 这种特殊情况。
我有一个包含超过 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 之间移植,但它适用于 这种特殊情况。