3. SQL环境准备:数据库与示例数据表结构

在进行阈值分层试验的SQL实现之前,需要搭建一个可复现的数据库环境。本章将提供MySQL与PostgreSQL两种主流数据库的DDL语句,并详细说明三张核心表(用户表、行为表、标签表)的结构设计与字段含义。

3.1 数据库选型与版本要求

本教程的SQL代码兼容以下数据库环境,建议使用对应版本或更高版本:

数据库 最低版本 推荐版本 关键特性依赖
MySQL 8.0 8.0.28+ 窗口函数、CTE、JSON函数
PostgreSQL 12 14+ 窗口函数、CTE、GENERATED列
提示: 如果使用MySQL 5.7,部分窗口函数(如ROW_NUMBER、NTILE)无法使用,建议升级至8.0或使用PostgreSQL。本教程所有示例均以MySQL 8.0语法为主,并在必要时标注PostgreSQL的差异写法。

3.2 数据库与Schema创建

首先创建专用数据库,避免与现有业务库冲突:

-- MySQL 版本
CREATE DATABASE IF NOT EXISTS threshold_experiment
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

USE threshold_experiment;

-- PostgreSQL 版本
-- CREATE DATABASE threshold_experiment
--   ENCODING 'UTF8'
--   LC_COLLATE 'zh_CN.UTF-8'
--   LC_CTYPE 'zh_CN.UTF-8';
-- \c threshold_experiment

3.3 用户表(users)

用户表存储试验参与者的基础画像信息,是分层与分桶的核心依据表。

