【问题标题】:Remove all records with opposite sign删除所有带有相反符号的记录
【发布时间】:2023-03-02 21:58:02
【问题描述】:

我正在寻找一个 SQL 查询(或者更好的是 LINQ 查询)来删除取消休假的人,即删除具有相同名称和相同 START 和 END 的所有记录,并且 DAYS_TAKEN 值仅在符号上有所不同.

如何从中获得

NAME    |DAYS_TAKEN |START      |END        |UNIQUE_LEAVE_ID    
--------|-----------|-----------|-----------|-----------
Alice   |  2        | 1 June    | 3 June    | 1 --remove because cancelled
Alice   | -2        | 1 June    | 3 June    | 2 --cancelled
Alice   |  3        | 5 June    | 8 June    | 3 --keep
Bob     | 10        | 4 June    | 14 June   | 4 --keep
Charles | 12        | 2 June    | 14 June   | 5 --remove because cancelled
Charles | -12       | 2 June    | 14 June   | 6 --cancelled
David   | 5         | 3 June    | 8 June    | 7 --keep

为了这个?

NAME    |DAYS_TAKEN |START      |END        |UNIQUE_LEAVE_ID    
--------|-----------|-----------|-----------|-----------
Alice   |  3        | 5 June    | 8 June    | 3 --keep
Bob     | 10        | 4 June    | 14 June   | 4 --keep
David   | 5         | 3 June    | 8 June    | 7 --keep

我尝试过的

Query1 查找所有取消的记录(不确定是否正确)

SELECT L1.UNIQUE_LEAVE_ID 
FROM LEAVE L1 
INNER JOIN LEAVE L2 ON L2.DAYS_TAKEN > 0 AND ABS(L1.DAYS_TAKEN) = L2.DAYS_TAKEN AND L1.NAME= L2.NAME AND L1.START = L2.START AND L1.END = L2.END
WHERE L1.DAYS_TAKEN < 0

然后我像这样在内部选择中使用 Query1 两次

SELECT L.* FROM LEAVE L WHERE 
L.UNIQUE_LEAVE_ID NOT IN (Query1)
AND L.UNIQUE_LEAVE_ID NOT IN (Query1)

有没有办法只使用一次内部查询?

