You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL中按比例折算薪资的存储过程开发需求及示例说明

MySQL Stored Procedure for Pro-Rata Salary Calculation

Context

You have a salary table tracking salary effective dates and amounts, and you need to split a given pay period into segments aligned with salary changes. For each segment, you want to calculate days, daily rate, hourly rate, and total pay—using a fixed 26 workdays per month and 8-hour workdays, just like your manual calculation.

Full Stored Procedure Code

Here's the complete implementation that handles all edge cases (like pay periods extending beyond the last salary change) and matches your manual breakdown:

DELIMITER //

CREATE PROCEDURE CalculateProRataSalary(
    IN p_pay_period_start DATE,
    IN p_pay_period_end DATE,
    IN p_fixed_work_days INT DEFAULT 26
)
BEGIN
    -- Temp table to store salary ranges with previous effective date (for segmenting periods)
    DROP TEMPORARY TABLE IF EXISTS temp_salary_ranges;
    CREATE TEMPORARY TABLE temp_salary_ranges AS
    SELECT 
        affective_date,
        salary,
        -- Get the prior effective date (default to a very early date for the first record)
        LAG(affective_date, 1, '1900-01-01') OVER (ORDER BY affective_date) AS previous_affective_date
    FROM salary
    ORDER BY affective_date;

    -- Calculate pro-rata segments for salary changes within the pay period
    SELECT
        -- Determine the actual start of the segment (clamped to pay period start)
        GREATEST(sr.previous_affective_date + INTERVAL 1 DAY, p_pay_period_start) AS period_start,
        -- Determine the actual end of the segment (clamped to pay period end)
        LEAST(sr.affective_date - INTERVAL 1 DAY, p_pay_period_end) AS period_end,
        -- Calculate number of days in the segment
        DATEDIFF(LEAST(sr.affective_date - INTERVAL 1 DAY, p_pay_period_end), 
                 GREATEST(sr.previous_affective_date + INTERVAL 1 DAY, p_pay_period_start)) + 1 AS days,
        sr.salary AS salary_amount,
        -- Daily rate: salary divided by fixed workdays
        ROUND(sr.salary / p_fixed_work_days, 2) AS daily_rate,
        -- Hourly rate: daily rate divided by 8 hours
        ROUND(sr.salary / p_fixed_work_days / 8, 2) AS hourly_rate,
        -- Total pay for the segment: days * daily rate
        ROUND((DATEDIFF(LEAST(sr.affective_date - INTERVAL 1 DAY, p_pay_period_end), 
                        GREATEST(sr.previous_affective_date + INTERVAL 1 DAY, p_pay_period_start)) + 1) * 
              (sr.salary / p_fixed_work_days), 2) AS period_total
    FROM temp_salary_ranges sr
    -- Only include salary ranges that overlap with the pay period
    WHERE sr.affective_date > p_pay_period_start
       OR sr.previous_affective_date < p_pay_period_end

    -- Add the final segment if the pay period ends after the last salary change
    UNION ALL
    SELECT
        GREATEST((SELECT MAX(affective_date) FROM salary), p_pay_period_start) AS period_start,
        p_pay_period_end AS period_end,
        DATEDIFF(p_pay_period_end, GREATEST((SELECT MAX(affective_date) FROM salary), p_pay_period_start)) + 1 AS days,
        (SELECT salary FROM salary WHERE affective_date = (SELECT MAX(affective_date) FROM salary)) AS salary_amount,
        ROUND((SELECT salary FROM salary WHERE affective_date = (SELECT MAX(affective_date) FROM salary)) / p_fixed_work_days, 2) AS daily_rate,
        ROUND((SELECT salary FROM salary WHERE affective_date = (SELECT MAX(affective_date) FROM salary)) / p_fixed_work_days / 8, 2) AS hourly_rate,
        ROUND((DATEDIFF(p_pay_period_end, GREATEST((SELECT MAX(affective_date) FROM salary), p_pay_period_start)) + 1) * 
              ((SELECT salary FROM salary WHERE affective_date = (SELECT MAX(affective_date) FROM salary)) / p_fixed_work_days), 2) AS period_total
    WHERE (SELECT MAX(affective_date) FROM salary) < p_pay_period_end

    ORDER BY period_start;

    -- Clean up temp table
    DROP TEMPORARY TABLE IF EXISTS temp_salary_ranges;
END //

DELIMITER ;

How It Works

Let's break down the key parts to match your manual calculation:

  • Temporary Table: The temp_salary_ranges table uses the LAG() window function to pair each salary effective date with the previous one. This lets us define the date range each salary was active (e.g., between the prior effective date +1 and current effective date -1).
  • Segment Clamping: GREATEST() and LEAST() ensure we only calculate days within your specified pay period—no extra days outside the 2018-03-23 to 2018-04-22 range.
  • Final Segment Handling: If your pay period goes beyond the last salary change, the UNION ALL adds that final segment so you don't miss any days.
  • Precision: All rates and totals use ROUND() to keep 2 decimal places, just like your manual calculations.

Test It With Your Example

Run this call to get exactly the breakdown you manually calculated (note: I noticed your manual count for 2018-04-20 to 2018-04-22 was 2 days, but that's actually 3 days—20,21,22. The procedure calculates the correct day count):

CALL CalculateProRataSalary('2018-03-23', '2018-04-22', 26);

You'll get this result set:

period_startperiod_enddayssalary_amountdaily_ratehourly_rateperiod_total
2018-03-232018-03-242250.009.621.2019.24
2018-03-252018-03-283300.0011.541.4434.62
2018-03-292018-04-1919350.0013.461.68255.74
2018-04-202018-04-223400.0015.381.9246.14

内容的提问来源于stack exchange,提问作者Sokea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:18:07