You need to enable JavaScript to run this app.
导航
BitMap64
最近更新时间:2024.10.30 17:14:54首次发布时间:2024.10.30 17:14:54

ByteHouse bitmap和社区的差异

ClickHouse社区提供了 bitmap 数据处理能力,详见Bitmap Functions | ClickHouse Docs 在差不多同期,ByteHouse 也自研了一套处理 bitmap 数据的类型和相关函数。它们两者的异同如下:
相同点:

  • 底层均使用 RoaringBitmap 作为 bitmap 数据的存储结构;
  • ClickHouse 社区官网上的普通函数,ByteHouse 做了兼容,也能够使用;
  • bitmap数据结构天然去重,且数据按照从小到大有序存储。

差异点:

  • 用户类型不一样。
    • ClickHouse 社区给用户提供的类型是AggregateFunction(groupBitmap, UIntX),X 可以是 8,16,32,64,代表上游数据的位长;
    • ByteHouse给用户提供的类型是BitMap64,默认选择 64 位整型值。
  • 使用方法上的差异。
    • ByteHouse 中的 BitMap64比较独立,可以在 insert 中直接插入,可以通过 select 直接打印;
    • ByteHouse 中的 BitMap64 提供了更多的高阶函数,以更直观和简洁的方式书写复杂SQL。

定义ByteHouse BitMap64类型

你可以使用类型关键字BitMap64在表中定义 bitmap 类型。示例如下:

CREATE TABLE test.bitmap_test (
    `p_date` Date, 
    `slice_id` UInt64,
    `tag_id` Int32,
    `tag_value` String, 
    `uids` BitMap64
) ENGINE = MergeTree 
PARTITION BY p_date
ORDER BY (tag_id, tag_value)
SETTINGS index_granularity = 128;

创建ByteHouse BitMap64类型数据

bitmap目前有两种构造函数:使用 bitmapFromColumn 函数从一列数据构造,亦或使用 arrayToBitmap 从一个数组构造。示例如下:

select bitmapFromColumn(number) as rbm, toTypeName(rbm) from numbers(10);
-- {0,1,2,3,4,5,6,7,8,9}, "BitMap64"

select arrayToBitmap(groupArray(number)) as rbm, toTypeName(rbm) from numbers(10);
-- {0,1,2,3,4,5,6,7,8,9}, "BitMap64"

ByteHouse BitMap64 类型的数据写入

本节分为外部系统写入 ByteHouse表,和 ByteHouse 内部写入两部分。

外部系统写入

目前主要是通过文本格式(CSV, JSONEachRow)和 Parquet 格式的文件进行导入。
在文本文件中,可以用{}包裹集合数据,也可以用[]包裹集合数据,引擎自动解析;
在 Parquet 中,可以用 LIST来存储集合数据,引擎自动转换。
CSV 格式的文件导入如下:

cat ./test_bitmap_input.csv
--
"2024-01-01",0,11,"a","{0,1,2,3,4,5,6,7,8,9}"


insert into test.bitmap_test format CSV infile 'test_bitmap_input.csv';

Parquet 格式的文件导入如下:

parquet-meta ./test_bitmap_input.parquet
---
file schema: schema
-------------------------------------------------
p_date:      REQUIRED INT32 O:UINT_16 R:0 D:0
slice_id:    REQUIRED INT64 O:UINT_64 R:0 D:0
tag_id:      REQUIRED INT32 R:0 D:0
tag_value:   REQUIRED BINARY O:UTF8 R:0 D:0
uids:        REQUIRED F:1
.list:       REPEATED F:1
..element:   OPTIONAL INT64 O:UINT_64 R:1 D:2
 
INSERT INTO test.bitmap_test FORMAT Parquet INFILE 'test_bitmap_input.parquet'

ByteHouse内部写入

这种通常是从一张表计算得到 bitmap 后写入目标表,通常使用insert select实现,此时可能需要结合【创建 BitMap64 类型数据】中的构造函数一同使用。
示例如下:

  • 从 bitmap表计算之后写回表中
INSERT INTO test.bitmap_test SELECT
    p_date,
    slice_id,
    100 AS tag_id,
    'test2' as tag_value,
    uids
