在 SQL 中,圆周率常量 PI() 是一个看似简单却极其重要的数学函数。虽然它只是一个返回常数的函数,但在几何计算、工程建模、物理仿真,甚至金融周期分析中都扮演着关键角色。PI() 是一个无参函数,返回数学常数圆周率 π 的近似值(通常是双精度浮点数),其基本用法如下:
SELECT
PI() AS pi_value, -- 3.141592653589793(15-17位有效数字)
ROUND(PI(), 2) AS pi_2dp, -- 3.14
ROUND(PI(), 4) AS pi_4dp, -- 3.1416
2 * PI() AS two_pi, -- 6.283185307179586(常用于角度转换)
PI() / 2 AS half_pi; -- 1.5707963267948966(90度弧度值)
精度说明:所有支持 PI() 的数据库都返回 IEEE 754 双精度浮点数(约15–17位有效数字),对绝大多数工程和商业应用已足够精确。
不同数据库中的获取方式
值得注意的是,并非所有数据库都原生提供 PI() 函数。
Oracle 数据库的替代方案
Oracle 没有内置的 PI() 函数。在 Oracle 中获取 π 值的常用方法是使用 ACOS(-1),因为 ACOS(cos(π)) 应返回 π。上面的查询在 Oracle 中可以写为:
SELECT
ACOS(-1) AS pi_value,
ROUND(ACOS(-1), 2) AS pi_2dp,
ROUND(ACOS(-1), 4) AS pi_4dp,
2 * ACOS(-1) AS two_pi,
ACOS(-1) / 2 AS half_pi
FROM dual;
注意:在 Oracle 中,所有标量表达式(如函数调用)必须配合 FROM dual 使用。
预期输出:
PI_VALUE PI_2DP PI_4DP TWO_PI HALF_PI
----------------- ------ ------ ------------------ ------------------
3.141592653589793 3.14 3.1416 6.283185307179586 1.5707963267948966
PostgreSQL 的注意事项
虽然 PostgreSQL 有 PI() 函数,但是它的 ROUND() 函数不接受 (double precision, integer) 类型的参数组合。也就是说,ROUND(PI(), 2) 在 PostgreSQL 中会报错,因为 PI() 返回的是 double precision(即浮点数),而 PostgreSQL 的内置 ROUND(x, n) 只支持 numeric 类型。
在 PostgreSQL 中的正确用法是先将 PI() 的结果显式转换为 NUMERIC 类型:
SELECT
PI() AS pi_value,
ROUND(PI()::NUMERIC, 2) AS pi_2dp,
ROUND(PI()::NUMERIC, 4) AS pi_4dp,
2 * PI() AS two_pi,
PI() / 2 AS half_pi;
或者使用更明确的 CAST 语法:ROUND(CAST(PI() AS NUMERIC), 2)。
说明:
::NUMERIC 是 PostgreSQL 的类型转换语法(等价于 CAST(... AS NUMERIC))。
- 如果不转换类型,PostgreSQL 会尝试匹配函数签名,但是
round(double precision, integer) 不存在,所以报错。
double precision 类型本身是二进制浮点数,不适合做精确的小数位舍入;而 NUMERIC 是十进制精确类型,更适合财务或显示用途。
同样,在 PostgreSQL 中使用 ACOS(-1) 也应进行类型转换:
SELECT
ACOS(-1) AS pi_value, -- ≈ 3.141592653589793 (double)
ROUND(ACOS(-1)::NUMERIC, 2) AS pi_2dp, -- 3.14
ROUND(ACOS(-1)::NUMERIC, 4) AS pi_4dp, -- 3.1416
2 * ACOS(-1) AS two_pi, -- ≈ 6.283185307179586
ACOS(-1) / 2 AS half_pi; -- ≈ 1.5707963267948966
实际应用场景
场景1:几何计算(圆、球、圆柱)
以下演示如何在支持 PI() 函数的数据库(如 MySQL)中计算常见几何属性,无需创建真实表:
-- 假设使用支持PI()函数的数据库(如:MySQL、SQL Server)
-- 通过UNION ALL构造一个临时的radius列表,无需真实表
SELECT
radius,
-- 圆面积公式:A = π × r²
-- 示例:r = 2 → π × 4 ≈ 3.1416 × 4 = 12.5664 → 四舍五入保留2位小数 → 12.57
ROUND(PI() * POWER(radius, 2), 2) AS area,
-- 圆周长公式:C = 2πr
-- 示例:r = 2 → 2 × π × 2 ≈ 12.5664 → 12.57
ROUND(2 * PI() * radius, 2) AS circumference,
-- 球体积公式:V = (4/3) × π × r³
-- 注意:使用4.0/3而不是4/3,确保结果为浮点数(避免整数除法截断)
-- 示例:r = 2 → (4/3) × π × 8 ≈ 33.5103 → 33.51
ROUND(4.0 / 3 * PI() * POWER(radius, 3), 2) AS sphere_volume,
-- 圆柱表面积(假设高度h = 2 × 半径,即等于直径)
-- 表面积 = 2个底面 + 侧面积 = 2πr² + 2πr·h
-- 代入h = 2r → 2πr² + 2πr·(2r) = 2πr² + 4πr² = 6πr²
-- 示例:r = 2 → 6 × π × 4 ≈ 75.3982 → 75.40
ROUND(
2 * PI() * POWER(radius, 2) + -- 两个圆形底面的面积
2 * PI() * radius * (2 * radius), -- 侧面积(高 = 2 × radius)
2
) AS cylinder_area
-- 构造临时数据集:模拟一张名为circles的表,包含5个不同的半径值
-- 使用UNION ALL高效合并多个单行结果(比多次INSERT更适合只读查询)
FROM (
SELECT 1 AS radius
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 5
UNION ALL SELECT 10
) AS circles; -- 派生表别名为circles,列名为radius
预期输出结果(基于π ≈ 3.141592653589793,保留2位小数):
| radius |
area |
circumference |
sphere_volume |
cylinder_area |
| 1 |
3.14 |
6.28 |
4.19 |
18.85 |
| 2 |
12.57 |
12.57 |
33.51 |
75.40 |
| 3 |
28.27 |
18.85 |
113.10 |
169.65 |
| 5 |
78.54 |
31.42 |
523.60 |
471.24 |
| 10 |
314.16 |
62.83 |
4188.79 |
1884.96 |
场景2:角度与弧度转换
在三角函数中,所有数据库的 SIN()、COS() 等函数都使用弧度制作为输入!这是一个常见的陷阱。
MySQL 实现:
-- 将常见角度(度)转换为弧度,并计算其正弦值(sin)
-- 数学基础:
-- • 所有三角函数(如:SIN、COS)在编程/数据库中均使用「弧度」作为输入单位
-- • 角度转弧度公式:radians = degrees × π / 180
SELECT
angle_deg, -- 输入的角度(单位:度)
-- 角度转弧度:radians = degrees × π / 180
-- 示例:30° → 30 × π / 180 = π/6 ≈ 0.5236弧度
angle_deg * PI() / 180 AS angle_rad,
-- 计算正弦值:SIN(弧度)
-- 注意:SIN函数要求输入为弧度,因此必须先转换
-- 示例:SIN(π/6) = 0.5
SIN(angle_deg * PI() / 180) AS sin_value
-- 构造临时角度表:包含0°, 30°, 45°, 90°四个典型角度
FROM (
SELECT 0 AS angle_deg UNION ALL
SELECT 30 UNION ALL
SELECT 45 UNION ALL
SELECT 90
) angles; -- 派生表别名为angles
预期输出结果:
| angle_deg |
angle_rad (≈) |
sin_value (≈) |
| 0 |
0.000000 |
0.000000 |
| 30 |
0.5235987756 |
0.500000 |
| 45 |
0.7853981634 |
0.70710678 |
| 90 |
1.5707963268 |
1.000000 |
场景3:周期性数据分析(如:季节性销售预测)
利用 SIN() 函数和 PI() 可以模拟具有周期性的数据波动,例如电商的年度销售季节性。
以下是一个在 MySQL 8.0+ 中实现的电商季节性销售分析示例:
-- 电商季节性销售分析SQL脚本
-- 创建日期维度表,包含一年365天
WITH RECURSIVE seq AS (
-- 生成1到365的数字序列(使用递归CTE)
SELECT 1 AS id
UNION ALL
SELECT id + 1
FROM seq
WHERE id < 365
),
sales_calendar AS (
SELECT
seq.id AS day_of_year,
DATE('2024-01-01') + INTERVAL (seq.id - 1) DAY AS actual_date,
10000 AS base_sales, -- 基础销售额
0.3 AS seasonal_factor, -- 季节性调整因子(30%的波动幅度)
CASE
WHEN seq.id = 100 THEN 1.5 -- 春季大促
WHEN seq.id = 300 THEN 1.6 -- 秋季大促
ELSE 1.0
END AS promotion_factor, -- 促销活动调整
CASE
WHEN seq.id IN (1, 150, 350) THEN 1.4 -- 节日效应
ELSE 1.0
END AS holiday_factor -- 节假日调整
FROM seq
),
-- 计算季节性销售预测
seasonal_analysis AS (
SELECT
day_of_year,
actual_date,
base_sales,
-- 核心公式:使用正弦函数模拟季节性波动
-- SIN(2 * PI() * day_of_year / 365):365天为一个完整周期
-- 0.3 * SIN(...):30%的波动幅度
-- +1:将波动从[-0.3, 0.3]转换到[0.7, 1.3]的乘数范围
(1 + seasonal_factor * SIN(2 * PI() * day_of_year / 365)) AS seasonal_multiplier,
base_sales * (1 + seasonal_factor * SIN(2 * PI() * day_of_year / 365)) AS seasonal_sales_only,
-- 计算最终预测销售额(考虑所有因素)
base_sales *
(1 + seasonal_factor * SIN(2 * PI() * day_of_year / 365)) *
promotion_factor *
holiday_factor AS predicted_sales,
-- 销售高峰识别标志
CASE
WHEN SIN(2 * PI() * day_of_year / 365) > 0.8 THEN '销售高峰'
WHEN SIN(2 * PI() * day_of_year / 365) < -0.8 THEN '销售低谷'
ELSE '正常水平'
END AS sales_peak_flag
FROM sales_calendar
)
-- 最终查询:获取关键日期和季度汇总
SELECT
day_of_year,
actual_date,
ROUND(predicted_sales, 2) AS predicted_sales,
ROUND(seasonal_sales_only, 2) AS seasonal_sales_only,
CONCAT(ROUND((seasonal_multiplier - 1) * 100, 1), '%') AS seasonal_change,
sales_peak_flag,
CASE
WHEN day_of_year BETWEEN 1 AND 90 THEN 'Q1'
WHEN day_of_year BETWEEN 91 AND 181 THEN 'Q2'
WHEN day_of_year BETWEEN 182 AND 273 THEN 'Q3'
ELSE 'Q4'
END AS quarter,
CASE
WHEN day_of_year BETWEEN 80 AND 172 THEN '春季'
WHEN day_of_year BETWEEN 173 AND 264 THEN '夏季'
WHEN day_of_year BETWEEN 265 AND 355 THEN '秋季'
ELSE '冬季'
END AS season
FROM seasonal_analysis
-- 查看几个关键时间点
WHERE day_of_year IN (1, 90, 100, 180, 181, 182, 270, 300, 350, 365)
OR day_of_year % 30 = 0 -- 每月查看一天
ORDER BY day_of_year;
这个脚本通过正弦函数 SIN(2 * PI() * day_of_year / 365) 巧妙地模拟了以年为周期的销售波动,并结合了促销和节假日因子,是后端业务逻辑中数据建模的一个经典示例。
场景4:地理距离计算(Haversine公式)
在需要计算地球表面两点间距离的场景中(如LBS应用),Haversine公式是标准解决方案,而该公式的核心离不开 π。
以下是在 Oracle 数据库中实现 Haversine 公式计算地理距离的完整示例:
-
创建测试表并插入示例数据:
-- 创建地点表
CREATE TABLE locations (
location_id NUMBER PRIMARY KEY,
location_name VARCHAR2(50),
lat NUMBER, -- 纬度,范围:-90到90
lon NUMBER -- 经度,范围:-180到180
);
-- 插入示例数据(全球主要城市坐标)
INSERT INTO locations VALUES (1, '北京', 39.9042, 116.4074);
INSERT INTO locations VALUES (2, '上海', 31.2304, 121.4737);
INSERT INTO locations VALUES (3, '纽约', 40.7128, -74.0060);
INSERT INTO locations VALUES (4, '伦敦', 51.5074, -0.1278);
INSERT INTO locations VALUES (5, '悉尼', -33.8688, 151.2093);
COMMIT;
-
创建计算地理距离的函数:
CREATE OR REPLACE FUNCTION calculate_distance_km(
lat1 IN NUMBER, -- 地点1纬度
lon1 IN NUMBER, -- 地点1经度
lat2 IN NUMBER, -- 地点2纬度
lon2 IN NUMBER -- 地点2经度
) RETURN NUMBER
IS
-- 常量定义
earth_radius_km CONSTANT NUMBER := 6371; -- 地球平均半径(公里)
pi CONSTANT NUMBER := 3.141592653589793;
-- 弧度变量
lat1_rad NUMBER;
lon1_rad NUMBER;
lat2_rad NUMBER;
lon2_rad NUMBER;
-- 中间计算变量
dlat NUMBER;
dlon NUMBER;
a NUMBER;
c NUMBER;
distance NUMBER;
BEGIN
-- 1.将角度转换为弧度(Oracle无RADIANS函数,需手动转换)
lat1_rad := lat1 * pi / 180;
lon1_rad := lon1 * pi / 180;
lat2_rad := lat2 * pi / 180;
lon2_rad := lon2 * pi / 180;
-- 2.计算纬度和经度的差值(弧度)
dlat := lat2_rad - lat1_rad;
dlon := lon2_rad - lon1_rad;
-- 3.应用Haversine公式: a = sin²(Δlat/2) + cos(lat1) * cos(lat2) * sin²(Δlon/2)
a := POWER(SIN(dlat / 2), 2) +
COS(lat1_rad) * COS(lat2_rad) *
POWER(SIN(dlon / 2), 2);
-- 4.计算中心角(弧度): c = 2 * atan2(√a, √(1−a))
c := 2 * ATAN2(SQRT(a), SQRT(1 - a));
-- 5.计算距离
distance := earth_radius_km * c;
-- 返回结果,四舍五入到两位小数
RETURN ROUND(distance, 2);
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END calculate_distance_km;
/
-
查询示例:
-- 示例1:计算所有城市到北京的距离
SELECT
location_name AS "城市",
calculate_distance_km(39.9042, 116.4074, lat, lon) AS "到北京的直线距离(公里)"
FROM locations
WHERE location_id != 1
ORDER BY "到北京的直线距离(公里)";
-- 示例2:计算所有城市组合之间的距离
SELECT
a.location_name AS "起点",
b.location_name AS "终点",
calculate_distance_km(a.lat, a.lon, b.lat, b.lon) AS "直线距离(公里)"
FROM locations a
CROSS JOIN locations b
WHERE a.location_id < b.location_id
ORDER BY a.location_name, b.location_name;
**说明**:
- **Haversine公式原理**:用于计算球面上两点间的最短距离(大圆距离),考虑了地球曲率,比平面距离计算更准确。
- **Oracle特性**:使用 `ATAN2(y, x)` 避免除零错误;手动进行角度到弧度的转换(`* pi / 180`)。
- **精度**:使用地球平均半径6371公里,对于长距离计算(>500公里)较为准确,约有0.5%的误差。
## 总结
从基础的几何运算到复杂的业务建模和空间计算,SQL 中的 `PI()` 函数(或其替代方案 `ACOS(-1)`)都是连接数学理论与数据处理实践的关键桥梁。掌握其在不同数据库中的特性和应用场景,能让你在解决涉及圆形、周期性、角度转换或地理距离等实际问题时更加得心应手。无论是进行数据分析、[后端](https://yunpan.plus/f/14-1)服务开发,还是构建地理位置服务,这个简单的常数都能发挥出强大的作用。