PostgreSQL条件判断函数

本章将详细介绍PostgreSQL中的条件判断函数,包括空值处理、条件选择、值比较等函数,帮助您编写更健壮的SQL查询。

空值处理函数

COALESCE函数

返回参数列表中第一个非空值:

SELECT 
    COALESCE(NULL, 'Default Value') AS coalesce_result1,  -- 返回 'Default Value'
    COALESCE(NULL, NULL, 'Third Value') AS coalesce_result2, -- 返回 'Third Value'
    COALESCE(username, email, 'Unknown User') AS user_display_name -- 用户显示名
FROM users;

NULLIF函数

如果两个参数相等则返回NULL,否则返回第一个参数:

SELECT 
    NULLIF(10, 10) AS nullif_result1,                     -- 返回 NULL
    NULLIF(10, 5) AS nullif_result2,                      -- 返回 10
    NULLIF('', '') AS nullif_result3;                     -- 返回 NULL

ISNULL和IS NOT NULL

检查值是否为NULL:

SELECT 
    username,
    CASE WHEN username IS NULL THEN 'No Username' ELSE username END AS safe_username,
    COALESCE(phone, 'No Phone') AS contact_phone
FROM users;

条件选择函数

CASE表达式

SQL标准的条件表达式:

SELECT 
    username,
    CASE 
        WHEN age < 18 THEN 'Minor'
        WHEN age BETWEEN 18 AND 65 THEN 'Adult'
        ELSE 'Senior'
    END AS age_group,
    CASE status
        WHEN 'active' THEN 'Active User'
        WHEN 'inactive' THEN 'Inactive User'
        ELSE 'Unknown Status'
    END AS status_description
FROM users;

简单CASE表达式

基于单个表达式的条件判断:

SELECT 
    product_name,
    CASE category_id
        WHEN 1 THEN 'Electronics'
        WHEN 2 THEN 'Books'
        WHEN 3 THEN 'Clothing'
        ELSE 'Other'
    END AS category_name
FROM products;

值比较函数

GREATEST函数

返回参数列表中的最大值:

SELECT 
    GREATEST(10, 20, 30) AS greatest_result,              -- 返回 30
    GREATEST('apple', 'banana', 'cherry') AS greatest_string, -- 返回 'cherry'
    GREATEST(score1, score2, score3) AS best_score        -- 返回最高分
FROM student_scores;

LEAST函数

返回参数列表中的最小值:

SELECT 
    LEAST(10, 20, 30) AS least_result,                    -- 返回 10
    LEAST('apple', 'banana', 'cherry') AS least_string,   -- 返回 'apple'
    LEAST(price1, price2, price3) AS lowest_price         -- 返回最低价
FROM product_prices;

布尔函数

TRUE和FALSE函数

处理布尔值:

SELECT 
    username,
    is_active,
    CASE 
        WHEN is_active IS TRUE THEN 'Active'
        WHEN is_active IS FALSE THEN 'Inactive'
        WHEN is_active IS NULL THEN 'Unknown'
    END AS status_text
FROM users;

布尔逻辑函数

组合布尔条件:

SELECT 
    username,
    COALESCE(is_premium, FALSE) AS is_premium_safe,
    COALESCE(is_verified, FALSE) AS is_verified_safe,
    COALESCE(is_premium, FALSE) AND COALESCE(is_verified, FALSE) AS is_eligible
FROM users;

实际应用场景

数据清洗和验证

-- 清洗用户数据,处理空值
UPDATE users 
SET 
    display_name = COALESCE(display_name, username, email, 'Anonymous'),
    phone = NULLIF(TRIM(phone), ''),
    age = GREATEST(LEAST(age, 150), 0) -- 限制年龄在0-150之间
WHERE id IN (SELECT id FROM users WHERE display_name IS NULL OR phone = '' OR age < 0 OR age > 150);

报告和统计

-- 生成用户状态报告
SELECT 
    CASE 
        WHEN last_login_date >= CURRENT_DATE - INTERVAL '7 days' THEN 'Active'
        WHEN last_login_date >= CURRENT_DATE - INTERVAL '30 days' THEN 'Recent'
        WHEN last_login_date IS NOT NULL THEN 'Inactive'
        ELSE 'Never Logged In'
    END AS user_activity_level,
    COUNT(*) AS user_count
FROM users
GROUP BY 
    CASE 
        WHEN last_login_date >= CURRENT_DATE - INTERVAL '7 days' THEN 'Active'
        WHEN last_login_date >= CURRENT_DATE - INTERVAL '30 days' THEN 'Recent'
        WHEN last_login_date IS NOT NULL THEN 'Inactive'
        ELSE 'Never Logged In'
    END
ORDER BY user_count DESC;

价格和折扣计算