FROM
(
    SELECT p_date, slice_id, subBitmap(uids, 1, 2) AS uids
    FROM test.bitmap_test
    WHERE tag_id = 1 and tag_value = 't1'
);
  • 从一列数据构建 bitmap 写入表中
INSERT INTO test.bitmap_test SELECT
    '2024-01-01' as p_date,
    1 as slice_id
    101 AS tag_id,
    'test2' as tag_value,
    bitmapFromColumn(number) as uids
FROM numbers(10)
  • 少部分直接通过insert values 显示写入
INSERT INTO test.bitmap_test values ('2024-01-01', 1, 102, 'test3', [1,2,3,4]);

ByteHouse BitMap64类型的查询

直接查询

ByteHouse BitMap64 类型是可以直接通过 select 查询并展示的。不顾需要注意当 bitmap 元素个数比较大(比方说数万个),打印到屏幕上耗时较大,不建议直接打印。

SELECT
    tag_id,
    uids
FROM test.bitmap_test
WHERE tag_id = 101

如果 bitmap元素个数比较多,建议打印出元素个数进行查看。

SELECT
    tag_id,
    bitmapCardinality(uids)
FROM test.bitmap_test
WHERE tag_id = 101

使用 jdbc-driver

请参考ByteHouse BitMap64在 jdbc中的用法提供的用法。

普通函数

函数接收一行记录或者一个BitMap64对象,返回一行结果

序号

函数接口

参数说明

返回值

功能释义

1

arrayToBitmap([x1, …])
-> bitmap

UIntN类型的数组
N={8, 16, 32, 64}

BitMap64对象

通过一个数组构造一个bitmap对象

2

bitmapToArray(bitmap)
-> [x1,...]

一个BitMap64对象

UInt64数组

将一个bimap对象转换为数组格式

3

bitmapAnd(bitmap1, bitmap2)
-> bitmap

两个BitMap64对象

BitMap64对象

bitmap的运算

4

bitmapOr(bitmap1, bitmap2)
-> bitmap

两个BitMap64对象

BitMap64对象

bitmap的运算

5

bitmapXor(bitmap1, bitmap2)
-> bitmap

两个BitMap64对象

BitMap64对象

bitmap的异或运算

6

bitmapAndnot(bitmap1, bitmap2)
-> bitmap

两个BitMap64对象

BitMap64对象

bitmap的运算

7

bitmapCardinality(bitmap)
-> integer

一个BitMap64对象

UInt64数值

bitmap中元素的个数

8

bitmapMin(bitmap)
-> integer

一个BitMap64对象

UInt64数值

bitmap中最小的元素

9

bitmapMax(bitmap)
-> integer

一个BitMap64对象

UInt64数值

bitmap中最大的元素

10

bitmapAndCardinality
(bitmap1, bitmap2)
-> integer

两个BitMap64对象

UInt64数值

bitmap的运算之后的元素个数

11

bitmapOrCardinality
(bitmap1, bitmap2)
-> integer

两个BitMap64对象

UInt64数值

bitmap的运算之后的元素个数

12

bitmapXorCardinality
(bitmap1, bitmap2)
-> integer

两个BitMap64对象

UInt64数值

bitmap的异或运算之后的元素个数

13

bitmapAndnotCardinality
(bitmap1, bitmap2)
-> integer

两个BitMap64对象

UInt64数值

bitmap的运算之后的元素个数

14

bitmapContains(bitmap, integer)
-> bool

一个BitMap64对象
UIntN类型的数字,
N={8, 16, 32, 64}

UInt8枚举
1->true
0->false

检查bitmap中是否包含指定元素

15

bitmapHasAll
(bitmap, sub_bitmap)
-> bool

两个BitMap64对象

UInt8枚举
1->true
0->false

检查sub_bitmap是否是bitmap的子集
(相等也返回1)

16

bitmapHasAny
(bitmap1, bitmap2)
-> bool

两个BitMap64对象

UInt8枚举
1->true
0->false

检查两个bitmap是否存在交集

17

bitmapSubsetInRange
(bitmap, range_start, range_end)
-> bitmap

