Advanced filter in SQL

僤鯓⒐⒋嵵緔 提交于 2019-12-08 10:20:32

问题


I have a problem with filtering in my SQL. Please take a look at the schema of database.

Now, I'd like to get programmers who know PHP with skills ●●/●●● and Java with skills ●●●. I executed following query:

SELECT p.name, p.city FROM programmers p INNER JOIN skills s
ON p.id = s.programmer
WHERE ( s.lang = 'PHP' AND s.level > 1 ) OR ( s.lang = 'Java' AND s.level = 3 );

What I got is

  • Joshua, Atlanta (because Java = 3)
  • Joshua, Atlanta (because PHP > 1)
  • Christopher, Chicago (because PHP > 1)

But Christopher does not know Java, yet he is returned. Replacing OR with AND between main conditions cause nothing is returned.

Moreover, I want to get all skills of a programmer in one row; here Joshua is in two separate rows. How to fix it?

All things considered, last questions. How to get:

  • programmers with skills PHP ●●/●●●, JS ●●● and HTML ●/●●/●●●
  • programmers with skills PHP ●●/●●● and PHP experience 2+ years and JS ●●● with experience 4+ years
  • programmers with skills PHP ●/●●/●●● and JS ●●● with experience at least 3+ years in both/any

Hope it's possible. Thanks in advance.


EDIT Operations focused on dates are no problem and there are no need to calculate the difference between dates, SQL can contain just year, i.e. 2010. Generally date is not the point.


回答1:


This is an example of a "set-within-sets" problem. I like to solve these using aggregation and having:

SELECT p.name, p.city
FROM programmers p INNER JOIN
     skills s
     ON p.id = s.programmer
GROUP BY p.name, p.city
HAVING SUM( s.lang = 'PHP' AND s.level > 1 ) > 0 AND
       SUM( s.lang = 'Java' AND s.level = 3 ) > 0;

EDIT:

If you want the list of skills (which wasn't part of the original question), then use group_concat():

SELECT p.name, p.city, group_concat(s.lang, ':', s.level separator '; ');
FROM programmers p INNER JOIN
     skills s
     ON p.id = s.programmer
GROUP BY p.name, p.city
HAVING SUM( s.lang = 'PHP' AND s.level > 1 ) > 0 AND
       SUM( s.lang = 'Java' AND s.level = 3 ) > 0;



回答2:


This will give you what you want, but it's not at all flexible.

select p.name, p.city, php.lang, php.level, j.lang, j.level
from   programmer p
join   skills     php
  on   php.programmer = p.id
join   skills     j
  on   j.programmer = p.id
where  php.lang = 'PHP' and php.level > 1
  and  j.lang = 'Java' and j.level = 3;

If you will be doing a lot of these kinds of queries, you might look into creating a view for each skill set:

create view JavaSkills as
select programmer, lang, level, exp_from
from   skills
where  lang = 'Java'

Then the query would be

select p.name, p.city, php.lang, php.level, j.lang, j.level
from   programmer p
join   PhpSkills  php
  on   php.programmer = p.id
join   JavaSkills j
  on   j.programmer = p.id
where  php.level > 1
  and  j.level = 3;

The execution plan should come out to be similar if not identical but the query is a little easier to read. This is also rather awkward to maintain, but one does what one can. ;)



来源:https://stackoverflow.com/questions/27253759/advanced-filter-in-sql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!