-- 计算最终价格,考虑多种折扣
SELECT 
    product_name,
    price AS original_price,
    COALESCE(member_discount, 0) AS member_discount,
    COALESCE(seasonal_discount, 0) AS seasonal_discount,
    COALESCE(coupon_discount, 0) AS coupon_discount,
    price * (1 - GREATEST(
        COALESCE(member_discount, 0),
        COALESCE(seasonal_discount, 0),
        COALESCE(coupon_discount, 0)
    )) AS final_price
FROM products
WHERE is_active = TRUE;

评分和排名

-- 计算综合评分
SELECT 
    student_name,
    test1_score,
    test2_score,
    test3_score,
    GREATEST(test1_score, test2_score, test3_score) AS best_score,
    LEAST(test1_score, test2_score, test3_score) AS worst_score,
    ROUND((test1_score + test2_score + test3_score) / 3.0, 2) AS average_score,
    CASE 
        WHEN (test1_score + test2_score + test3_score) / 3.0 >= 90 THEN 'A'
        WHEN (test1_score + test2_score + test3_score) / 3.0 >= 80 THEN 'B'
        WHEN (test1_score + test2_score + test3_score) / 3.0 >= 70 THEN 'C'
        WHEN (test1_score + test2_score + test3_score) / 3.0 >= 60 THEN 'D'
        ELSE 'F'
    END AS letter_grade
FROM student_scores;

性能优化

索引考虑

在使用条件函数时考虑索引影响:

-- 不好的查询(可能无法使用索引)
SELECT * FROM users 
WHERE COALESCE(phone, email) = 'john@example.com';

-- 优化后的查询(可以使用索引)
SELECT * FROM users 
WHERE phone = 'john@example.com' OR email = 'john@example.com';

-- 为条件查询创建合适的索引
CREATE INDEX idx_users_contact ON users (phone, email);

函数索引

为常用条件函数创建索引:

-- 为显示名创建函数索引
CREATE INDEX idx_users_display_name ON users (COALESCE(display_name, username));

-- 为年龄范围创建部分索引
CREATE INDEX idx_users_adults ON users (age) WHERE age BETWEEN 18 AND 65;

高级应用

嵌套条件函数

SELECT 
    username,
    COALESCE(
        CASE 
            WHEN is_premium IS TRUE THEN 'Premium User'
            WHEN is_verified IS TRUE THEN 'Verified User'
            ELSE NULL
        END,
        'Regular User'
    ) AS user_type
FROM users;

与聚合函数结合

SELECT 
    department,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary,
    MAX(salary) AS max_salary,
    MIN(salary) AS min_salary,
    GREATEST(AVG(salary) * 1.1, MAX(salary) * 0.9) AS target_salary
FROM employees
GROUP BY department
HAVING COUNT(*) > 5
ORDER BY avg_salary DESC;

综合示例

-- 创建用户分析函数
CREATE OR REPLACE FUNCTION analyze_user_engagement()
RETURNS TABLE(
    engagement_level TEXT,
    user_count BIGINT,
    avg_session_duration NUMERIC,
    most_common_action TEXT
) AS $$
BEGIN
    RETURN QUERY
    WITH user_stats AS (
        SELECT 
            u.id,
            u.username,
            COUNT(s.id) AS session_count,
            COALESCE(AVG(EXTRACT(EPOCH FROM (s.end_time - s.start_time))), 0) AS avg_duration_seconds,
            CASE 
                WHEN COUNT(s.id) >= 10 THEN 'Highly Engaged'
                WHEN COUNT(s.id) >= 5 THEN 'Moderately Engaged'
                WHEN COUNT(s.id) > 0 THEN 'Low Engaged'
                ELSE 'Inactive'
            END AS engagement_category
        FROM users u
        LEFT JOIN user_sessions s ON u.id = s.user_id
        GROUP BY u.id, u.username
    ),
    action_stats AS (
        SELECT 
            us.engagement_category,
            a.action_type,
            COUNT(*) AS action_count
        FROM user_stats us
        JOIN user_sessions s ON us.id = s.user_id
        JOIN user_actions a ON s.id = a.session_id
        GROUP BY us.engagement_category, a.action_type
    ),
    top_actions AS (
        SELECT 
            engagement_category,
            action_type,
            ROW_NUMBER() OVER (PARTITION BY engagement_category ORDER BY action_count DESC) as rn
        FROM action_stats
    )
    SELECT 
        us.engagement_category AS engagement_level,
        COUNT(*) AS user_count,
        ROUND(AVG(us.avg_duration_seconds) / 60, 2) AS avg_session_duration,
        COALESCE(ta.action_type, 'No Actions') AS most_common_action
    FROM user_stats us
    LEFT JOIN top_actions ta ON us.engagement_category = ta.engagement_category AND ta.rn = 1
    GROUP BY us.engagement_category, ta.action_type
    ORDER BY user_count DESC;
END;
$$ LANGUAGE plpgsql;

-- 使用函数分析用户参与度
SELECT * FROM analyze_user_engagement();

提示:合理使用条件判断函数可以让SQL查询更加灵活和健壮,但要注意避免过度嵌套,保持代码的可读性和维护性。