2015-12-28 71 views
1

我編寫了一個過濾器,它生成一個查詢以顯示特殊員工。關係部門SQL

我有餐桌員工和很多1:1,1:n和n:m的關係,例如技能和語言像這樣的員工:

Employees 
id name 
1 John 
2 Mike 

Skills 
id skill experience 
1 PHP 3 
2 SQL 1 

Employee_Skills 
eid sid 
1 1 
1 2 

現在我要過濾已在使用PHP,1年SQL至少有2年工作經驗的員工。

我的過濾器始終爲每個表,關係和字段生成正確的工作查詢。

但現在我的問題是,當我想在一個相關的表中多次過濾同一個字段並且它不起作用。

e.g. 
John PHP 3 
John SQL 1 

PHP和SQL是不同的行,所以AND不能工作。

我嘗試使用GROUP_CONCAT和FIND_IN_SET但我有,我不能超過2年FIND_IN_SET和FIND_IN_SET不知道PHP是三濾的經驗問題,SQL是1

我也試過

WHERE emp.id IN (SELECT eid FROM Employee_Skills WHERE sid IN (SELECT id FROM Skills WHERE skill = 'PHP' AND experience > 1)) AND emp.id IN (SELECT eid FROM Employee_Skills WHERE sid IN (SELECT id FROM Skills WHERE skill = 'SQL' AND experience > 0)) 

它適用於這個例子,但它只適用於n:m,並且知道關係類型太複雜。

我有

ski.skill = 'PHP' AND ski.experience > 1 AND ski.skill = 'SQL' AND ski.experience > 0 

最終查詢,我想操縱查詢,使其工作。

查詢如何看起來像處理關係分割。

+0

幫你一個忙,不要這樣稱呼你的聯合表('emp_ski')。使用清晰/有意義的內容,例如'employee_skills'或'employee_to_skills'。你會很高興你以後做。 –

+1

現場'體驗'應該在'Emp_Ski'表 –

+0

這個數據庫非常龐大複雜,這只是一個小例子來解釋我的問題。 – phpjssql

回答

1

你可以嘗試下方法:

select * from Employees 
where id in (
    select eid 
    from Employee_Skills as a 
    inner join 
    Skills as ski 
    on (a.sid = ski.id) 
    where 
    (ski.skill = 'PHP' AND a.experience > 2) OR 
    (ski.skill = 'SQL' AND a.experience > 1) 
    group by eid 
    having count(*) = 2 
) 

因此,對於每一個過濾器將添加或陳述,having將篩選員工通過了所有過濾器,只是通過適當數量

+0

但是,當我想用​​PHP和(SQL或JS)篩選員工時,我無法使用HAVING。 – phpjssql

+0

你仍然可以使用這種方法,比如'(ski.skill ='PHP'AND a.experience> 2)OR((ski.skill ='SQL'AND a.experience> 1)或者(ski.skill ='JS 'AND a.experience> 1))''但它當然不是普遍的(它不適用於NOT),普遍的方法就像你用'IN'描述的那樣 –

0

你可以做一個樣數據透視查詢,在這裏你將經驗放在列中所有已知技能的每一箇中。這可能是一個漫長的查詢,但你可以在PHP動態構建它,所以它會作爲列添加的所有技能,最終的查詢,這應該是這樣的:

SELECT  e.*, php_exp, sql_exp 
FROM  Employee e 
INNER JOIN (
     SELECT  es.eid, 
        SUM(CASE s.skill WHEN 'PHP' THEN s.experience END) php_exp, 
        SUM(CASE s.skill WHEN 'SQL' THEN s.experience END) sql_exp, 
        SUM(CASE s.skill WHEN 'JS' THEN s.experience END) js_exp 
        -- do the same for other skills here -- 
     FROM  Employee_Skills es 
     INNER JOIN Skills s ON es.sid = s.id 
     GROUP BY es.eid 
     ) pivot ON pivot.eid = e.id 
WHERE  php_exp > 2 AND sql_exp > 0; 

WHERE子句則非常簡潔,直觀:在其他情況下使用邏輯運算符。

如果技能集合相當靜態,您甚至可以爲子查詢創建一個視圖。然後最後的SQL非常簡潔。

這裏是一個fiddle

替代

利用同樣的原理,但HAVING子句中使用SUM,你能避免收集所有技能的經驗:

SELECT  e.* 
FROM  Employee e 
INNER JOIN (
     SELECT  es.eid 
     FROM  Employee_Skills es 
     INNER JOIN Skills s ON es.sid = s.id 
     GROUP BY es.eid 
     HAVING  SUM(CASE s.skill WHEN 'PHP' THEN s.experience END) > 2 
     AND  SUM(CASE s.skill WHEN 'SQL' THEN s.experience END) > 0 
     ) pivot ON pivot.eid = e.id; 

這裏是一個fiddle

您也可以通過IF函數替換CASE結構,像這樣:

 HAVING  SUM(IF(s.skill='PHP', s.experience, 0)) > 2 
     ... etc. 

但它歸結爲同一個。

0

直截了當的方法是反覆JOIN技能:

SELECT e.* 
    FROM Employees AS e 

    JOIN Employee_Skills AS j1 ON (e.id = j1.eid) 
    JOIN Skills AS s1 ON (j1.sid = s1.id AND s1.skill = 'PHP' AND s1.experience > 3) 

    JOIN Employee_Skills AS j2 ON (e.id = j2.eid) 
    JOIN Skills AS s2 ON (j2.sid = s2.id AND s2.skill = 'SQL' AND s2.experience > 1) 

    ... 

由於需要所有條款這翻譯成一條直線JOIN。

您將需要爲每個子句添加兩個JOIN,但它們非常快速連接。

一個比較蹩腳的方法是將技能壓縮成與員工1:1關係的代碼。如果經驗永遠不會超過30,那麼你可以將第一個條件的經驗乘以1,第二個乘以30,第三個乘以30 * 30,第四個乘以30 * 30 * 30 ...,並且不會發生溢出。

SELECT eid, SUM(CASE skill 
         WHEN 'PHP' THEN 30*experience 
         WHEN 'SQL' THEN 1*experience) AS code 
FROM Employees_Skills JOIN Skills ON (Skills.id = Employees_Skills.sid) 
GROUP BY eid HAVING code > 0; 

其實既然你想3年PHP,你可以有代碼> 91.如果您有相關的經歷2,3和5三個條件,你會要求超過X = 2 * 30 * 30 + 3 * 30 + 5。這隻能減少結果,因爲3 * 30 * 30 + 2 * 30 + 4仍然通過濾波器,但對您沒有用處。但是既然你想限制代碼,「> x」的開銷與「> 0」相同,並且給出了更好的結果......(如果你需要比一系列AND更復雜的過濾,儘管> 0更安全)。

你上面與員工的加入,然後對結果執行真正的濾波,需要

((code/30*30) % 30) > 7 // for instance :-) 
AND 
((code/30) % 30) > 3  // for PHP 
AND 
((code/1) % 30) > 1  // for SQL 

的表(* 1和/ 1是多餘的,且僅插入到澄清)

這個解決方案需要對技能進行全表掃描,而沒有實際的自動優化的可能性。所以它比其他解決方案慢。另一方面,它的成本增長速度要慢得多,所以如果你有複雜的查詢,或者需要OR運算符或條件表達式而不是AND,那麼實現「hackish」解決方案可能會更方便。