SQL:查找在某个类别中没有支出的客户

Posted

技术标签:

【中文标题】SQL:查找在某个类别中没有支出的客户【英文标题】:SQL: Finding customers with no spend in a category 【发布时间】:2013-06-02 03:31:45 【问题描述】:

我认为我在这方面工作的时间太长了,因为我很难回答一个非常简单的问题:在零售环境中,哪些客户没有在我提供优惠券的类别上花钱?

考虑以下数据:

-- The offer ID and the category for which it is valid.
select * from t_offers
OFFER   CAT_NBR
foo34   34
xxx30   30
baz60   60
bar50   50

-- The customer ID (HH) and their total spending by all
-- categories (not just the ones for which coupons are being offered).
-- PLEASE NOTE that when a customer has zero spend, there will NOT be an
--             entry in this table for that category.
select * from t_catspend
HH  CAT_NBR SPEND
1   30      5
1   60      7
2   34      8

我想要得到的是:对于t_offers 中的每个优惠,每个在该优惠类别中没有支出的客户的HH ID。例如,对于优惠 foo34,我应该得到 HH #1,因为 HH #1 没有显示该类别的任何支出(HH #1 的类别 34 没有条目)。

因此,查找空数据时的第一直觉是外连接。所以我在cat_nbr 上尝试了左连接。但这只会让我有消费的客户;我不知道如何告诉我在该类别中没有消费的客户的 ID。

如果重要的话,这在 Netezza 上。

非常感谢您的任何帮助。

【问题讨论】:

【参考方案1】:
SELECT  a.HH
FROM    t_catspend a
WHERE   NOT EXISTS
        (
            SELECT  null
            FROM    t_offers b
                    INNER JOIN t_catspend c
                        ON c.CAT_NBR = b.CAT_NBR
            WHERE   b.offer = 'foo34' AND
                    a.HH = c.HH
        )
GROUP   BY a.HH
SQLFiddle Demo SQLFiddle Demo (an offer that spends on all HH returns empty)

输出

╔════╗
║ HH ║
╠════╣
║  1 ║
╚════╝

更新

SELECT  b.*, a.*
FROM    t_offers a
        CROSS JOIN (SELECT HH FROM t_catspend GROUP BY HH) b
        LEFT JOIN t_catspend x
          ON    a.CAT_NBR = x.CAT_NBR AND
                b.HH = x.HH
WHERE   x.CAT_NBR IS NULL
        -- AND a.offer = 'foo34'  -- <<== specific OFFER
ORDER   BY b.HH
SQLFiddle Demo

输出

╔════╦═══════╦═════════╗
║ HH ║ OFFER ║ CAT_NBR ║
╠════╬═══════╬═════════╣
║  1 ║ bar50 ║      50 ║
║  1 ║ foo34 ║      34 ║
║  2 ║ baz60 ║      60 ║
║  2 ║ bar50 ║      50 ║
║  2 ║ xxx30 ║      30 ║
╚════╩═══════╩═════════╝

由于您提到您有一个非常大的表,添加复合 INDEX 将导致更快的查询执行。

ALTER TABLE t_catspend ADD INDEX (HH, CAT_NBR)

如果可能,t_catspend.CAT_NBR 必须引用 t_offers.CAT_NBR

希望这会有所帮助。

【讨论】:

这看起来不错!有没有办法让它对所有优惠通用,而不是为 foo34 定制?理想情况下,我会取回每件符合条件的事物的优惠代码和 hh ID(即“符合条件”,即该类别中没有支出)。 我已更新答案以获取所有用户的状态。您需要在此使用CROSS JOIN 将所有报价与所有hh 值配对。无论如何,请不要犹豫。 :)【参考方案2】:

你在找这个吗?

SELECT b.cat_nbr, b.hh 
  FROM
(
 SELECT cat_nbr, hh
   FROM t_offers CROSS JOIN 
 ( 
   SELECT DISTINCT hh FROM t_catspend
 ) a
) b LEFT JOIN t_catspend s
    ON b.cat_nbr = s.cat_nbr AND b.hh = s.hh
 WHERE s.spend IS NULL
 GROUP BY b.cat_nbr, b.hh

基于提供的样本数据的输出:

| CAT_NBR | HH |
----------------
|      30 |  2 |
|      34 |  1 |
|      50 |  1 |
|      50 |  2 |
|      60 |  2 |

这里是SQLFiddle

【讨论】:

你将如何过滤记录foo34 @SkyDrive OP 的确切词 For each offer in t_offers,HH ID for each customer在该优惠类别中的支出。并且可以通过在...) a WHERE cat_nbr = 34 ...之后添加轻松过滤 谢谢,彼得。当我决定跳到 *** 时,我正沿着笛卡尔加入路径走……这很不幸,因为大约有 30MM 客户和一百个左右的类别。不是世界末日,而是大!我会让你知道这是怎么回事。 @Chris 恕我直言,在您的情况下,您必须以一种或另一种方式使用CROSS JOIN。 JW 的更新答案支持它。【参考方案3】:

我认为您在正确的轨道上,可能缺少 WHERE 标准?

SELECT *
FROM t_catspend spend
LEFT JOIN t_offers offers
 ON spend.CAT_NBR = offers.CAT_NBR
WHERE offers.CAT_NBR IS NULL

【讨论】:

【参考方案4】:

不知道这是否有效但有效。

SELECT DISTINCT HH, offer FROM t_offers, t_catspend
WHERE (HH, offer) NOT IN
(SELECT HH, offer FROM t_offers t1, t_catspend t2
WHERE t1.cat_nbr = t2.cat_nbr)

【讨论】:

以上是关于SQL:查找在某个类别中没有支出的客户的主要内容,如果未能解决你的问题,请参考以下文章

SQL 查询查找专属客户

SQL查询,用于在一周中的每一天为项目排序的EACH类别中查找UNITS

查找最佳类别匹配的 SQL 查询

如何在 Oracle SQL 中查找最具体的匹配行

如何在sql中找到客户明智类别中的最大产品ID?

Firebird(如何使用 SQL 查找父类别)