【问题标题】:SQL Stored Procedure Help - Doing Actions on Multiple Recordsets And Then Comparing ResultsSQL 存储过程帮助 - 对多个记录集执行操作,然后比较结果
【发布时间】:2011-03-23 15:10:29
【问题描述】:

我在存储过程方面的经验有限,我希望这能解决我的工资单问题。此外,对于我试图完成的存储过程可能不是最好的答案。

我正在尝试一个工资核算函数,其中存储过程的目标是获取给定工资核算周期的数据,对第一个返回的员工进行数学运算,输出计算结果,然后转到下一个。让我再解释一下……

基于提供给存储过程的支付周期 ID,我希望它执行以下操作:

选择该期间的员工,添加他的总工作时间,然后添加员工的总佣金,以较大者为准 - 返回该值(连同总数 - 例如总小时数和总佣金) - 然后转移到下一位员工,直到完成。

数据排列如下:

工资周期表 PayrollID、DateBegin、DateEnd 存储过程一开始就给出了工资单 ID(作为 QueryString),所以我必须获取相应的 DateBegin 和 DateEnd 数据,并从 EmployeeTimeTable 中获取 DateTimeIn 和 DateTimeOut 以获取适当的出勤记录。

PayrollID - 支付周期的 ID。
DateBegin - 支付期的开始日期。
DateEnd - 支付周期的结束日期。

员工时间表 EmployeeID、DateTimeIn、DateTimeOut、HourlyRate、CalculatedHours、AmountPaid - 每天工作都有一个实例,因此存储过程将计算当天的工作时间。

EmployeeID - 与此相关的记录的员工 ID。
DateTimeIn - 员工打卡的日期和时间。
DateTimeOut - 员工打卡的日期和时间。
HourlyRate - 员工每小时支付的货币费率。
CalculatedHours - 为此记录工作的小时数。
AmountPaid - 只需 HourlyRate x CalculatedHours。

佣金表 EmployeeID、PayrollID、AmountPaid - 每笔销售赚取的“佣金”都有记录。

EmployeeID - 与此相关的记录的员工 ID。
PayrollID - 支付周期的 ID。
AmountPaid - 为此记录赚取的佣金金额。

所以我的问题是 - 我如何能够创建所描述的功能?甚至可能吗?这需要奇迹来完成吗!?或者我有更好的方法来解决这个问题吗?

此外,理论上这最终会输出到 WebForm GridView。

【问题讨论】:

  • SQL 的乐趣(一旦你习惯了它)就在于你指定你想要达到的结果,而不是你想如何达到它们。您无需编写一系列步骤,而是根据输入定义所需的输出集。不过,我们还没有足够的信息来回答这个问题——佣金表中也有日期吗?另外 AmountPaid 和 TotalHoursWorked 通常是两个不同的概念,那么我们如何比较它们以确定最大的呢?如果您可以发布实际的表定义和一些示例数据(和预期结果),那将非常有帮助。
  • 优点 - 让我更新我的帖子以澄清。
  • 我已经更新了我原来的帖子。佣金表中没有日期,它通过 ID 与保存日期的 PayrollTable 相关联。我们通常支付佣金的方式是基于基础与佣金的时间表:如果您的基础(支付期间的每小时总金额)高于您的佣金总额(给定支付期间的所有佣金加起来),那么您会得到报酬你的基地。反之亦然,如果您的佣金更高,您将获得佣金。
  • 看起来你需要一个 temptable 和一个 cursor 在你的 proc 中。在您的光标中,您将进行所有计算,然后将它们添加到您的临时表中,然后对光标中的下一个员工也是如此。最后,归还你的诱惑
  • 他们是否获得部分工作时间的报酬?还是我们总是四舍五入(向上/向下)?

标签: sql vb.net stored-procedures


【解决方案1】:

@Damien_The_Unbeliever 的建议是正确的。

但是,我认为您有 3 个选项可供您选择:

  • 编写单个 SQL 查询来检索 您正在寻找的数据。我认为 这是可能的,虽然它可能是 有点复杂。这将是 最快的解决方案,没有错误。这 你进行比较的方式可能是 通过联合和子查询。和 一个适当的模式,我相信人们会 在这个选项上有一个平底船。这是最纯粹的哲学方法,但如果您不是忍者级别的 SQL,也很棘手。
  • 编写一个查询来检索工资单期间所有活动的员工,将其返回到您的 VB 前端,并遍历结果集以运行单个查询以检索他们的工时和工资单;用VB做业务逻辑。这在性能方面有点欠佳,但允许您逐步工作,并使用一些开发人员更喜欢的 VB IDE。
  • 编写一个存储过程,在其中创建一个临时表来保存您的结果集,将员工记录插入到该临时表中,用于在工资核算期间有效的所有员工,然后编写特定查询以使用他们的小时数更新临时表工作,佣金值等。这将您在数据库中的所有业务逻辑本地化,并且可能执行得非常快;根据您的 SQL 的复杂程度,它可能会比选项 1 更快。它还允许您逐步处理业务逻辑,大多数开发人员发现这比在单个 SQL 语句中体现所有内容更容易。

