从一个表中选择小时数,但从存在条件的另一个表中减去小时数?

时间:2017-11-06 05:40:27

标签: sql sql-server join

使用SQL Server并且不确定是否空白或尝试做我以前从未做过的事情!

为了简化,我正在为排班做一个Select查询。我有2张桌子:

[dbo].[LeaveBalances]
[PrimaryKey]    [Employee]    [LeaveType]    [AsAtDate]    [Balance]
1               100000        ANN            2017-10-23    10
2               100000        SIC            2017-10-23    16

[dbo].[P_R]
[PrimaryKey]    [Employee]    [ShiftDate]    [LeaveType]    [Hrs]
1               100000        2017-10-21     ANN            5
2               100000        2017-10-24     ANN            2
3               100000        2017-10-25     SIC            7
4               123456        2017-10-25     ANN            8

我希望查询获得休假余额的结果,除了[AsAtDate]之后的任何内容,是:

[Employee]    [LeaveType]    [AsAtDate]    [Balance]
100000        ANN            2017-10-23    8
100000        SIC            2017-10-23    9

所以2017-10-21休假将被忽略,因为它在日期之前,并且员工123456休假将被忽略,因为它是一个不同的雇员,但其他两个将从相应的休假类型余额中减去。 我到目前为止的查询是:

SELECT [Employee]
    ,[LeaveType]
    ,[AsAtDate]
    ,[Balance] -
        (SELECT SUM([Hrs]) FROM [dbo].[P_R] WHERE [Employee] = 100000)
        AS [Balance]
FROM [dbo].[LeaveBalances]
WHERE [Employee] = 100000

但显然,无论[P_R]离开类型或日期如何,都会从每个休假类型中减去员工100000的所有[Hrs]值。我如何融入其他条件?

5 个答案:

答案 0 :(得分:1)

基本上您要选择LeaveBalances的所有P_R条记录和总和小时数。您可以在SELECT子句中的子查询中执行此操作:

select
  employee,
  leavetype,
  asatdate,
  balance - (select sum(hrs) from dbo.p_r p where p.employee = b.employee
                                              and p.leavetype = b.leavetype
                                              and p.shiftdate > b.asatdate
            ) as balance
from dbo.leavebalances b
order by employee, leavetype;

或者使用FROM将子查询移动到OUTER APPLY子句:

select
  employee,
  leavetype,
  asatdate,
  balance - coalesce(sums.total, 0) as balance
from leavebalances b
outer apply
(
  select sum(hrs) as total
  from p_r p
  where p.employee = b.employee
    and p.leavetype = b.leavetype
    and p.shiftdate > b.asatdate
) sums
order by employee, leavetype;

答案 1 :(得分:0)

我设法解决了以下问题。在下面的CTE中,我汇总了每个员工在AsAtDate表中指定的LeaveBalances之后花费的病假天数。请注意,我必须在LeaveBalances上做一个明确的选择,以隔离每个员工的截止日期,这意味着您的数据未正常化。

WITH cte AS (
    SELECT t1.[Employee], t1.[LeaveType], SUM(t1.[Hrs]) AS [Hrs]
    FROM [dbo].[P_R] t1
    INNER JOIN
    (
        SELECT DISTINCT [Employee], [AsAtDate]
        FROM [dbo].[LeaveBalances]
    ) t2
        ON t1.[Employee] = t2.[Employee]
    WHERE t1.[ShiftDate] > t2.[AsAtDate]
    GROUP BY t1.[Employee], t1.[LeaveType]
)

SELECT
    t1.[Employee], t1.[LeaveType], t1.[AsAtDate],
    t1.[Balance] - COALESCE(t2.[Hrs], 0) AS [Balance]
FROM [dbo].[LeaveBalances] t1
LEFT JOIN cte t2
    ON t1.[Employee] = t2.[Employee] AND
       t1.[LeaveType] = t2.[LeaveType];

<强>输出:

enter image description here

在这里演示:

Rextester

答案 2 :(得分:0)

This Can be achive by Using CTE
BEGIN TRAN

