2015-06-19 51 views
2

我有以下數據表中的SQL查詢來發現與不同值的行某些列

Employee id  Status  email   partition 
      A   P   [email protected]  1 
      A   P   [email protected]  2 

      D   T   [email protected]  1 
      D   T   [email protected]  2 

      G   P   [email protected]  1 
      G   T   [email protected]  2 

我們期待一個員工的所有三個列應該是相同的分區1和2。如果有任何員工對於這三列中的任何一個在分區1和2之間不同,應該返回這兩條記錄。

對於上述數據,查詢應該返回員工G的兩條記錄。 任何人都可以請幫忙查詢嗎?

+0

顯示您想要的輸出。 –

+0

@NareshK你可以使用分析** LAG()**。 –

+3

你需要這個查詢,因爲你已經非規範化搞砸了數據,承認它;) – Drew

回答

1

此代碼將返回所有行,其中員工(單個記錄)沒有分區= 2或某些字段在兩行中不同。

select t1.*, t2.* 
from tbl t1 
    left join tbl t2 
    on t2.Employee_id = t1.Employee_id 
    AND t2.partition > t1.partition 
where t2.Employee_id is null 
OR t1.Status != t2.Status 
or t1.email != t2.email 
+0

對於每個員工,應該有兩個分別分區= 1和分區= 2的記錄。 – Pro

+0

只需在tbl t3(選擇t1.Employee_id作爲Employee_id1,t2.Employee_id作爲Employee_id2 <..>)選擇*之外設置爲t2,其中t3.Employee_id = t2.Employee_id1或t3.Employee_id = t2。Employee_id2' – BaBL86

+0

@NareshK你說「應該有兩條記錄」,但是你對這個查詢的需求正是因爲你有數據*而不是它應該如何*。一旦你開始了非規範化路線,就不可能預測數據如何被破壞。 – APC

0

這應該給您預期的結果:

select * from tablename where employee_id in (
    select t1.employee_id 
    from tablename t1 
     left outer join tablename t2 on t1.employee_id = t2.t2.employee_id and t1.status = t2.status and t1.email=t2.email and t1.partition=1 and t2.partition=2 
    where t2.employeeid is null) 
0

您可以更改表的結構insted的編寫複雜的查詢,

簡單的解決方案是有2個表

  1. 以員工ID爲主鍵的員工表(EmployeeID,狀態,電子郵件)
  2. 表分區(EID外鍵,分區號)。

這將確保您有更好的設計和非冗餘表格。

+0

EmailAddr通常是一個可怕的想法pk – Drew

+0

@DrewPierce我承認給了很多假賬戶,但是當它是一個組織的內部數據庫時,這會很安靜。 –

+0

從我的思維中根本沒有想到假的,而是歸一化的數據和第三範式。我甚至沒有員工表中的電子郵件地址,這是愚蠢的。谷歌第三範式 – Drew

0

您可以使用分析LAG()函數。

設置

CREATE TABLE t 
    (
    Employee_id VARCHAR2(1), 
    Status  VARCHAR2(1), 
    email  VARCHAR2(10), 
    partition INT 
); 
​ 
INSERT ALL 
    INTO t (Employee_id, Status, email, partition) 
     VALUES ('A', 'P', '[email protected]', 1) 
    INTO t (Employee_id, Status, email, partition) 
     VALUES ('A', 'P', '[email protected]', 2) 
    INTO t (Employee_id, Status, email, partition) 
     VALUES ('D', 'T', '[email protected]', 1) 
    INTO t (Employee_id, Status, email, partition) 
     VALUES ('D', 'T', '[email protected]', 2) 
    INTO t (Employee_id, Status, email, partition) 
     VALUES ('G', 'P', '[email protected]', 1) 
    INTO t (Employee_id, Status, email, partition) 
     VALUES ('G', 'T', '[email protected]', 2) 
SELECT * FROM dual; 
COMMIT; 

查詢

SQL> WITH t1 AS(
    2 SELECT t.*, LAG(status) OVER(PARTITION BY employee_id, email ORDER BY status) rn FROM t 
    3 ), 
    4 t2 AS(
    5 SELECT Employee_id, Status, email, PARTITION FROM t1 
    6 WHERE 
    7 status <> rn 
    8 ) 
    9 SELECT t.Employee_id, 
10 t.Status, 
11 t.email, 
12 t.partition 
13 FROM t, 
14 t2 
15 WHERE t.Employee_id = t2.Employee_id 
16 ORDER BY t.partition; 

EMPLOYEE_ID STATUS EMAIL  PARTITION 
----------- ------ ---------- ---------- 
G   P  [email protected]   1 
G   T  [email protected]   2 

SQL> 
+0

這適合具體的樣本數據,但不會找到匹配的STATUS行,但有不同的EMAIL – APC

+0

@APC OP聲明*我們期望所有三列一個員工應該是相同的分區1和2 *這意味着EMAIL將是相同的。 –

+0

那麼「所有三列」也意味着STATUS應該是一樣的。爲什麼測試一列失敗的規則而不是另一列呢? – APC

0

試試這個,

Select 
    t1.* 
from 
    table t1, table t2 
where 
    t1.partition < t2.partition 
and t1.employee_id = t2.employee_id 
and (t1.status != t2.status or t1.email !=t2.email) 

Union all 

Select 
    t2.* 
from 
    table t1, table t2 
where 
    t1.partition < t2.partition 
and t1.employee_id = t2.employee_id 
and (t1.status != t2.status or t1.email !=t2.email) 
+0

對不起,我錯過了在查詢中添加臨時表,我已經更新了答案。 –

0

這是很普遍的和命中的數據只有一次:

select employee_id, status, email, partition 
    from ( 
    select test.*, 
     count(1) over (partition by employee_id, status, email) cnt1, 
     count(1) over (partition by employee_id) cnt2 
     from test) 
    where cnt1 <> cnt2 

SQLFiddle

這個查詢還將處理情況,當有3個或更多行的一個人,而不是所有的匹配。 如果一名員工只有一行 - 而您想將其顯示爲異常 - 請在最後一行添加or cnt2 = 1