迭代查询开发...
如果您没有product 表,而只有customer_country_product 表,则可以使用内联视图为每个国家/地区创建不同的产品列表。
按国家/地区获取产品...
SELECT ccp.product_id
, ccp.country_id
FROM customer_country_product ccp
GROUP
BY ccp.product_id
, ccp.country_id
我们可以将该查询用作行源,方法是将其设为内联视图。将该查询包装在括号中,分配一个别名,并在另一个查询的 FROM 子句中引用它。为了获得“对”产品,我们可以将内联视图加入到自身中(避免返回相同产品的对(A_A),并避免返回“重复”对(仅返回 A_C 和 @987654326 中的一个) @)。
SELECT a.country_id
, a.product_id AS a_product_id
, b.product_id AS b_product_id
FROM ( SELECT ccpa.product_id
, ccpa.country_id
FROM customer_country_product ccpa
GROUP
BY ccpa.product_id
, ccpa.country_id
) a
JOIN ( SELECT ccpb.product_id
, ccpb.country_id
FROM customer_country_product ccpb
GROUP
BY ccpb.product_id
, ccpb.country_id
) b
ON b.country_id = a.country_id
AND b.product_id > a.product_id
ORDER
BY a.country_id
, a.product_id
, b.product_id
这应该为您提供每个国家/地区的所有产品“配对”。注意:这将省略没有客户拥有该产品的产品。如果我们想要所有可能的产品对,对于每个国家/地区,我们需要写得稍微不同......
SELECT c.country_id
, a.product_id AS a_product_id
, b.product_id AS b_product_id
FROM ( SELECT ccpa.product_id
FROM customer_country_product ccpa
GROUP BY ccpa.product_id
) a
JOIN ( SELECT ccpb.product_id
FROM customer_country_product ccpb
GROUP BY ccpb.product_id
) b
ON b.product_id > a.product_id
CROSS
JOIN ( SELECT ccpc.country_id
FROM customer_country_product ccpc
GROUP BY ccpc.country_id
) c
ORDER
BY c.country_id
, a.product_id
, b.product_id
如果您有 product 和 country 表,则可以将上述查询中的内联视图替换为对这些表的引用。
要获取客户的“计数”,我们可以使用 SELECT 列表中的相关子查询,也可以在 SELECT 列表中执行连接操作和聚合。 (对于连接,如果我们不小心,可能会生成和计算“重复项”。)
获取特定国家/地区拥有特定产品的不同客户的计数
SELECT COUNT(DISTINCT ccp.customer_id) AS cnt_cust
FROM customer_country_product ccp
WHERE ccp.country_id = ?
AND ccp.product_id = ?
获取来自特定国家/地区至少拥有两种特定产品中的一种的不同客户的计数
SELECT COUNT(DISTINCT ccp.customer_id) AS cnt_cust_have_either
FROM customer_country_product ccp
WHERE ccp.country_id = ?
AND ccp.product_id IN ( ? , ? )
要获得在特定国家/地区拥有两种特定产品的客户数量:
SELECT COUNT(DISTINCT ccp1.customer_id) AS cnt_cust_have_both
FROM customer_country_product ccp1
JOIN customer_country_product ccp2
ON ccp2.country_id = ccp1.country_id
AND ccp2.customer_id = ccp1.customer_id
WHERE ccp1.country_id = ?
AND ccp1.product_id = ?
AND ccp2.product_id = ?
由于这些查询返回包含单个列的单行,我们可以将它们用作另一个查询的 SELECT 列表中的表达式。我们从“产品对”查询开始,并添加到 SELECT 列表中。我们将这些问号占位符替换为对外部查询中列的引用:
SELECT c.country_id
, a.product_id AS a_product_id
, b.product_id AS b_product_id
, ( SELECT COUNT(DISTINCT ccp1.customer_id)
FROM customer_country_product ccp1
JOIN customer_country_product ccp2
ON ccp2.country_id = ccp1.country_id
AND ccp2.customer_id = ccp1.customer_id
WHERE ccp1.country_id = c.country_id
AND ccp1.product_id = a.product_id
AND ccp2.product_id = b.product_id
) AS cnt_cust_have_both
, ( SELECT COUNT(DISTINCT ccp.customer_id)
FROM customer_country_product ccp
WHERE ccp.country_id = c.country_id
AND ccp.product_id IN (a.product_id,b.product_id)
) AS cnt_cust_have_either
FROM ( SELECT ccpa.product_id
FROM customer_country_product ccpa
GROUP BY ccpa.product_id
) a
JOIN ( SELECT ccpb.product_id
FROM customer_country_product ccpb
GROUP BY ccpb.product_id
) b
ON b.product_id > a.product_id
CROSS
JOIN ( SELECT ccpc.country_id
FROM customer_country_product ccpc
GROUP BY ccpc.country_id
) c
ORDER
BY c.country_id
, a.product_id
, b.product_id
现在,要计算“百分比”,我们只需要进行除法运算即可。对于 MySQL,“除以零”将返回 NULL。 (如果我们的外部查询只返回我们知道来自该国家/地区的客户拥有其中一种产品的行...即第一个查询返回的结果
SELECT c.country_id
, a.product_id AS a_product_id
, b.product_id AS b_product_id
, ( SELECT COUNT(DISTINCT ccp1.customer_id)
FROM customer_country_product ccp1
JOIN customer_country_product ccp2
ON ccp2.country_id = ccp1.country_id
AND ccp2.customer_id = ccp1.customer_id
WHERE ccp1.country_id = c.country_id
AND ccp1.product_id = a.product_id
AND ccp2.product_id = b.product_id
)
/ ( SELECT COUNT(DISTINCT ccp.customer_id)
FROM customer_country_product ccp
WHERE ccp.country_id = c.country_id
AND ccp.product_id IN (a.product_id,b.product_id)
)
* 100.00 AS percent_cust_have_both
FROM ( SELECT ccpa.product_id
FROM customer_country_product ccpa
GROUP BY ccpa.product_id
) a
JOIN ( SELECT ccpb.product_id
FROM customer_country_product ccpb
GROUP BY ccpb.product_id
) b
ON b.product_id > a.product_id
CROSS
JOIN ( SELECT ccpc.country_id
FROM customer_country_product ccpc
GROUP BY ccpc.country_id
) c
ORDER
BY c.country_id
, a.product_id
, b.product_id
就“扩展”而言,对于任何重要的表,我们都需要有合适的索引可用。特别是对于相关的子查询。这些将为外部查询返回的每一行执行。
当分母中的计数为零时,最后一个查询有可能返回 NULL。我们可以通过将 while 除法运算包装在条件测试中来替换零
IFNULL( <expr> , 0) * 100.00 AS
(可能在这些查询中某处存在错误、缺少括号、无效引用、错误的限定符等。这些查询未经测试。我强烈建议您测试每一个,而不仅仅是抓住最后一个。)
跟进
用于测试的表...
CREATE TABLE customer_country_product
( customer_id INT
, country_id VARCHAR(2)
, product_id VARCHAR(2)
)
;
INSERT INTO customer_country_product (customer_id, country_id, product_id) VALUES
('1','US','A')
,('1','US','B')
,('2','CA','A')
,('2','CA','C')
,('3','US','A')
,('3','US','C')
,('4','US','B')
,('5','US','A')
;
最终查询返回:
country_id a_product_id b_product_id percent_cust_have_both
---------- ------------ ------------ ----------------------
CA A B 0.000000
CA A C 100.000000
CA B C 0.000000
US A B 25.000000
US A C 33.333333
US B C 0.000000
将a.product_id 和b.product_id 连接到一个列中将是一个微不足道的更改。 SELECT 列表中的第二列和第三列可以替换为 CONCAT(a.product_id,'_',b.product_id) AS a_b 之类的内容。