找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2810

积分

0

好友

368

主题
发表于 17 小时前 | 查看: 0| 回复: 0

在 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 的注意事项

虽然 PostgreSQLPI() 函数,但是它的 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 公式计算地理距离的完整示例:

  1. 创建测试表并插入示例数据

    -- 创建地点表
    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;
  2. 创建计算地理距离的函数

    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;
    /
  3. 查询示例

    
    -- 示例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)服务开发,还是构建地理位置服务,这个简单的常数都能发挥出强大的作用。



上一篇:企业资产测绘实战指南:七大信息收集技巧与工具详解
下一篇:FFmpeg RTSP连接海康摄像头断连原因与保活机制详解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-25 19:35 , Processed in 0.346226 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表