CREATE TABLE [dbo].[LeaveBalances]([ID] INT,   [Employee] NVARCHAR(255),   [LeaveType] NVARCHAR(6) ,  [AsAtDate] DATETIME,   [Balance] INT)
CREATE TABLE[dbo].[P_R](ID INT,[Employee]NVARCHAR(255),    [ShiftDate]  DATETIME,   [LeaveType] NVARCHAR(6) ,  [Hrs] INT)

    INSERT INTO LeaveBalances
    SELECT 1,'100000','ANN','2017-10-23',10 UNION ALL
    SELECT 2,'100000','SIC','2017-10-23',16


    INSERT INTO P_R
    SELECT 1,'100000','2017-10-21','ANN',5 UNION ALL
    SELECT 2,'100000','2017-10-24','ANN', 2 UNION ALL
    SELECT 3 ,'100000','2017-10-25','SIC', 7 UNION ALL
    SELECT 4,'123456','2017-10-25','ANN',8

;WITH CTE  AS
 ( 

    SELECT DISTINCT a.Employee , a.LeaveType , a.AsAtDate , a.Balance
    FROM LeaveBalances a INNER JOIN  P_R P ON a.Employee=P.Employee 
    WHERE p.ShiftDate>a.AsAtDate 
    GROUP BY a.Employee , a.LeaveType , a.AsAtDate , a.Balance,p.Hrs

 )
 SELECT a.Employee , a.LeaveType , a.AsAtDate , a.Balance-b.Hrs Balance
 FROM CTE  a
 INNER JOIN P_R b ON a.Employee=b.Employee AND a.LeaveType=b.LeaveType
  WHERE b.ShiftDate > a.AsAtDate 
ROLLBACK TRAN

答案 3 :(得分:0)

我实际上设法提出了一个似乎有效的解决方案,不知道哪种方法最有效,但是这个方法看起来很优雅:

SELECT [LB].[Employee]
      ,[LB].[LeaveType]
      ,[LB].[AsAtDate]
      ,[LB].[Balance] - 
          SUM(CASE WHEN [R].[LeaveType] = [LB].[LeaveType] 
          AND [R].[ShiftDate] > [LB].[AsAtDate] 
          THEN [R].[Hrs] ELSE 0 END) [Balance]
  FROM [dbo].[LeaveBalances] [LB]
  LEFT JOIN [dbo].[P_R] [R] ON [LB].[Employee] = [R].[Employee]
  WHERE [LB].[Employee] = 100000 AND NOT [LB].[Entitlement] = 0
  GROUP BY [LB].[LeaveType]
      ,[LB].[Employee]
      ,[LB].[AsAtDate]
GO

答案 4 :(得分:0)

CREATE TABLE #LeaveBalances([ID] INT,   [Employee] NVARCHAR(255),   [LeaveType] NVARCHAR(6) ,  [AsAtDate] DATETIME,   [Balance] INT)
CREATE TABLE #P_R(ID INT,[Employee]NVARCHAR(255),    [ShiftDate]  DATETIME,   [LeaveType] NVARCHAR(6) ,  [Hrs] INT)

    INSERT INTO #LeaveBalances
    SELECT 1,'100000','ANN','2017-10-23',10 UNION ALL
    SELECT 2,'100000','SIC','2017-10-23',16

    INSERT INTO #P_R
    SELECT 1,'100000','2017-10-21','ANN',5 UNION ALL
    SELECT 2,'100000','2017-10-24','ANN', 2 UNION ALL
    SELECT 3 ,'100000','2017-10-25','SIC', 7 UNION ALL
    SELECT 4,'123456','2017-10-25','ANN',8;

;WITH CTE AS (
  SELECT  LBS.Employee,LBS.LeaveType,LBS.AsAtDate,LBS.Balance,#P_R.Hrs,
     ROW_NUMBER()OVER(
       PARTITION BY LBS.Employee,LBS.LeaveType 
       ORDER BY LBS.LeaveType,#P_R.ShiftDate DESC
                   ) AS RN 
    FROM #LeaveBalances AS LBS INNER JOIN #P_R 
        ON (LBS.Employee=#P_R.Employee) 
             AND
              (LBS.LeaveType=#P_R.LeaveType)
              )

SELECT Employee,LeaveType,CAST(AsAtDate AS DATE) AS AsAtDate,
       SUM(Balance-Hrs)over(PARTITION BY Employee,LeaveType ORDER BY LeaveType) AS Balance
        from CTE WHERE RN=1;
------------------------------------------
Employee    LeaveType   AsAtDate    Balance
---------------------------------------------
100000  ANN 2017-10-23  8
100000  SIC 2017-10-23  9