查询列出在日期之间订购和未在日期之间订购的客户
Query listing customers who ordered between dates and not ordered between dates
我有两个表 customers
其中有很多 orders
.
我想列出在两个日期之间订购但不在以后的日期(之后)订购的客户。我想弄清楚哪些客户没有 return。
我可以编写查询,在其中列出在不同日期之间订购的客户。
SELECT
"customers".*
FROM
"customers"
LEFT JOIN orders ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN '2020/06/05' AND '2020/06/13' )
WHERE
( orders.customer_id IS NOT NULL )
GROUP BY
customers.ID
我也可以写查询,它将给我一个在日期之间没有订购的列表
SELECT
"customers".*
FROM
"customers"
LEFT JOIN orders ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN '2020/06/16' AND '2020/06/18' )
WHERE
( orders.customer_id IS NULL )
GROUP BY
customers.ID
但无法弄清楚如何在一次查询中获得此列表。
我的最后一个查询(不起作用)
SELECT
"customers".*
FROM "customers"
LEFT JOIN "orders" ON "orders"."customer_id" = "customers"."id" AND "orders"."deleted_at" IS NULL
WHERE
-- LAST WEEK
AND (orders.created_at BETWEEN '2020/06/05' AND '2020/06/13' AND orders.customer_id IS NOT NULL)
-- NOT ORDERED TODAY
AND (orders.created_at BETWEEN '2020/06/16' AND '2020/06/18' AND orders.customer_id IS NULL)
GROUP BY customers.id
请试试这个,如果它对你不起作用或者你想要对它的任何部分进行解释,请在评论中告诉我。
with params as (
select '2020/06/05' as r1start, '2020/06/13' as r1end,
'2020/06/16' as r2start, '2020/06/18' as r2end
) ordersinfo as (
SELECT customers.id,
MAX(CASE
WHEN orders.created_at between p.r1start and p.r1end then 1
ELSE 0
END) as r1order,
MAX(CASE
WHEN orders.created_at between p.r2start and p.r2end then 1
ELSE 0
END) as r2order
FROM customers
CROSS JOIN params p
JOIN orders
ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN p.r1start AND p.r2end )
GROUP BY customers.ID
)
select c.*
from ordersinfo o
join customers c on c.id = o.id
where o.r1order = 1 and o.r2order = 0;
PostgreSQL 允许您 combine 查询结果 - 在本例中您似乎在寻找 INTERSECT
。它为您提供了一组匹配这两个查询的记录。您可以直接使用第一个并将其结果与另一个相交,而无需重写任何内容:
SELECT
*
FROM --here goes your first query
(SELECT
"customers".*
FROM
"customers"
LEFT JOIN orders ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN '2020/06/05' AND '2020/06/13' )
WHERE
( orders.customer_id IS NOT NULL )
GROUP BY
customers.ID
) a
INTERSECT --here you put your other query
(SELECT
"customers".*
FROM
"customers"
LEFT JOIN orders ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN '2020/06/16' AND '2020/06/18' )
WHERE
( orders.customer_id IS NULL )
GROUP BY
customers.ID
);
如果您想保留重复项,请将它们与 INTERSECT ALL
合并。
您可以通过多种方式重写您的查询以一次性获得您想要的结果,无需此机制,但可以使用 UNION
、INTERSECT
和 [=15= 组合结果] 在这种情况下是一个非常有用的工具,当您努力想出具有一组条件的单个查询时,它会为您提供所需的结果。值得注意的是,在一定规模上,性能可能会成为比查询优化难度更大的问题。
您可以使用 EXISTS 和 NOT EXISTS 的组合:
select cust.*
from "customers" cust
where
exists
(select null
from orders ord1
where ord1.customer_id = cust.id
and ord1.created_at between date '2020-06-05' and date '2020-06-13'
)
and not exists
( select null
from orders ord2
where ord2.customer_id = cust.id
and ord2.created_at between date '2020-06-16' and date '2020-06-18'
) ;
这应该会很好地执行,特别是如果您有索引订单(customer_id, created_at)。
我有两个表 customers
其中有很多 orders
.
我想列出在两个日期之间订购但不在以后的日期(之后)订购的客户。我想弄清楚哪些客户没有 return。
我可以编写查询,在其中列出在不同日期之间订购的客户。
SELECT
"customers".*
FROM
"customers"
LEFT JOIN orders ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN '2020/06/05' AND '2020/06/13' )
WHERE
( orders.customer_id IS NOT NULL )
GROUP BY
customers.ID
我也可以写查询,它将给我一个在日期之间没有订购的列表
SELECT
"customers".*
FROM
"customers"
LEFT JOIN orders ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN '2020/06/16' AND '2020/06/18' )
WHERE
( orders.customer_id IS NULL )
GROUP BY
customers.ID
但无法弄清楚如何在一次查询中获得此列表。
我的最后一个查询(不起作用)
SELECT
"customers".*
FROM "customers"
LEFT JOIN "orders" ON "orders"."customer_id" = "customers"."id" AND "orders"."deleted_at" IS NULL
WHERE
-- LAST WEEK
AND (orders.created_at BETWEEN '2020/06/05' AND '2020/06/13' AND orders.customer_id IS NOT NULL)
-- NOT ORDERED TODAY
AND (orders.created_at BETWEEN '2020/06/16' AND '2020/06/18' AND orders.customer_id IS NULL)
GROUP BY customers.id
请试试这个,如果它对你不起作用或者你想要对它的任何部分进行解释,请在评论中告诉我。
with params as (
select '2020/06/05' as r1start, '2020/06/13' as r1end,
'2020/06/16' as r2start, '2020/06/18' as r2end
) ordersinfo as (
SELECT customers.id,
MAX(CASE
WHEN orders.created_at between p.r1start and p.r1end then 1
ELSE 0
END) as r1order,
MAX(CASE
WHEN orders.created_at between p.r2start and p.r2end then 1
ELSE 0
END) as r2order
FROM customers
CROSS JOIN params p
JOIN orders
ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN p.r1start AND p.r2end )
GROUP BY customers.ID
)
select c.*
from ordersinfo o
join customers c on c.id = o.id
where o.r1order = 1 and o.r2order = 0;
PostgreSQL 允许您 combine 查询结果 - 在本例中您似乎在寻找 INTERSECT
。它为您提供了一组匹配这两个查询的记录。您可以直接使用第一个并将其结果与另一个相交,而无需重写任何内容:
SELECT
*
FROM --here goes your first query
(SELECT
"customers".*
FROM
"customers"
LEFT JOIN orders ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN '2020/06/05' AND '2020/06/13' )
WHERE
( orders.customer_id IS NOT NULL )
GROUP BY
customers.ID
) a
INTERSECT --here you put your other query
(SELECT
"customers".*
FROM
"customers"
LEFT JOIN orders ON orders.customer_id = customers.ID
AND orders.deleted_at IS NULL
AND ( orders.created_at BETWEEN '2020/06/16' AND '2020/06/18' )
WHERE
( orders.customer_id IS NULL )
GROUP BY
customers.ID
);
如果您想保留重复项,请将它们与 INTERSECT ALL
合并。
您可以通过多种方式重写您的查询以一次性获得您想要的结果,无需此机制,但可以使用 UNION
、INTERSECT
和 [=15= 组合结果] 在这种情况下是一个非常有用的工具,当您努力想出具有一组条件的单个查询时,它会为您提供所需的结果。值得注意的是,在一定规模上,性能可能会成为比查询优化难度更大的问题。
您可以使用 EXISTS 和 NOT EXISTS 的组合:
select cust.*
from "customers" cust
where
exists
(select null
from orders ord1
where ord1.customer_id = cust.id
and ord1.created_at between date '2020-06-05' and date '2020-06-13'
)
and not exists
( select null
from orders ord2
where ord2.customer_id = cust.id
and ord2.created_at between date '2020-06-16' and date '2020-06-18'
) ;
这应该会很好地执行,特别是如果您有索引订单(customer_id, created_at)。