我编写了一个过滤器,用于生成查询以显示特殊员工。
我有桌子员工和很多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
现在我想过滤至少有2年使用PHP和1年SQL经验的员工。
我的过滤器始终为每个表,关系和字段生成正确的工作查询。
但现在我的问题是当我想用a过滤相关表中的相同字段多次时它不起作用。
e.g.
John PHP 3
John SQL 1
PHP和SQL是不同的行,因此AND无法工作。
我尝试使用group_concat和find_in_set,但我遇到的问题是我无法使用find_in_set过滤2年以上的经验,而find_in_set不知道PHP是3而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 :(得分: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
)
因此,对于每个过滤器,您将添加OR语句,having
将过滤所有过滤器的员工,只需传递适当的号码
答案 1 :(得分:0)
您可以进行一种数据透视查询,您可以将经验放在列中的所有已知技能中。这可能是一个很长的查询,但您可以在php中动态构建它,因此它会将所有技能作为列添加到最终查询中,如下所示:
>>> t = ((1,2,3),(1,3,1),(2,2,3),(0,2,2))
>>> sorted(t, key=sum)[0]
(0, 2, 2)
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;
子句非常简洁直观:在其他情况下使用逻辑运算符。
如果这组技能是静态的,你甚至可以为子查询创建一个视图。然后最终的SQL非常简洁。
这是fiddle。
<强>替代强>
使用相同的原则,但使用WHERE
条款中的SUM
,您可以避免收集所有技能的经验:
HAVING
这是fiddle。
你也可以用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;
函数替换CASE
构造,如下所示:
IF
但它归结为同样的。
答案 2 :(得分:0)
直截了当的方法是重复加入技能:
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,但它们是非常快速的连接。
一种更为骇人的方式是将技能压缩为与员工以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,你可以拥有代码&gt; 91.如果你有三个经历2,3和5的条件,你会要求超过x = 2 * 30 * 30 + 3 * 30 + 5.这只会减少结果,因为3 * 30 * 30 + 2 * 30 + 4仍然通过过滤器,但对你没用。但是因为你想要对代码进行限制,并且&#34;&gt; X&#34;费用与&#34;&gt;相同0&#34;并且给出了更好的结果......(如果你需要比一系列AND更复杂的过滤,但是> 0更安全)。
上面的表与Employees一起加入,然后在结果上执行真正的过滤,需要
((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,那么实现&#34; hackish&#34;可能会更方便。溶液