9. WeDPR MPC语法参考¶
9.1 联合统计SQL语法参考¶
联合统计任务支持通过简洁的SQL语法来定义多方联合计算任务。
9.1.1 数据类型¶
支持数值类型的数据,包括整数和浮点数。数据表必须包含id列,表中不允许出现空值,合法的数据示例如下:
| id | age | tax | accumulation_fund |
|---|---|---|---|
| 9527 | 30 | 100000 | 150000 |
| 9528 | 35 | 200000 | 300000 |
| 9529 | 40 | 400000 | 600000 |
9.1.2 语法¶
基础语句:SELECT、FROM、WHERE、JOIN、INNER JOIN、ON、AS
复杂特性:GROUP BY
运算符合:+、-、*、/、>、<、==
聚合函数:COUNT、SUM、AVG、MAX、MIN
9.1.3 聚合函数¶
| 聚合函数 | 描述 |
|---|---|
| COUNT | 用于统计数据数目 |
| SUM | 用于计算数据之和 |
| AVG | 用于计算平均值 |
| MAX | 用于计算最大值 |
| MIN | 用于计算最小值 |
SELECT¶
SELECT 语句用于从数据库中选取数据。SELECT 语句用于从数据库中选取数据。结果被存储在一个结果表中,称为结果集。
SELECT column1, column2, ...
FROM table_name;
与
SELECT * FROM table_name;
参数说明:
column1, column2, …:要选择的字段名称,可以为多个字段。如果不指定字段名称,则会选择所有字段。
table_name:要查询的表名称。
*: 通配符,表示选择表中的所有列。
WHERE¶
WHERE 子句用于提取那些满足指定条件的记录。
SELECT column1, column2, ...
FROM table_name
WHERE condition;
参数说明:
column1, column2, …:要选择的字段名称,可以为多个字段。如果不指定字段名称,则会选择所有字段。
table_name:要查询的表名称。
JOIN¶
SQL INNER JOIN 从多个表中返回满足 JOIN 条件的所有行。
SELECT column1, column2, ...
FROM table1
JOIN table2 ON condition;
column1, column2, …:要选择的字段名称,可以为多个字段。如果不指定字段名称,则会选择所有字段。
table1:要连接的第一个表。
table2:要连接的第二个表。
condition:连接条件,用于指定连接方式。
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name=table2.column_name;
别名 AS¶
通过使用 SQL,可以为表名称或列名称指定别名,创建别名是为了让列名称的可读性更强。
列别名
SELECT column_name AS alias_name
FROM table_name;
表别名
SELECT column_name(s)
FROM table_name AS alias_name;
GROUP BY¶
GROUP BY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;
9.1.4 简单使用示例¶
以三方求和为例,对应的SQL语句为:
SELECT source0.field0 + source1.field0 + source2.field0 as total_balance
FROM source0,
source1,
source2
WHERE source0.id = source1.id = source2.id;
使用GROUP BY的示例:
SELECT s1.field4 AS r0,
COUNT(s1.field4) AS 'count',
AVG(s0.field1) * 2 + s1.field4 AS r1,
(SUM(s0.field2) + SUM(s1.field2))/(COUNT(s1.field4) + 100/(MIN(s0.field1)+MIN(s1.field1))) + 10,
MAX(s1.field1),
MIN(s2.field2)
FROM (source0 AS s0
INNER JOIN source1 AS s1 ON s0.id = s1.id)
INNER JOIN source2 AS s2 ON s0.id = s2.id
GROUP BY s1.field4
9.1.5 综合使用示例¶
这段SQL查询代码使用了数据源(source0, source1, source2)进行连接,并基于这些数据源的字段执行一系列聚合计算,最后根据source1中的field4字段值进行分组。
SELECT 3*s1.field4 AS r0,
COUNT(s1.field4) AS 'count',
AVG(s0.field1) * 2 + s1.field4 AS r1,
(SUM(s0.field2) + SUM(s1.field2))/(COUNT(s1.field3) + 100/(MIN(s0.field1)+MIN(s1.field1))) + 10,
MAX(s1.field1),
MIN(s2.field2)
FROM (source0 AS s0
INNER JOIN source1 AS s1 ON s0.id = s1.id)
INNER JOIN source2 AS s2 ON s0.id = s2.id
GROUP BY s1.field4;
下面是对这段SQL的详细解释:
选择列表:
3*s1.field4 AS r0: 选择source1中的field4字段值,乘以3,并将结果命名为r0。COUNT(s1.field4) AS 'count': 计算source1中field4字段值的出现次数,并将结果命名为count。注意,由于使用了单引号,列名实际上是'count',这在某些数据库系统中可能不是有效的列名。AVG(s0.field1) * 2 + s1.field4 AS r1: 计算source0中field1字段值的平均值,乘以2后加上source1的field4字段值,结果命名为r1。(SUM(s0.field2) + SUM(s1.field2)) / (COUNT(s1.field3) + 100 / (MIN(s0.field1) + MIN(s1.field1))) + 10: 这是一个复杂的表达式,首先计算source0和source1中field2字段值的总和,然后除以source1中field3字段值的出现次数加上100除以source0和source1中field1字段值的最小值之和,最后结果加上10。MAX(s1.field1): 计算source1中field1字段值的最大值。MIN(s2.field2): 计算source2中field2字段值的最小值。
FROM子句:
source0 AS s0: 这是查询的主数据源,别名为s0。source1 AS s1和source2 AS s2: 这是另外两个要连接的数据源,别名分别为s1和s2。
INNER JOIN子句:
INNER JOIN source1 AS s1 ON s0.id = s1.id: 将source1与source0连接,条件是两个数据源的id字段相等。INNER JOIN source2 AS s2 ON s0.id = s2.id: 将source2与已经连接的source0和source1的组合连接,条件同样是id字段相等。
GROUP BY子句:
GROUP BY s1.field4: 根据source1中的field4字段值对结果进行分组。这意味着SELECT语句中的聚合函数(如COUNT, AVG, SUM, MAX, MIN)将在每个field4值的分组上分别计算。
结果:
查询结果将为每个
field4值的分组返回多列数据:r0,'count',r1, 复杂表达式的结果,MAX(s1.field1), 和MIN(s2.field2)。
9.2 自定义计算语法参考¶
自定义计算语法可以使用类似python语法方式,自定义计算逻辑,通过自定义任务进行多方安全计算。联合统计的SQL执行之前平台会将SQL解析成自定义计算任务,然后执行该任务。
9.2.1 数据类型¶
| 数据类型 | 描述 | 示例 |
|---|---|---|
| pint | 秘密数据整数类型,如pint(1)、pint(-1)、pint(10) | a = pint(1)b = pint(-1) |
| pnum | 秘密数据默认数据类型,等同于pfloat | a = pnum(1.2) b = pnum(2) |
| pfix | 秘密数据定点数类型,如pfix(1.1)、 pfix(-2) | a = pfix(1.1) b = pfix(-2) |
| pfloat | 秘密数据浮点数类型,如pfloat(1.1)、 pfloat(-2) | a = pfloat(1.1) b = pfloat(-2) |
| pint | pfix | pfloat(pnum) | |
|---|---|---|---|
| 支持小数 | 否 | 是 | 是 |
| 数据精度 | - | 可配置 | 高 |
| 性能 | 高 | 中 | 低 |
pfix类型设置精度
pfix.set_precision(n,m)
n 小数部分的位数长度(初始默认值为16)
m 定点数的总位数长度,如果没有指定,默认为
f的两倍(初始默认值为31)。
高级类型
不可变数组Array:ppc的数组仅为数据的存储形式,运算时请对单个元素进行操作,
不可变而为数组Matrix:ppc的数组仅为数据的存储形式,运算时请对单个元素进行操作
9.2.2 语法¶
WeDPR自定义计算明文数据语法与Python类似。所有秘密数据类型均支持以下操作。
9.2.2.1 运算符¶
支持加+、减-、乘*、除/(整数类型pint为//)、取模%(仅限整数类型pint)运算
a_int = pint(1)
b_int = pint(-1)
c_int = a_int + b_int
a_fix = pfix(1.1)
b_fix = pfix(-2)
c_fix = a_fix * b_fix
a_float = pfloat(1.1)
b_float = pfloat(-2)
c_float = a_float/b_float
9.2.2.2 读取数据,支持读取为数组以及矩阵¶
读取秘密输入为数组:read_array
read_array(party_id, source_record_count, value_type=pnum)
读取数据源数据输入为秘密数组
参数
party_id: 数据方id
source_record_count:读取的数据条数
value_type:数据类型,包括pint、pfix、pfloat、pnum,默认类型为pnum
返回值
Array(数据类型):不可变数组类型,通过.length访问长度
注意: 读取数据时,可以不用在算法文件中写具体记录数,使用占位符$(source{参与方序号}_record_count)代替,例如$(source0_record_count),表示读取第0方所有行数据。
。
示例
source0_record = read_array(SOURCE0, 1, pfix)
source1_record = read_array(SOURCE1, 2, pfix)
source2_record = read_array(SOURCE2, 3, pfloat)
# 使用数据
c_fix = source0_record[0] + source1_record[0]
c = c_fix.reveal()
source0_record = read_array(SOURCE0, 1, pint)
a = source0_record.length
# a = 1
读取数据为矩阵 read_matrix
read_matrix(party_id, height, width, value_type=pnum)
读取数据源数据输入为秘密数组
参数
party_id: 数据方id
height:读取的矩阵行数
width:读取的矩阵列数
value_type:数据类型,包括pint、pfix、pfloat、pnum,默认类型为pnum
返回值
Matrix(数据类型):不可变矩阵类型,通过.sizes访问长度
示例
source0_record = read_matrix(SOURCE0, 1,1, pfix)
source1_record = read_matrix(SOURCE1, 2,1, pfix)
source2_record = read_matrix(SOURCE2, 3,2, pfloat)
# 使用数据
c_fix = source0_record[0][0] + source1_record[0][1]
c = c_fix.reveal()
source0_record = read_matrix(SOURCE0, 1, 2, pint)
a = source0_record.sizes[0]
b = source0_record.sizes[1]
# a = 1, b = 2
9.2.2.3 披露明文结果¶
reveal(data)
将秘密数据转换明文
参数
data: 秘密数据
返回值
明文数据,如int、fix、float
示例
a_int = pint(1)
b_int = pint(-1)
c_int = a_int + b_int
display_data([c_int.reveal()])
9.2.3 工具函数¶
针对密文数据类型,PPC提供了多种工具函数,分别如下:
9.2.3.1 条件判断:condition¶
a = pint(1)
b = pint(2)
c = condition(a > b, a, b)
# c is pint(2)
9.2.3.2 合并两个秘密数组combine_array¶
source0_record = read_array(SOURCE0, 1, pint) # read pint(1)
source1_record = read_array(SOURCE1, 1, pint) # read pint(2)
array_result = combine_array(source0_record, source1_record)
# array_result is [pint(1), pint(2)]
9.2.3.3 返回秘密数组中的最大值max_in_array,最小值min_in_array¶
source0_record = read_array(SOURCE0, 1, pint) # read pint(4)
source1_record = read_array(SOURCE1, 1, pint) # read pint(5)
array_result = combine_array(source0_record, source1_record)
# array_result is [pint(4), pint(5)]
max_value, max_index = max_in_array(array_result)
# max_value = pint(5) max_index = pint(1)
min_value, min_index = min_in_array(array_result)
# min_value = pint(4) min_index = pint(0)
9.2.3.4 结果输出¶
display_data(data)
将明文结果展示
参数
data: 明文数据
示例
a_int = pint(1)
b_int = pint(-1)
c_int = a_int + b_int
c = c_int.reveal()
display_data([c_int.reveal()])
set_display_field_names(data)
设置输出列表中的列名
参数
data: 字段列名数组
display_array(data)
将秘密数组展示
参数
data: 秘密数组
示例
# 输出数组
data_array = read_array(SOURCE0, 3)
set_display_field_names(["age", "salary", "score"])
display_array(data_array)
display_matrix(data)
将秘密矩阵展示
参数
data: 秘密矩阵
示例
# 输出矩阵
data_matrix = read_matrix(SOURCE0, 2, 3)
set_display_field_names(["age", "salary", "score"])
display_matrix(data_matrix)
9.2.4 自定义计算简单示例¶
下面是一个模拟2方比较(经典的百万富翁问题)的逻辑,安全比较两方数值的大小
# 定义两方参与
SOURCE0 = 0
SOURCE1 = 1
# sourceX_record_count和sourceX_column_count为wedpr读取数据时的关键字,系统会根据填写的行列进行数据预处理
# 读取数据源0第1列数据(具体指id列之后的第1列)
source0_record_count=$(source0_record_count)
source0_column_count=1
source0_record = read_array(SOURCE0, source0_record_count, pint)
source1_record_count=$(source1_record_count)
source1_column_count=1
source1_record = read_array(SOURCE1, source1_record_count, pint)
# 定义计算模块
def wedpr_main(source0_record, source1_record):
# 比较大小,条件判断
result_value = condition(source0_record[0] > source1_record[0], 1, 0)
# 输出结果,输出1表示结果接收方大,输出0表示另一方大
set_display_field_names(["is_winner"])
display_data([result_value.reveal()])
wedpr_main(source0_record, source1_record)
9.2.5 自定义计算综合示例¶
下面是一个模拟三方投票的逻辑,每一方给一个候选项投一个数值
# 定义三方参与
SOURCE0 = 0
SOURCE1 = 1
SOURCE2 = 2
# sourceX_record_count和sourceX_column_count为wedpr读取数据时的关键字,系统会根据填写的行列进行数据预处理
# 读取数据源0第1列数据(具体指id列之后的第1列)
source0_record_count=$(source0_record_count)
source0_column_count=1
# 读取数据源1的前1列数据
source1_record_count=$(source1_record_count)
source1_column_count=1
# 读取数据源2的前1列数据
source2_record_count=$(source2_record_count)
source2_column_count=1
# 读取数据为数组
candidate_count = 3
source0_record = read_array(SOURCE0, candidate_count, pint)
source1_record = read_array(SOURCE1, candidate_count, pint)
source2_record = read_array(SOURCE2, candidate_count, pint)
def wedpr_main(source0_record, source1_record, source2_record):
candidate0 = source0_record[0] + source1_record[0] + source2_record[0]
candidate1 = source0_record[1] + source1_record[1] + source2_record[1]
candidate2 = source0_record[2] + source1_record[2] + source2_record[2]
set_display_field_names(["candidate1", "candidate2", "candidate3"])
display_data([candidate0.reveal(),candidate1.reveal(),candidate2.reveal()])
wedpr_main(source0_record, source1_record, source2_record)