一个BitMap64对象,两个UIntN数字,标识取值范围
N={8, 16, 32, 64}

BitMap64对象

检查并返回bitmap中符合给定元素大小范围的值组成的bitmap。
函数过滤的是数据,返回的 bitmap中元素均大于等于 range_start,且小于 range_end。

18

bitmapSubsetLimit
(bitmap, range_start, cardinality_limit)
-> bitmap

一个BitMap64对象,两个UIntN数字,标识下标启示范围,和子集大小

BitMap64对象

检查并返回由bitmap中指定元素开始的,不超过指定数量的元素组成的bitmap。
函数过滤的是数据,返回的 bitmap 中元素聚大于等于 range_start,且总共的元素个数不超过 cardinality_limit。

19

subBitmap(bitmap, start_offset, length)

一个BitMap64对象,
两个UIntN类型的数字,
N={8, 16, 32, 64}

BitMap64对象

提取bitmap中指定下标范围的数据,类比subString。
需要注意,start_offset从0开始,代表实际存储的第一个有效值,类似于数组下标。start_offset和 length 超过 bitmap 元素个数均无意义。

这些函数主要是对ClickHouse社区相关函数的兼容,详见:
https://clickhouse.tech/docs/en/sql-reference/functions/bitmap-functions/
使用示例见【函数示例章节】。

聚合函数

普通聚合函数

函数接收多行记录,进行聚合操作,返回一行结果

序号

函数接口

参数说明

返回值

功能释义

41

bitmapColumnAnd
(bitmap_column)
-> bitmap

BitMap64类型的一列数据

BitMap64对象

接收一个bimap列,该列所有bitmap做运算

42

bitmapColumnOr
(bitmap_column)
-> bitmap

BitMap64类型的一列数据

BitMap64对象

接收一个bimap列,该列所有bitmap做运算

43

bitmapColumnXor
(bitmap_column)
-> bitmap

BitMap64类型的一列数据

BitMap64对象

接收一个bimap列,该列所有bitmap做异或运算

44

bitmapColumnCardinality
(bitmap_column)
-> UInt64

BitMap64类型的一列数据

UInt64数值

接收一个bimap列,该列所有bitmap做运算,返回最终结果bitmap的元素个数

45

bitmapColumnHas
(bitmap_column, integer)
-> bool

BitMap64类型的一列数据
UIntN类型的数字,
N={8, 16, 32, 64}

UInt8枚举
1->true
0->false

接收一个bimap列,检查该列是否包含指定元素

46

bitmapFromColumn(id) -> bitmap

整数列
UIntN类型的数字,
N={8, 16, 32, 64}

BitMap64对象

接收一列整数,聚合成一个bitmap

高阶聚合函数

试想一个场景:有若干个 bitmap 对象,如何以给定的方式进行计算呢?比方说第一个和第二个求并集,再和第三个求交集。。。 你可能想到了通过 JOIN 和上文中的【普通函数】进行两两计算。如果有100 甚至 1000 个这样的 bitmap,那可能 JOIN 会有 100 个、1000 个,此时 SQL复杂性,可读性,包括计算性能会变得非常差。有没更直观和简介的方法来实现这个需求呢?就是接下来要介绍的高阶聚合函数。
ByteHouse 提供了一系列聚合函数用于批量处理大量 bitmap 之间的复杂计算。通过给每个待计算的 bitmap编序号,并用序号书写表达出集合的交并差运算,ByteHouse 可以根据指定的运算表达式完成对应的 bitmap 运算,直接把计算可视化和简便化,极大提升了写 SQL的体验!
简单图示如下:

带表达式的高阶聚合函数

这种函数有两个括号传递参数:
第一个括号称为parameter,用于控制聚合函数的行为 (控制流)
第二个括号称为argument,用于向聚合函数传递输入数据列 (数据流)

说明

这部分高阶函数并不复杂,但是文字解释函数较为困难,可以结合上图,以及后文详细的【函数示例】章节

序号

函数接口

参数说明

返回值

功能释义

61

bitmapCount
(expression)
(idx, bitmap)
-> integer