我在路上,没有可以使用的 SQL Server 实例,所以下面的示例可能包含一些语法错误,但选项 3 的工作原理如下:

       create proc calculatePaySlipForPeriod @periodID int
    as
    begin

    create table #results
    (emp_id   int,
    emp_name varchar(255), 
    commission_earnt money, 
    labour_earnt money,
    amount_to_pay money)

    insert into #results
    select emp_id
    from employee_time
    where timeIn between (select dateBegin from PayrollPeriod where PayrollID = @periodID)
          and (select dateEnd from PayrollPeriod where PayrollID = @periodID)

    insert into #results
    select emp_id 
    from Commission
    where payrollID = @periodID
    and emp_id not in
      (select emp_id from #results)
   /** We now have all the employees in the table, so we can populate their earnings **/

   update #results
   set labour_earnt_money = sum(AmountPaid)
   from #results   r, 
        employee_time et
   where r.emp_id = et.emp_id
    and timeIn between (select dateBegin from PayrollPeriod where PayrollID = @periodID)
          and (select dateEnd from PayrollPeriod where PayrollID = @periodID)

  update #results
  set    commission_earnt = sum(AmountPaid)
  from   #results r, 
         Commission c
  where r.emp_id = c.emp_id
  and   c.payroll_id = @periodID

  update #results
  set amount_to_pay = commission_earnt
  where commission_earnt > labour_earnt

  update #results
  set amount_to_pay = labour_earnt
  where labour_earnt >= commission_earnt

  /** We now have all the data populated, so we return the table to the front end. 

  select * from #results

  /** no need to explicitly drop the table, happens automatically at the end of the proc.

    end

这是粗略的概述 - 您可能需要处理日期/时间的异常(当操作员在工资核算期间开始或结束时计算班次?我假设开始)。

通过逐步建立结果,您可以轻松了解正在发生的事情;要调试,您可以在执行期间选择 * 形成临时表。

【讨论】:

  • 你能提供一个简短的例子来说明我如何处理选项 3 吗?从你的建议来看,这似乎是最好的路线。我相信它会正常工作 - 然后我可以将“最终”数据转储到另一个表,然后清空临时表。
【解决方案2】:

假设 EmployeeTime 表如下所示:

CREATE TABLE EmployeeTime (
     EmployeeID int not null,
     DateTimeIn datetime not null,
     DateTimeOut datetime not null,
     HourlyRate decimal(10,2) not null,
     HoursWorked as (CONVERT(int,DATEDIFF(minute,DateTimeIn,DateTimeOut)/15.0)+1)/4.0,
     AmountPaid as (CONVERT(int,DATEDIFF(minute,DateTimeIn,DateTimeOut)/15.0)+1)/4.0 * HourlyRate
)

(不幸的是,您不能基于另一个计算列构建一个计算列。但是,您可以将公式 (CONVERT(int,DATEDIFF(minute,DateTimeIn,DateTimeOut)/15.0)+1)/4.0 移动到用户定义的函数中)

然后你会编写一个如下所示的查询:

;WITH PayrollPayments as (
     SELECT PayrollID,EmployeeID, SUM(HoursWorked) as HoursWorkedTotal,SUM(AmountPaid) as TotalPay,0 as Commission from EmployeeTime et inner join Payroll p on p.DateBegin < et.DateTimeOn and p.DateEnd > et.DateTimeOut GROUP BY PayrollID,EmployeeID
     UNION ALL
     SELECT PayrollID,EmployeeID,0,SUM(AmountPaid) as TotalPay,1 from Commissions
)
SELECT PayrollID,EmployeeID,MAX(HoursWorkedTotal) as TotalHours,MAX(TotalPay) as MaxPay,MAX(CASE WHEN Commission=0 THEN TotalPay END) as TotalPayment,MAX(CASE WHEN Commission=1 THEN TotalPay END) as TotalCommission
FROM PayrollPayments
WHERE
     PayrollID = @PayrollID

如果您的 EmployeeTime 表与上述不同,并且您无法更改现有的 EmployeeTime 表,您始终可以创建一个公共表表达式(类似于上述查询中的PayrollPayments),以添加工作时间/金额先计算。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    相关资源
    最近更新 更多