postgresql 无法根据元组条件筛选行

bn31dyow  于 2023-02-18  发布在  PostgreSQL
关注(0)|答案(2)|浏览(103)

| 雇员标识|技能水平|技能标识|
| - ------|- ------|- ------|
| 小行星1550|初学者|五百六十|
| 小行星6540|初学者|五百六十|
| 小行星2354|中级|五百六十|
| 小行星665|高级|五百六十|
| 小行星1550|高级|七百八十|
| 小行星6540|初学者|七百八十|
| 小行星1550|中级|七百八十|
| 小行星2354|中级|七百八十|
| 小行星1550|中级|四百五十|
| 小行星6540|初学者|六五四|
| 小行星8888|初学者|五百六十|
| 小行星665|高级|四百五十五|
| 小行星1550|高级|一百一十|
| 小行星6540|高级|八八五|
| 小行星2354|高级|九八零|
| 小行星665|中级|八七零|
我只想得到具有特定技能和他们各自特定水平的员工;我会得到这样的结果:
| 雇员标识|技能水平|技能标识|
| - ------|- ------|- ------|
| 小行星1550|初学者|五百六十|
| 小行星1550|中级|七百八十|
我尝试了这个方法,但显然它不是我想要的,因为它有一个包含的OR,所以我不知道应该使用哪个运算符/技术

select * 
from employees_skills mec
where (mec.skill_id, mec.skill_level) = (560, 'BEGINNER') 
or (mec.skill_id, mec.skill_level) = (780, 'INTERMEDIATE')

如果我这样做的一套两个技能(和他们的resp.水平),我将能够这样做更多。

    • EDIT**:不应返回雇员2354(即使他们是560的初学者,但他们不具备其他技能780和/或他们不是中级)。

我希望员工具备WHERE条件下的所有技能及其相应级别

jyztefdp

jyztefdp1#

你需要一些额外的括号():

SELECT * 
FROM employees_skills mec
WHERE ((mec.skill_id, mec.skill_level) = (560, 'BEGINNER'))
OR ((mec.skill_id, mec.skill_level) = (423, 'INTERMEDIATE'));

这将创建一个元组,这就是您要查找的内容。

kokeuurv

kokeuurv2#

第一个问题是您正在查找组合技能组合(423,'中级'),但是该组合 * 在您的数据中不存在 *。
您的场景呈现出一个有趣的两难境地:你的结果必须有多个skill_id,skill_level组合,但是一行只能有一个skill_id,skill_level的值集。那么如何将多行与多个值进行比较 * 在同一时间 *。你可以使用一系列的and exists (select ...)。但是,这需要知道组合的数量或者动态sql来构造exist子句。另一个,是使用数组比较,特别是array containment operator
anyarray @〉anyarray → boolean第一个数组是否包含第二个数组,也就是说,第二个数组中出现的每个元素是否等于第一个数组中的某个元素?
首先生成包含skill_id和skill_level的UDT。然后创建一个表(可以是临时表)以包含UDT的数组。

-- Create a UDT to contain both skill_id and skill_level
create type skill_level_t as (sk_id integer, sk_level text); 
    
-- create a work table to contain Arrays of target skill_id, skill_level combinations
 create table target_skills(tgt_skill_id integer generated always as identity
                                         primary key 
                           , skills      skill_level_t[]
                           );

完成上述操作后,现在可以按雇员聚合雇员技能集,然后使用包含操作符(@>)将该聚合加入到目标技能中。(see demo

-- target query
select emp_id           "Employee Id" 
     , (slist).sk_level "Skill Level"
     , (slist).sk_id    "Skill Id" 
  from ( select emp_id, unnest(skills) slist 
           from ( select es.emp_id,ts.skills
                   from ( select emp_id, array_agg( (skill_id, skill_level )::skill_level_t) skills 
                            from emp_skills
                          group by emp_id
                        )  es
                   join target_skills ts 
                     on  es.skills @> ts.skills
                 ) sq1
        ) sq2;

注意:最后的查询可能可以重构和简化,但是我想展示它开发过程中的每一步。
编辑问题:没有更简单/更快的方法吗?
正如我所说的,它可能可以被减少,但是仔细观察,您会发现( select es ... ) s1部分是唯一实际访问数据库的部分,其他部分只是对上一步中已经存在于内存中的结果进行简单转换,因此不需要额外的实际i/o。如前所述,您可以使用一组and exists (select ...,但所需的选择数与(mec.skill_id, mec.skill_level)条件集的数量相同(5个条件,然后5个选择,10个条件,然后10个选择,50个条件,您明白了吧)。每个条件访问数据库时可能会有额外的i/o。无论条件的数量如何,上述内容都不会更改(5,10,50查询保持不变)。同时,将简单的转换组合成一个大的转换会很快变得复杂,仅仅将它们串在一起几乎不起作用。显然,我的意见是 * 没有更简单/更快的方法吗?*。一个选项可能是将查询隐藏在SQL函数中。

相关问题