(这是一个 Oracle 数据库,从 .NET/C# 调用)

【问题讨论】:

    标签: sql oracle linq


    【解决方案1】:

    我使用 Giorgos 的答案提出了这个 Linq 解决方案。该解决方案还考虑了多次取消/申请休假的人。请参阅下面的 Alice 和 Edgar。

    样本数据

      int id = 0;
      List<Leave> allLeave = new List<Leave>()
      {
        new Leave() { UniqueLeaveID=id++, Name="Alice", Start=new DateTime(2016,6,1), End=new DateTime(2016,6,3), Taken=-2 },
        new Leave() { UniqueLeaveID=id++,Name="Alice", Start=new DateTime(2016,6,1), End=new DateTime(2016,6,3), Taken=2 },
        new Leave() { UniqueLeaveID=id++, Name="Alice", Start=new DateTime(2016,6,1), End=new DateTime(2016,6,3), Taken=2 },
        new Leave() { UniqueLeaveID=id++,Name="Alice", Start=new DateTime(2016,6,3), End=new DateTime(2016,6,5), Taken=3 },
    
        new Leave() { UniqueLeaveID=id++,Name="Bob", Start=new DateTime(2016,6,4), End=new DateTime(2016,6,14), Taken=10 },
    
        new Leave() { UniqueLeaveID=id++,Name="Charles", Start=new DateTime(2016,6,2), End=new DateTime(2016,6,14), Taken=12 },
        new Leave() { UniqueLeaveID=id++,Name="Charles", Start=new DateTime(2016,6,2), End=new DateTime(2016,6,14), Taken=-12 },
    
        new Leave() { UniqueLeaveID=id++,Name="David", Start=new DateTime(2016,6,3), End=new DateTime(2016,6,8), Taken=5 },
    
                new Leave() { UniqueLeaveID=id++,Name="Edgar", Start=new DateTime(2016,6,3), End=new DateTime(2016,6,8), Taken=5 },
        new Leave() { UniqueLeaveID=id++,Name="Edgar", Start=new DateTime(2016,6,3), End=new DateTime(2016,6,8), Taken=5 },
        new Leave() { UniqueLeaveID=id++,Name="Edgar", Start=new DateTime(2016,6,3), End=new DateTime(2016,6,8), Taken=5 },
        new Leave() { UniqueLeaveID=id++,Name="Edgar", Start=new DateTime(2016,6,3), End=new DateTime(2016,6,8), Taken=5 }
    
      };
    

    Linq 查询(注意 Oracle 版本 11 和 12)

     var filteredLeave = allLeave
     .GroupBy(a => new { a.Name, a.Start, a.End })
     .Select(a => new { Group = a.OrderByDescending(b=>b.Taken), Count = a.Count() })
     .Where(a => a.Count % 2 != 0)
     .Select(a => a.Group.First());
    

    “OrderByDescending”确保只返回正数天数。

    Oracle SQL

    SELECT
    *
    FROM
    (
        SELECT 
        L1.NAME, L1.START, L1.END, MAX(TAKEN) AS TAKEN, COUNT(*) AS CNT
        FROM LEAVE L1
        GROUP BY L1.NAME, L1.START, L1.END
    ) L2
    WHERE MOD(L2.CNT,2)<>0 -- replace MOD with % for Microsoft SQL
    

    条件“WHERE MOD(L2.CNT,2)0”(或在 Linq 中为“a.Count % 2 != 0”)只返回申请一次或奇数次的人(例如申请 - 取消- 申请)。但是申请 - 取消 - 申请 - 取消的人被过滤掉了。

    【讨论】:

      【解决方案2】:

      这是SUM() OVER 的变体:

      SELECT x.*
        FROM (SELECT l.*, SUM (days_taken) OVER (PARTITION BY name, "START", "END", ABS (days_taken) ORDER BY NULL) s
                FROM leave l) x
       WHERE s <> 0
      

      如果你有 Oracle 12,这会给你取消:

      SELECT l.*
        FROM leave l,
             LATERAL (SELECT days_taken
                        FROM leave l2
                       WHERE l2.name = l.name 
                         AND l2."START" = l."START" 
                         AND l2."END" = l."END" 
                         AND l2.days_taken = -l.days_taken) x
      

      这是应该保留的:

      SELECT l.*
        FROM leave l
             OUTER APPLY (SELECT days_taken
                            FROM leave l2
                           WHERE l2.name = l.name 
                             AND l2."START" = l."START" 
                             AND l2."END" = l."END" 
                             AND l2.days_taken = -l.days_taken) x
       WHERE x.days_taken IS NULL
      

      关于列名的一些事情。不建议在Oracle SQL中使用保留字,但如果必须这样做,请使用'"',如这里。

      【讨论】:

        【解决方案3】:

        你可以使用not exists:

        select l.*
        from leave l
        where not exists (select 1
                          from leave l2
                          where l2.name = l.name and l2.start = l.start and
                                l2.end = l.name and l2.days_taken = - l.days_taken
                         );
        

        此查询可以利用leave(name, start, end, days_taken) 上的索引。

        【讨论】:

          【解决方案4】:

          您可以使用如下查询:

          SELECT NAME, START, END  
          FROM LEAVE
          GROUP BY NAME, START, END
          HAVING SUM(DAYS_TAKEN) = 0
          

          为了得到已经被取消的NAME, START, END组(假设取消记录的DAYS_TAKEN否定了初始记录的天数)。

          输出:

          NAME    |START      |END        
          --------|-----------|----------
          Alice   | 1 June    | 3 June
          Charles | 2 June    | 14 June
          

          将上述查询用作派生表,您可以获得与“已取消”组无关的记录:

          SELECT L1.NAME, L1.DAYS_TAKEN, L1.START, L1.END, L1.UNIQUE_LEAVE_ID  
          FROM LEAVE L1
          LEFT JOIN (
            SELECT NAME, START, END  
            FROM LEAVE
            GROUP BY NAME, START, END
            HAVING SUM(DAYS_TAKEN) = 0
          ) L2 ON L1.NAME = L2.NAME AND L1.START = L2.START AND L1.END = L2.END
          WHERE L2.NAME IS NULL
          

          输出:

          NAME    |DAYS_TAKEN |START      |END        |UNIQUE_LEAVE_ID    
          --------|-----------|-----------|-----------|-----------
          Alice   | 3         | 5 June    | 8 June    | 3 
          Bob     | 10        | 4 June    | 14 June   | 4 
          David   | 5         | 3 June    | 8 June    | 7 
          

          【讨论】:

            猜你喜欢
            • 2019-08-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-24
            • 2012-10-01
            • 2011-11-06
            相关资源
            最近更新 更多