【问题标题】:SQL code updating table with same values for each rowsSQL 代码更新表,每行具有相同的值
【发布时间】:2019-02-17 16:29:59
【问题描述】:

我需要使用游标完成此问题的第二部分 (https://imgur.com/yyoZxsw),但我的代码正在使用相同的值更新每一行。基本上我需要检查时间和超时是否在某个范围内,例如上午 9 点到下午 12 点之间,付款将是 350。另外,如果它是从上午 10 点到下午 16 点,我需要在 2 个范围之间进行计算。

我尝试了下面的代码,但它不起作用。预计会通过timeintimeout 计算要支付的金额到amtpaid 列。

create table babysitter (
babysitterid char(5) not null primary key,
datein date not null,
timein time not null,
dateout date not null,
timeout time not null, 
noofhrswrk int,
amtpaid int
);

insert into babysitter values('BS001', '18-Jan-2019', '10:00', '18-Jan- 
2019', '16:00', '', '')
insert into babysitter values('BS002', '15-Jan-2019', '13:00', '15-Jan- 
2019', 
'20:00', '', '')
insert into babysitter values('BS003', '21-Jan-2019', '21:00', '21-Jan- 
2019', 
'07:00', '', '')
insert into babysitter values('BS004', '11-Jan-2019', '08:00', '11-Jan- 
2019', '13:00', '', '')

declare @timein time
declare @timeout time
declare @hoursworked datetime

declare Calculate_No_Hrs cursor for 
select timein, timeout, noofhrswrk from babysitter

open Calculate_No_Hrs

fetch next from Calculate_No_Hrs into @timein, @timeout, @hoursworked

while (@@FETCH_STATUS = 0)
begin
update babysitter
set noofhrswrk = abs(datediff(hour, timeout, timein)) 

fetch next from Calculate_No_Hrs into @timein, @timeout, @hoursworked
end

close Calculate_No_Hrs
deallocate Calculate_No_Hrs ---end first question
--------------------------------------------------------------

declare @timein time
declare @timeout time
declare @amount int
declare @hourswrk int 
declare @pay int

set @pay = 0

declare Amt_Paid cursor for 
    select timein, timeout, noofhrswrk, amtpaid 
    from babysitter

open Amt_Paid

fetch next from Amt_Paid into @timein, @timeout, @hourswrk, @amount

while (@@FETCH_STATUS = 0)
begin
    if (@timein >= '09:00' and @timeout <= '12:00')
    begin
        set @amount = 350 * @hourswrk
        set @pay += @amount

        -- update babysitter
        -- set amtpaid = @amount
     end

     if (@timein >= '12:00' and @timeout <= '17:00')
     begin
         set @amount = 400 * @hourswrk
         set @pay += @amount

         -- update babysitter
         -- set amtpaid = @amount
     end

     if (@timein >= '17:00' and @timeout <= '21:00')
     begin
         set @amount = 500 * @hourswrk
         set @pay += @amount

         -- update babysitter
         -- set amtpaid = @amount
     end

     if (@timein >= '21:00' and @timeout <= '00:00')
     begin
         set @amount = 600 * @hourswrk
         set @pay += @amount

         -- update babysitter
         -- set amtpaid = @amount
     end

     if (@timein >= '00:00' and @timeout <= '07:00')
     begin
         set @amount = 800 * @hourswrk

         -- update babysitter
         -- set amtpaid = @amount
     end

     update babysitter
     set amtpaid = @pay

     fetch next from Amt_Paid into @timein, @timeout, @hourswrk, @amount
end

close Amt_Paid
deallocate Amt_Paid

【问题讨论】:

  • 为什么要使用光标来执行此操作?我建议你可以在没有人的情况下做到这一点。就像我在您的另一个(现已删除)问题中提到的那样,将您的问题的上下文放在您的问题中;不在场外资源上。另外,不要忘记,我们无权访问您的数据(或作业),因此如果没有 DDL 和 DML,我们将无法运行上述内容。
  • @Larnu 我认为这只是使用光标的问题练习是要求之一。 @Cat_img.jpeg 我没有检查您的代码是否有效,但如果您不在update 语句中指定babysiterid,那将毫无意义。
  • 您需要确定每个单独范围内的工作小时数,并将适当的费率应用于这些小时数,例如从 10:00 到 14:00 工作的人以 350 卢比/小时的价格工作了 2 小时,以 400 卢比/小时的价格工作了 2 小时,对吧?
  • @HABO 是的,我在第一部分完成了这项工作,并将其存储在名为noofhrswrk的列中
  • @Larnu 我已经用我的数据更新了帖子,但是对于第三行,我从 21:00 到 07:00 得到了 14 小时而不是 10 小时

标签: sql-server tsql database-cursor


【解决方案1】:

有一个想法你需要问;从 08:00 起,保姆的报酬是多少,不在任何范围内。就我个人而言,我会重建整个日期时间并添加一小时,直到 datetimeIn

SQL 标准函数不是DATEADDDATE_ADD,也许你应该改变它。

declare @time time, @timeout time
declare @dateIn date, @dateOut date
declare @BabysiterID NVARCHAR(10)
declare @pay int = 0
declare Amt_Paid cursor for 
select BabysiterID, timein, timeout, DateIn, DateOut from @table

open Amt_Paid

fetch next from Amt_Paid into @BabysiterID, @time, @timeout, @dateIn, @dateOut

while (@@FETCH_STATUS = 0) begin

    while (@time < @timeout) or (@dateIn < @dateOut) BEGIN

    if (@time >= '09:00' and @time < '12:00') begin
        set @pay += 350 end

    if (@time >= '12:00' and @time < '17:00') begin
        set @pay += 400 end

    if (@time >= '17:00' and @time < '21:00') begin
        set @pay += 500 end

    if (@time >= '21:00') begin
        set @pay += 600 end 

    if (@time >= '00:00' and @time < '07:00') begin
        set @pay += 800 end   

    SELECT @time = DATEADD(HOUR,1,@time)    

    IF(@time = '00:00') BEGIN SELECT @dateIn = DATEADD(DAY,1,@dateIn) END

    END -- while

    update @table
     set amtpaid = @pay
    where BabysiterID = @BabysiterID

    set @pay = 0

    fetch next from Amt_Paid into @BabysiterID, @time, @timeout, @dateIn, @dateOut
end --cursor

close Amt_Paid
deallocate Amt_Paid

【讨论】:

  • 您似乎将可能适用的各种小时费率相加,但从未乘以任何费率下的工作小时数。
  • @HABO 如果我们逐小时通过 while 循环,则无需乘以工作时间。
  • @HABO 建议通过excel(例如)计算结果,然后运行脚本。测试脚本是否返回了正确的结果。
  • 好吧,这个行得通,但是 1 行的金额为 0,小时数显示为 14 而不是 10,它适用于 21:00 到 07:00
  • @Cat_img.jpeg 第三行返回 0,因为您的输入数据不正确。 insert into babysitter values('BS003', '21-Jan-2019', '21:00', '21-Jan- 2019', '07:00', '', '') dateIn 和 dateOut 等于 timeIn > timeOut。
【解决方案2】:

检查此代码

DECLARE @timein TIME, @timeout TIME
DECLARE @amount INT, @hourswrk INT

SET @timein = '13:00'
SET @timeout = '20:00';

SET @hourswrk = 7

if (@timein > '7:00' AND @timeout <= '12:00:00')
BEGIN
set @amount = 350 * @hourswrk
END
else if (@timeout <= '17:00:00')
BEGIN
set @amount = 400 * @hourswrk
END
else if (@timeout <= '21:00:00')
BEGIN
set @amount = 500 * @hourswrk
END
else if (@timeout <= '00:00')
BEGIN
set @amount = 600 * @hourswrk
END
else if (@timeout <= '07:00')
BEGIN
set @amount = 800 * @hourswrk
END

【讨论】:

    【解决方案3】:

    代码末尾的各种select 语句可用于查看CTE 的中间结果,并逐步了解正在执行的操作。 (这也是一种将问题分解为更简单的部分并一次调试一个的常用方法。)

    值得注意的是DateDiff 返回越界次数。更多信息请参考documentation

    留给读者的练习是拆开 CTE 以按照 OP 的要求使用游标和循环。

    declare @Rates as Table ( RateId Int Identity, Shift Int, StartTime Time, EndTime Time, Rate Decimal(6,2) );
    -- A rate is applicable from the   StartTime   up to, but not including, the   EndTime .
    -- StartTime < EndTime   unless the   EndTime   is 00:00 indicating a rate that applies until midnight.
    -- A rate cannot span across midnight, but two entries for a single shift may be used to continue a rate past midnight.
    insert into @Rates ( Shift, StartTime, EndTime, Rate ) values
      ( 5, '00:00', '07:00', 800.00 ),
      ( 0, '07:00', '09:00', 0.00 ), -- No rate supplied in homework assignment.
      ( 1, '09:00', '12:00', 350.00 ),
      ( 2, '12:00', '17:00', 400.00 ),
      ( 3, '17:00', '21:00', 500.00 ),
      ( 4, '21:00', '00:00', 600.00 );
    
    select * from @Rates order by Shift;
    
    declare @Work as Table ( WorkId Int Identity, WorkerId Int, Started DateTime, Ended DateTime );
    insert into @Work ( WorkerId, Started, Ended ) values
      ( 1, '2000-01-01T11:00:00', '2000-01-01T11:15:00' ), -- Single rate.
      ( 1, '2000-01-01T09:00:00', '2000-01-01T12:00:00' ), -- Single rate.
      ( 1, '2000-01-01T10:00:00', '2000-01-01T16:00:00' ), -- Multiple rates.
      ( 5, '2000-01-01T00:00:00', '2000-01-01T04:00:00' ), -- Single rate starting at midnight.
      ( 6, '2000-01-01T10:00:00', '2000-01-02T00:00:00' ), -- Multiple rates ending at midnight.
      ( 7, '2000-01-01T10:00:00', '2000-01-02T16:00:00' ), -- Multiple dates and rates.
      ( 8, '2000-01-01T10:00:00', '2000-01-03T16:00:00' ), -- Multiple dates and rates.
      ( 9, '2000-01-01T10:00:00', '2000-01-04T00:00:00' ); -- Multiple dates and rates.
    
    select * from @Work order by Started, WorkerId;
    
    declare @Midnight as Time = '00:00'; -- For easier reading.
    
    with
      Ten ( Number ) as ( select * from ( values (0), (1), (2), (3), (4), (5), (6), (7), (8), (9) ) as Digits( Number ) ),
      TenUp2 ( Number ) as ( select 42 from Ten as L cross join Ten as R ),
      Numbers ( Number ) as ( select Row_Number() over ( order by ( select NULL ) ) from TenUp2 ),
      Work as ( -- Split out the date/times into separate date and time columns.
        select WorkId, WorkerId,
          Cast( Started as Date ) as StartedDate, Cast( Started as Time ) as StartedTime,
          Cast( Ended as Date ) as EndedDate, Cast( Ended as Time ) as EndedTime
          from @Work ),
      WorkOverDates as ( -- Split work across dates into separate rows for each date.
        -- Work completed in a single day.
        select WorkId, WorkerId, StartedDate, StartedTime, EndedDate, EndedTime
          from Work
          where StartedDate = EndedDate
        union
        -- First day of work that spans dates.
        select WorkId, WorkerId, StartedDate, StartedTime, StartedDate, @Midnight
          from Work
          where StartedDate <> EndedDate
        union
        -- Last day of work that spans dates.
        select WorkId, WorkerId, EndedDate, @Midnight, EndedDate, EndedTime
          from Work
          where StartedDate <> EndedDate and EndedTime <> @Midnight
        union
        -- Add any intermediate days, just in case someone worked a really long time.
        select WorkId, WorkerId, DateAdd( day, N.Number, StartedDate ), @Midnight, DateAdd( day, N.Number, StartedDate ), @Midnight
          from Work as W inner join
            Numbers as N on N.Number < DateDiff( day, StartedDate, EndedDate )
          where DateDiff( day, StartedDate, EndedDate ) > 1 ),
      WorkOverRates as ( -- For each work row generate rows for all of the applicable rates (for each date).
        select WOD.WorkId, WOD.WorkerId, WOD.StartedDate, WOD.StartedTime, WOD.EndedDate, WOD.EndedTime,
          R.RateId, R.Shift, R.StartTime, R.EndTime, R.Rate
          from WorkOverDates as WOD inner join
            -- The general test for overlapping ranges is: Start1 <= End2 and Start2 <= End1.
            @Rates as R on ( WOD.StartedTime < R.EndTime or R.EndTime = @Midnight ) and
              ( R.StartTime < WOD.EndedTime or WOD.EndedTime = @Midnight ) ),
      PaidIntervals as ( -- Determine the hours worked from at each rate (for each date).
        select WorkId, WorkerId, StartedDate, StartedTime, EndedDate, EndedTime,
          RateId, Shift, StartTime, EndTime, Rate,
          DateDiff( millisecond,
            -- From the later of the work or rate start time to ...
            case when StartedTime < StartTime then StartTime else StartedTime end,
            -- ... the earlier of the work or rate end time allowing for midnight.
            case
              when EndedTime = @Midnight and EndTime = @Midnight then DateAdd( day, 1, 0 )
              when EndedTime = @Midnight then EndTime
              when EndTime = @Midnight then EndedTime
              when EndedTime < EndTime then EndedTime
              else EndTime end ) / 3600000.0 as HoursWorked
          from WorkOverRates ),
        PaySummary as ( -- Summarize all of the rate periods for each   WorkId .
          select WorkId, Sum( HoursWorked ) as TotalHours, Count( 42 ) as RatePeriods,
            Sum( Rate * HoursWorked ) as TotalPay
            from PaidIntervals
            group by WorkId )
      -- To see the intermediate results in the CTE use one of the following   select   statements instead of the final   select :
      --select * from Numbers;
      --select * from Work order by WorkId;
      --select * from WorkOverDates order by WorkId, StartedDate;
      --select * from WorkOverRates order by WorkId, StartedDate, StartedTime;
      --select * from PaidIntervals order by WorkId, StartedDate, StartedTime;
      --select * from PaySummary order by WorkId;
      -- Put the summary together with the original work data.
      select W.WorkId, W.WorkerId, W.Started, W.Ended, PS.TotalHours, PS.RatePeriods, PS.TotalPay
        from @Work as W inner join
          PaySummary as PS on PS.WorkId = W.WorkId
        order by PS.WorkId;
    

    【讨论】:

    • 这个对我来说有点高级,但我会研究它,谢谢
    猜你喜欢
    • 1970-01-01
    • 2013-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    相关资源
    最近更新 更多