expression是与或差表达式,&表示交集,|表示并集,~表示差集
idx是每行bitmap对应的标记,使用正整数Int16/32类型

UInt64数值

对于bitmap列中的每一行,取其标记idx,存入 map 中。最终依据expression指定的计算方式进行bitmap运算,返回最终bitmap中元素个数

62

bitmapExtract
(idx, bitmap)
-> bitmap

expression是与或差表达式,&表示交集,|表示并集,~表示差集
idx是每行bitmap对应的标记,使用正整数Int16/32类型

BitMap64对象

对于bitmap列中的每一行,取其idx,并依据expression指定的计算方式进行bitmap运算,返回最终计算结果的bitmap

63

bitmapMultiCount
(expr1, expr2, ...)
(idx, bitmap)
-> [int1, int2, ...]

expression是与或差表达式,&表示交集,|表示并集,~表示差集
idx是每行bitmap对应的标记,使用正整数Int16/32类型

UInt64数组

对于bitmap列中的每一行,取其编号 idx,并依据每个expr指定的计算方式进行多组bitmap运算,返回每组expr计算的结果bitmap中元素个数

64

bitmapMultiExtract
(expr1, expr2, ...)
(idx, bitmap)
-> [bitmap1, bitmap2, ...]

expression是与或差表达式,&表示交集,|表示并集,~表示差集
idx是每行bitmap对应的标记,使用正整数Int16/32类型

BitMap64数组

对于bitmap列中的每一行,取其编号 idx,并依据每个expr指定的计算方式进行多组bitmap运算,返回每组expr计算的结果的bitmap数据

65

bitmapMultiCountWithDate
(expr1, expr2, ...)
(date, idx, bitmap)
-> [int1, int2, ...]

expression是与或差表达式,&表示交集,|表示并集,~表示差集
date是Int64类型的日期格式
idx是每行bitmap对应的标记,使用正整数Int16/32类型

UInt64数组

在bitmapMultiCount的基础上增加了日期维度,可以区别出不同日期中的编号。即 bitmapMultiCount 只支持一个编号维度,该函数则额外增加了日期维度。

66

bitmapMultiExtractWithDate
(expr1, expr2, ...)
(date, idx, bitmap)
-> [bitmap1, bitmap2, ...]

注:vanilla-1.5新增,低版本无

expression是与或差表达式,&表示交集,|表示并集,~表示差集
date是Int64类型的日期格式
idx是每行bitmap对应的标记,使用正整数Int16/32类型

BitMap64数组

在bitmapMultiExtract的基础上增加了日期维度,可以区别出不同日期中的标签。即 bitmapMultiExtract 只支持一个编号维度,该函数则额外增加了日期维度。

Tips:

  • 如果存在相同编号idx的多行数据,聚合函数会自动把多行bitmap通过OR运算合并为一个,参与计算。最终计算时,一个idx对应一个bitmap。
  • 搜索函数名可在本文中找到示例。

函数使用示例

建测试表

示例用表 test.bitmap_test,schema 详见【定义 ByteHouse BitMap64 类型】一节。

写入数据

数据涉及两个标签,tag_id=11,有两个标签值'a'和'b';tag_id=12,有一个标签值'c'。
每个标签值有两行,其 slice_id 不一样。slice_id 是为了把大 bitmap 进行切分,减小每行 bitmap量级,可优化构建效率和内存使用量,同时也能够为分布式存储提供分片凭据。

insert into test.bitmap_test values ('2024-01-01', 0, 11, 'a', [1,2,3,4,5]), ('2024-01-02', 1, 11,'a', [11,12,13,14,15]);
insert into test.bitmap_test values ('2024-01-01', 0, 11, 'b', [3,4,5,6,7,8]), ('2024-01-02', 1, 11,'b', [13, 14,15,16]);
insert into test.bitmap_test values ('2024-01-01', 0, 12, 'c', [3,4,6,7,9]), ('2024-01-02', 1, 12,'c', [13,15,16,18]);

普通函数

普通函数的使用和社区demo,以及其它函数并无差异。仅举几个例子。

1. 数组转bitmap

SELECT arrayToBitmap([1, 2, 3, 4, 5])
FORMAT TSV