字段名 类型(MySQL) 类型(PostgreSQL) 约束 说明
user_id BIGINT UNSIGNED BIGSERIAL PRIMARY KEY 用户唯一标识
user_name VARCHAR(64) VARCHAR(64) NOT NULL 用户昵称
registration_date DATE DATE NOT NULL 注册日期
user_level TINYINT UNSIGNED SMALLINT DEFAULT 1 用户等级(1-5)
membership_type VARCHAR(16) VARCHAR(16) DEFAULT 'normal' 会员类型:normal/vip/svip
city VARCHAR(32) VARCHAR(32) DEFAULT '' 所在城市
age_group VARCHAR(8) VARCHAR(8) DEFAULT 'unknown' 年龄段:18-24/25-34/35-44/45+
created_at DATETIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP 记录创建时间
-- MySQL DDL
CREATE TABLE users (
  user_id          BIGINT UNSIGNED AUTO_INCREMENT,
  user_name        VARCHAR(64)      NOT NULL,
  registration_date DATE            NOT NULL,
  user_level       TINYINT UNSIGNED DEFAULT 1,
  membership_type  VARCHAR(16)      DEFAULT 'normal',
  city             VARCHAR(32)      DEFAULT '',
  age_group        VARCHAR(8)       DEFAULT 'unknown',
  created_at       DATETIME         DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (user_id),
  INDEX idx_membership (membership_type),
  INDEX idx_age_group (age_group)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- PostgreSQL DDL
-- CREATE TABLE users (
--   user_id          BIGSERIAL,
--   user_name        VARCHAR(64)      NOT NULL,
--   registration_date DATE            NOT NULL,
--   user_level       SMALLINT         DEFAULT 1,
--   membership_type  VARCHAR(16)      DEFAULT 'normal',
--   city             VARCHAR(32)      DEFAULT '',
--   age_group        VARCHAR(8)       DEFAULT 'unknown',
--   created_at       TIMESTAMP        DEFAULT CURRENT_TIMESTAMP,
--   PRIMARY KEY (user_id)
-- );
-- CREATE INDEX idx_users_membership ON users (membership_type);
-- CREATE INDEX idx_users_age_group ON users (age_group);

3.4 行为表(user_actions)

行为表记录用户在试验期间产生的各类事件,是计算阈值指标(如点击率、转化率、停留时长)的数据源。

字段名 类型(MySQL) 类型(PostgreSQL) 约束 说明
action_id BIGINT UNSIGNED BIGSERIAL PRIMARY KEY 行为记录唯一ID
user_id BIGINT UNSIGNED BIGINT NOT NULL, FK 关联users.user_id
action_type VARCHAR(32) VARCHAR(32) NOT NULL 行为类型:click/purchase/share/view
action_value DECIMAL(12,4) NUMERIC(12,4) DEFAULT 0.0000 行为数值(如金额、时长)
page_url VARCHAR(512) VARCHAR(512) DEFAULT '' 行为发生页面
action_time DATETIME TIMESTAMP NOT NULL 行为发生时间
experiment_group VARCHAR(16) VARCHAR(16) DEFAULT 'control' 试验分组:control/treatment
-- MySQL DDL
CREATE TABLE user_actions (
  action_id        BIGINT UNSIGNED AUTO_INCREMENT,
  user_id          BIGINT UNSIGNED   NOT NULL,
  action_type      VARCHAR(32)       NOT NULL,
  action_value     DECIMAL(12,4)     DEFAULT 0.0000,
  page_url         VARCHAR(512)      DEFAULT '',
  action_time      DATETIME          NOT NULL,
  experiment_group VARCHAR(16)       DEFAULT 'control',
  PRIMARY KEY (action_id),
  INDEX idx_user_id (user_id),
  INDEX idx_action_type (action_type),
  INDEX idx_action_time (action_time),
  INDEX idx_experiment_group (experiment_group),
  CONSTRAINT fk_actions_user FOREIGN KEY (user_id) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- PostgreSQL DDL
-- CREATE TABLE user_actions (
--   action_id        BIGSERIAL,
--   user_id          BIGINT            NOT NULL,
--   action_type      VARCHAR(32)       NOT NULL,
--   action_value     NUMERIC(12,4)     DEFAULT 0.0000,
--   page_url         VARCHAR(512)      DEFAULT '',
--   action_time      TIMESTAMP         NOT NULL,
--   experiment_group VARCHAR(16)       DEFAULT 'control',
--   PRIMARY KEY (action_id),
--   CONSTRAINT fk_actions_user FOREIGN KEY (user_id) REFERENCES users(user_id)
-- );
-- CREATE INDEX idx_actions_user_id ON user_actions (user_id);
-- CREATE INDEX idx_actions_type ON user_actions (action_type);
-- CREATE INDEX idx_actions_time ON user_actions (action_time);

3.5 标签表(user_tags)

标签表存储用户的分层标签与试验分组信息,是阈值分层试验中“分层变量”与“试验组别”的核心映射表。

字段名 类型(MySQL) 类型(PostgreSQL) 约束 说明
tag_id BIGINT UNSIGNED BIGSERIAL PRIMARY KEY 标签记录唯一ID
user_id BIGINT UNSIGNED BIGINT NOT NULL, FK 关联users.user_id
stratum_name VARCHAR(32) VARCHAR(32) NOT NULL 分层名称,如'age_group'/'membership'
stratum_value VARCHAR(64) VARCHAR(64) NOT NULL 分层取值,如'25-34'/'vip'
experiment_id VARCHAR(32) VARCHAR(32) NOT NULL 试验标识,如'exp_2025_01'
group_assignment VARCHAR(16) VARCHAR(16) NOT NULL 试验组分配:control/treatment
assigned_at DATETIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP 分组分配时间
-- MySQL DDL
CREATE TABLE user_tags (
  tag_id           BIGINT UNSIGNED AUTO_INCREMENT,
  user_id          BIGINT UNSIGNED   NOT NULL,
  stratum_name     VARCHAR(32)       NOT NULL,
  stratum_value    VARCHAR(64)       NOT NULL,
  experiment_id    VARCHAR(32)       NOT NULL,
  group_assignment VARCHAR(16)       NOT NULL,
  assigned_at      DATETIME          DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (tag_id),
  UNIQUE KEY uk_user_experiment (user_id, experiment_id, stratum_name),
  INDEX idx_experiment (experiment_id),
  INDEX idx_stratum (stratum_name, stratum_value),
  CONSTRAINT fk_tags_user FOREIGN KEY (user_id) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- PostgreSQL DDL
-- CREATE TABLE user_tags (
--   tag_id           BIGSERIAL,
--   user_id          BIGINT            NOT NULL,
--   stratum_name     VARCHAR(32)       NOT NULL,
--   stratum_value    VARCHAR(64)       NOT NULL,
--   experiment_id    VARCHAR(32)       NOT NULL,
--   group_assignment VARCHAR(16)       NOT NULL,
--   assigned_at      TIMESTAMP         DEFAULT CURRENT_TIMESTAMP,
--   PRIMARY KEY (tag_id),
--   UNIQUE (user_id, experiment_id, stratum_name)
-- );
-- CREATE INDEX idx_tags_experiment ON user_tags (experiment_id);
-- CREATE INDEX idx_tags_stratum ON user_tags (stratum_name, stratum_value);

3.6 三表关系与数据流向

下图展示了三张表之间的关联关系以及数据在阈值分层试验中的流转路径:

阈值分层试验三表关系与数据流向图 三表关系与数据流向 用户表 users user_id (PK) user_level, membership_type 行为表 user_actions action_id (PK) user_id (FK), action_type 标签表 user_tags tag_id (PK) user_id (FK), stratum_name 1 : N 1 : N 数据流向说明: 1. 用户表提供分层变量(user_level, membership_type, age_group) 2. 标签表记录每个用户在每次试验中的分层归属与组别分配 阈值分层试验典型流程: ① 从 users 表读取用户分层特征 → ② 按分层变量将用户划入不同层(stratum) ③ 在每层内随机分桶(control/treatment)→ ④ 记录分组到 user_tags 表 ⑤ 用户行为写入 user_actions 表 → ⑥ 按层与组聚合计算阈值指标

3.7 环境验证脚本

执行完DDL后,建议运行以下验证脚本,确保所有表创建成功且外键约束生效:

-- MySQL 验证
SELECT 
  TABLE_NAME, 
  ENGINE, 
  TABLE_ROWS, 
  CREATE_TIME
FROM information_schema.TABLES 
WHERE TABLE_SCHEMA = 'threshold_experiment';

-- 检查外键约束
SELECT 
  TABLE_NAME, 
  CONSTRAINT_NAME, 
  REFERENCED_TABLE_NAME, 
  REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE 
WHERE TABLE_SCHEMA = 'threshold_experiment' 
  AND REFERENCED_TABLE_NAME IS NOT NULL;

-- PostgreSQL 验证
-- SELECT table_name, table_schema 
-- FROM information_schema.tables 
-- WHERE table_schema = 'public';
注意: 在生产环境中,user_actions 表的数据量可能非常大(日均千万级)。建议根据实际查询模式提前规划分区策略(如按 action_time 按月分区),并在 action_time 和 user_id 上建立复合索引,避免全表扫描影响试验分析性能。

3.8 示例数据快速填充

为方便后续章节的SQL练习,提供以下示例数据插入脚本(仅插入少量数据用于功能验证):

-- 插入5个测试用户
INSERT INTO users (user_name, registration_date, user_level, membership_type, city, age_group) VALUES
('张三', '2024-01-15', 3, 'vip', '北京', '25-34'),
('李四', '2024-03-20', 1, 'normal', '上海', '18-24'),
('王五', '2024-06-10', 5, 'svip', '广州', '35-44'),
('赵六', '2024-08-05', 2, 'normal', '深圳', '25-34'),
('孙七', '2024-11-01', 4, 'vip', '杭州', '45+');

-- 插入行为数据
INSERT INTO user_actions (user_id, action_type, action_value, page_url, action_time, experiment_group) VALUES
(1, 'click', 0, '/home', '2025-01-10 10:30:00', 'control'),
(1, 'purchase', 199.00, '/product/101', '2025-01-10 10:35:00', 'control'),
(2, 'view', 0, '/search', '2025-01-10 11:00:00', 'treatment'),
(3, 'click', 0, '/promo', '2025-01-10 12:15:00', 'treatment'),
(3, 'purchase', 599.00, '/product/202', '2025-01-10 12:20:00', 'treatment'),
(4, 'share', 0, '/article/5', '2025-01-11 09:00:00', 'control'),
(5, 'view', 0, '/home', '2025-01-11 14:30:00', 'treatment');

-- 插入标签数据(试验ID: exp_2025_01,按年龄分层)
INSERT INTO user_tags (user_id, stratum_name, stratum_value, experiment_id, group_assignment) VALUES
(1, 'age_group', '25-34', 'exp_2025_01', 'control'),
(2, 'age_group', '18-24', 'exp_2025_01', 'treatment'),
(3, 'age_group', '35-44', 'exp_2025_01', 'treatment'),
(4, 'age_group', '25-34', 'exp_2025_01', 'control'),
(5, 'age_group', '45+', 'exp_2025_01', 'treatment');
关键点总结:
  • 三张表通过 user_id 外键关联,形成星型模型,便于分层聚合查询。
  • user_tags 表的 stratum_name + stratum_value 组合定义了分层维度,支持多维度分层(如同时按年龄和会员类型分层)。
  • experiment_id 字段支持在同一数据库中并行运行多个试验,互不干扰。
  • 行为表中的 experiment_group 字段作为冗余字段,可加速按组别过滤的查询,避免每次都需要关联标签表。