2016-07-05 7 views
2

私はSQLのクエリ(またはより良いLINQクエリ)を探して、休暇をキャンセルした人物を削除します。つまり、同じNAME、同じ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 

から取得する方法

?私は(これが正しいかどうかわからない)次に

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をを使用するすべてキャンセルされたレコードを検索し

Query1を試みた何

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 

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

内部クエリを1回だけ使用する方法はありますか?

(これは、.NET/C#のから呼び出され、Oracleデータベースです)

答えて

2

あなたは次のようなクエリを使用することができます。

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

NAME, START, ENDグループを取得するために仮定(キャンセルされましたキャンセル記録のDAYS_TAKENは、最初の記録の日を無効にする)。

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 |START  |END   
--------|-----------|---------- 
Alice | 1 June | 3 June 
Charles | 2 June | 14 June 

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 
1

あなたは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)のインデックスを利用することができます。ここで

1

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の予約語を使用することはお勧めできませんが、しかし、もしあなたがそれをしなければならないなら、ここのように '' 'を使ってください。

0

私はGiorgosの答えを使用してこのLinqソリューションを考え出しました。この解決策はまた、休暇を複数回キャンセル/適用する人々を考慮する。以下のアリスとエドガーを参照してください。

サンプル・データ

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バージョン12対11に注意)

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 

条件 "MOD(L2.CNT、2)<> 0"(またはLINQの中に "a.Count%2!= 0")1回だけ適用人を返しますまたは奇数回(例:適用 - 取消 - 適用)。しかし、申し込み - キャンセル - 適用 - キャンセルは除外されます。

関連する問題