-- 结果:
{1,2,3,4,5}

2. bitmap转数组

SELECT bitmapToArray(uids)
FROM test.bitmap_test
WHERE tag_value = 'a'
FORMAT TSV

-- 结果:
[11,12,13,14,15]
[1,2,3,4,5]


SELECT bitmapToArray(arrayToBitmap([1, 2, 3, 4, 5]))
FORMAT TSV

-- 结果:
[1,2,3,4,5]

3. 求bitmap中的元素个数

SELECT bitmapCardinality(arrayToBitmap([1, 2, 3, 4, 5]))
FORMAT TSV

-- 结果:
5

聚合函数

1-5. 多个bitmap的交/并/异或运算

-- 求两个 bitmap 的交集
SELECT bitmapColumnAnd(uids)
FROM test.bitmap_test
WHERE (slice_id = 0) AND (tag_value IN ('a', 'b'))
FORMAT TSV

-- 结果:
{3,4,5}

-- 求两个 bitmap 的并集(两个函数同义)
SELECT
    bitmapCardinality(bitmapColumnOr(uids)),
    bitmapColumnCardinality(uids)
FROM test.bitmap_test
WHERE (slice_id = 0) AND (tag_value IN ('a', 'b'))
FORMAT TSV

-- 结果:
8        8

-- 判断某个 bitmap中是否存在给定元素
SELECT bitmapColumnHas(uids, 12)
FROM test.bitmap_test
WHERE tag_value = 'a'
FORMAT TSV

-- 结果:
1

6. 聚合一个bitmap

SELECT bitmapFromColumn(number)
FROM
(
    SELECT number
    FROM system.numbers
    LIMIT 100, 10
)
FORMAT TSV

-- 结果
{100,101,102,103,104,105,106,107,108,109}

7. bitmapCount函数

**用途:**实现多个bitmap之间的复杂交并差运算。用户通过输入表达式表示出 bitmap 的运算,函数解析后进行对应计算。示例参考上文【聚合函数】- 【高阶聚合函数】小节的图例。
**输入:**编号列,bitmap列。编号列用于标识 bitmap,可以是表中字段(如 tag_id),也可以是人为定义变量(如 idx)。
**表达式:**一个编号锚定一个 bitmap。编号表达式是与或差操作,对应 bitmap的交并差运算。
支持的编号运算符:

  • 或操作:使用逗号('1,2')或者竖线('1|2')表示
  • 与操作:使用&表示('1&2')
  • 差集操作:使用表示('12')

**输出:**bitmap 交并差运算之后的结果 bitmap 中的元素个数。

Case 1: 求某个标签'a'总共元素个数:

SELECT bitmapColumnCardinality(uids)
FROM test.bitmap_test
WHERE tag_value = 'a'
FORMAT TSV

-- 结果:
10

