# Count(Distinct)
假如一个表数据分布如下。
| **ds（分区）** | **cnt（记录数）** |
|:---|:---|
| 20220416   | 73025514    |
| 20220415 | 2292806      |
| 20220417  | 2319160   |
   
使用下面的语句就容易发生数据倾斜：
```
SELECT
    ds,
    COUNT(DISTINCT shop_id) AS cnt
FROM
    demo_data0
GROUP BY
    ds;
```
解决方案如下：
| **序号** | **方案**    | **说明**                                          |
|:---|:---|:---|
| 方案一    | 通用两阶段聚合 | 在partition字段值拼接随机数。                             |
| 方案二    | 类似两阶段聚合 | 先通过Group By两分组字段(ds+shop_id)再使用Count(Distinct)。 |
   
方案一：通用两阶段聚合。
若shop_id字段数据分布均匀，可先按ds、shop_id两字段分组，再对ds做 Count(Distinct)；若shop_id字段数据不均匀（存在热点值），简单的两阶段聚合仍会倾斜，需在分区字段值中拼接随机数打散热点，具体见以下方式1、方式2。
```
-- 方式1：拼接随机数 CONCAT(ROUND(rand(),1)*10,'_', ds) AS rand_ds
SELECT  split_part(rand_ds, '_', 2) AS ds
       ,COUNT(*) AS id_cnt
FROM (
    SELECT  rand_ds, shop_id
    FROM    demo_data0
    GROUP BY rand_ds, shop_id
)
GROUP BY split_part(rand_ds, '_', 2);
-- 方式2：新增随机数字段 round(rand()*10) AS randint10
SELECT  ds
       ,COUNT(*) AS id_cnt
FROM (
    SELECT  ds, randint10, shop_id
    FROM    demo_data0
    GROUP BY ds, randint10, shop_id
)
GROUP BY ds;
```
方案二：类似两阶段聚合。
如果GroupBy与Distinct的字段数据都均匀，则可以采用如下方式优化，先Group By两分组字段（ds和shop_id）再使用Count(Distinct)命令。
```
SELECT  ds
       ,COUNT(*) AS cnt
FROM (
    SELECT  ds, shop_id
    FROM    demo_data0
    GROUP BY ds, shop_id
)
GROUP BY ds;
```