-- 求标签'a'总共元素个数。等同于bitmapColumnCardinality的计算
SELECT bitmapCount('1')(If(tag_value = 'a', toInt32(1), toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
WHERE tag_value = 'a'
FORMAT TSV

-- 结果:
10

-- 求标签'a'总共元素个数,标签为字符串,可以直接用字符串表达式
SELECT bitmapCount('a')(tag_value, uids)
FROM test.bitmap_test
WHERE tag_value = 'a'
FORMAT TSV

-- 结果:
10

Case 2: 求两个标签的交集'a'和'b'

SELECT bitmapCardinality(bitmapColumnAnd(uids)) AS card
FROM test.bitmap_test
WHERE (tag_value IN ('a', 'b')) AND (slice_id = 0)
FORMAT CSV

-- 结果:
10

-- 等同于bitmapCardinality(bitmapColumnAnd)的计算
SELECT bitmapCount('1&2')(multiIf(tag_value = 'a', toInt32(1), tag_value = 'b', 2, toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
WHERE (tag_value IN ('a', 'b')) AND (slice_id = 0)
FORMAT TSV

-- 结果:
3

-- 上述sql的另一种写法,适合复杂sql嵌套
SELECT bitmapCount('1&2')(idx, uids)
FROM
(
    SELECT
        toInt32(1) AS idx,
        uids
    FROM test.bitmap_test
    WHERE (slice_id = 0) AND (tag_value IN ('a'))
    UNION ALL
    SELECT
        toInt32(2) AS idx,
        uids
    FROM test.bitmap_test
    WHERE (slice_id = 0) AND (tag_value IN ('b'))
)
FORMAT TSV

-- 结果:
3

Case 3: 更多标签的交并差运算

-- 写法一
SELECT bitmapCount('1&2|3')(multiIf(tag_value = 'a', toInt32(1), tag_value = 'b', toInt32(2), tag_value = 'c', toInt32(3), toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
FORMAT TSV

-- 结果:
11

-- 写法二:刚好 tag_value是字符串类型,可以使用字符串表达式
SELECT bitmapCount('a&b|c')(tag_value, uids)
FROM test.bitmap_test
FORMAT TSV

-- 结果:
11

8.bitmapExtract

这个函数计算方法和bitmapCount是一样的,区别是它返回计算结果的bitmap, 不是个数。
至于怎么组合标签,等同于bitmapCount函数。bitmapCount 那里使用的是 tag_value 作为示例,这里展示一个 tag_id 作为示例的 case: 求两个标签的交集:

SELECT bitmapExtract('1&2')(multiIf(tag_id = 11, toInt32(1), tag_id = 12, 2, toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
WHERE (tag_id IN (1,2)) AND (slice_id = 0)
FORMAT TSV

-- 结果(没有交集):
{}

另一种写法:
SELECT bitmapExtract('11&12')(tag_id, uids)
FROM test.bitmap_test
WHERE (tag_id IN (1, 2)) AND (slice_id = 0)
FORMAT TSV

-- 结果(没有交集):
{}

9. bitmapMultiCount函数

这个聚合函数再bitmapCount的基础上,支持了多组表达式
**用途:**实现多个bitmap之间的复杂交并差运算,同时支持了多组运算和运算复用。用户通过输入表达式表示出 bitmap 的运算,函数解析后进行对应计算。示例参考上文【聚合函数】- 【高阶聚合函数】小节的图例。
**输入:**编号列,bitmap列。编号列用于标识 bitmap,可以是表中字段(如 tag_id),也可以是人为定义变量(如 idx)。
**表达式:**一个编号锚定一个 bitmap。编号表达式是与或差操作,对应 bitmap的交并差运算。同时可以提供多组表达式,函数依次执行对应计算,把多组结果一并返回。
支持的编号运算符:

  • 或操作:使用逗号('1,2')或者竖线('1|2')表示
  • 与操作:使用&表示('1&2')
  • 差集操作:使用表示('12')
  • 表达式复用:通过下划线+位置数字的方式复用某一个表达式的结果。如"_3"表示复用第三个表达式(从一开始数)

**输出:**bitmap 交并差运算之后的结果 bitmap 中的元素个数。每个表达式对应一个结果,存储在数组中。

-- '_3|3'等价于 '(1&2)|3'
SELECT bitmapMultiCount('1', '2', '1&2', '_3|3')(multiIf(tag_value = 'a', toInt32(1), tag_value = 'b', toInt32(2), tag_value = 'c', toInt32(3), toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
WHERE slice_id = 0
FORMAT TSV

-- 结果:
[5,6,3,6]

-- tag_value 刚好是字符串类型,所以也可以使用字符串表达式
SELECT bitmapMultiCount('a', 'b', 'a&b', '_3|c')(tag_value, uids) AS card
FROM test.bitmap_test
WHERE slice_id = 0
FORMAT TSV

-- 结果:
[5,6,3,6]

_3表示复用了位置3处的表达式,即'1&2'。
有一组集合关系可以验证是否正确。该关系是
A + B = AUB + A交B

SELECT bitmapMultiCount('1', '2', '1&2', '1|2')(multiIf(tag_value = 'a', toInt32(1), tag_value = 'b', toInt32(2), tag_value = 'c', toInt32(3), toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
WHERE slice_id = 0
FORMAT TSV

-- 结果:
[5,6,3,8]

-- tag_value 刚好是字符串类型,所以也可以使用字符串表达式
SELECT bitmapMultiCount('a', 'b', 'a&b', 'a|b')(tag_value, uids)
FROM test.bitmap_test
WHERE slice_id = 0
FORMAT TSV

-- 结果:
[5,6,3,8]

10. bitmapMultiExtract

含义用户同bitmapMultiCount,差异点在返回值,返回 bitmap 数据。

SELECT bitmapMultiExtract('1', '2', '1&2', '1|2')(multiIf(tag_value = 'a', toInt32(1), tag_value = 'b', toInt32(2), tag_value = 'c', toInt32(3), toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
WHERE slice_id = 0
FORMAT TSV

[{1,2,3,4,5},{3,4,5,6,7,8},{3,4,5},{1,2,3,4,5,6,7,8}]

-- tag_value 刚好是字符串类型,所以也可以使用字符串表达式
SELECT bitmapMultiExtract('a', 'b', 'a&b', 'a|b')(tag_value, uids)
FROM test.bitmap_test
WHERE slice_id = 0
FORMAT TSV

-- 结果:
[{1,2,3,4,5},{3,4,5,6,7,8},{3,4,5},{1,2,3,4,5,6,7,8}]

11. bitmapMultiCountWithDate

**用途:**这个函数在bitmapMultiCount的基础上又加入了日期维度,可以在表达式中区别不同日期的标签。
注意日期列需要是 Int64 类型,一般日期用 Date类型,需要使用 toYYYYMMDD 函数进行转换。
**输入:**日期列,编号列,bitmap 列。
**表达式:**一个日期和一个编号组合锚定一个 bitmap,无法单独使用。所以日期_编号作为整体使用,如示例中的'20240101_1' 才能标识一个 bitmap。上文的函数bitmapMultiCount只需要'1'就可以,这是两者的差异点。
**输出:**bitmap 交并差运算之后的结果 bitmap 中的元素个数。每个表达式对应一个结果,存储在数组中。

SELECT bitmapMultiCountWithDate('20240101_1', '20240102_2', '20240101_1|20240102_2')(CAST(toYYYYMMDD(p_date), 'Int64') AS date1, multiIf((tag_value = 'a') AND (p_date = '2024-01-01'), toInt64(1), (tag_value = 'b') AND (p_date = '2024-01-02'), toInt64(2), (tag_value = 'c') and (p_date = '2024-01-01'), toInt64(3),toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
FORMAT TSV

-- 结果:
[5,4,9]

-- tag_value刚好是字符串类型,所以可以使用字符串表达式
SELECT bitmapMultiCountWithDate('20240101_a', '20240102_b', '20240101_a|20240102_b')(CAST(toYYYYMMDD(p_date), 'Int64') AS date1, tag_value, uids) AS card
FROM test.bitmap_test
FORMAT TSV

-- 结果:
[5,4,9]

12. bitmapMultiExtractWithDate

功能和用法同bitmapMultiCountWithDate,差异点在返回值,返回 bitmap 数据。

SELECT bitmapMultiExtractWithDate('20240101_1', '20240102_2', '20240101_1|20240102_2')(CAST(toYYYYMMDD(p_date), 'Int64') AS date1, multiIf((tag_value = 'a') AND (p_date = '2024-01-01'), toInt64(1), (tag_value = 'b') AND (p_date = '2024-01-02'), toInt64(2), (tag_value = 'c') and (p_date = '2024-01-01'), toInt64(3),toInt32(999)) AS idx, uids) AS card
FROM test.bitmap_test
FORMAT TSV

-- 结果:
[{1,2,3,4,5},{13,14,15,16},{1,2,3,4,5,13,14,15,16}]


-- tag_value刚好是字符串类型,所以可以使用字符串表达式
SELECT bitmapMultiExtractWithDate('20240101_a', '20240102_b', '20240101_a|20240102_b')(CAST(toYYYYMMDD(p_date), 'Int64') AS date1, tag_value, uids) AS card
FROM test.bitmap_test
FORMAT TSV

-- 结果:
[{1,2,3,4,5},{13,14,15,16},{1,2,3,4,5,13,14,15,16}]