2014-09-05 22 views
6

我正在使用Firebird 2.1。火鸟如何选择匹配集合中所有项目的ID

有一个表:IDs, Labels

有可能为同一ID多个标签:

10 Peach 
10 Pear 
10 Apple 
11 Apple 
12 Pear 
13 Peach 
13 Apple 

比方说,我有一组标签,即:(苹果,梨,桃) 。

如何编写单个选择来返回所有标签都关联给定集合中的所有标识?最好我想用逗号分隔一个字符串中的集合,如:('Apple','Pear','Peach') - >这应该返回ID = 10。

回答

2

如问,我发布了我的简单版本的piclrow的答案。我已经在我的Firebird 2.5版上测试过了,但OP(史蒂夫)已经在2.1上测试过了,它也可以工作。

SELECT id 
FROM table 
WHERE label IN ('Apple', 'Pear', 'Peach') 
GROUP BY id 
HAVING COUNT(DISTINCT label)=3 

该解决方案具有相同的缺点段落符号的...你需要知道你有多少价值寻找,因为HAVING =条件必须在WHERE IN条件相匹配。在这方面,埃德的答案更加灵活,因为它分割连接值字符串参数并计数值。所以你只需要改变一个参数,而不是我和pilcrow使用的两个条件。如果效率值得关注,我宁愿认为(但我绝对不确定)埃德的CTE方法可能不如火鸟引擎优于我建议的方法。 Firebird在优化查询方面非常出色,但是现在如果能够以这种方式使用CTE时就能够做到这一点。但是WHERE + GROUP BY + HAVING应该通过简单地在(id,label)上有一个索引来优化。

总之,如果执行时间是你的情况的关注,那么你可能需要一些解释一下计划,看看发生了什么,无论你选择哪个解决方案;)

+0

您的(或pilcrow's)查询中没有CTE(“公用表格表达式”) – 2014-09-06 12:37:23

+0

该评论被引用到Ed的答案中,这很好而且灵活,但是**使用CTE。我会更清楚。谢谢 – Frazz 2014-09-06 16:23:18

+0

也适用于FB2.1。我会以此作为答案,因为这是最简单的查询。谢谢! – Steve 2014-09-10 20:39:30

2

这是最简单的拆分代码中的字符串,然后查询

SQL> select ID 
CON> from (select ID, count(DISTINCT LABEL) as N_LABELS 
CON>   from T 
CON>   where LABEL in ('Apple', 'Pear', 'Peach') 
CON>   group by 1) D 
CON> where D.N_LABELS >= 3; -- We know a priori we have 3 LABELs 

      ID 
============ 
      10 
+1

如果有什么(ID,标签)是不是唯一的?我会在子查询中添加一个DISTINCT ...以防万一;) – Frazz 2014-09-05 20:36:24

+0

@Frazz,是的,谢谢,明白了。 – pilcrow 2014-09-05 20:50:56

+0

我有一段时间没有使用Firebird,我没有用它来做这种类型的查询。如果没有FireBird中的SUBSELECT,不能做到这一点吗?我的意思是...使用HAVING而不是外部选择中的WHERE? – Frazz 2014-09-05 20:53:47

1

如果可以接受建立一个辅助存储过程,将从主叫选择,然后考虑下。

辅助存储过程发生在一个分隔的字符串与所述分隔符一起,并返回一个行对每个分隔的字符串

CREATE OR ALTER PROCEDURE SPLIT_BY_DELIMTER (
    WHOLESTRING VARCHAR(10000), 
    SEPARATOR VARCHAR(10)) 
RETURNS (
    ROWID INTEGER, 
    DATA VARCHAR(10000)) 
AS 
DECLARE VARIABLE I INTEGER; 
BEGIN 
    I = 1; 
    WHILE (POSITION(:SEPARATOR IN WHOLESTRING) > 0) DO 
    BEGIN 
     ROWID = I; 
     DATA = TRIM(SUBSTRING(WHOLESTRING FROM 1 FOR POSITION(TRIM(SEPARATOR) IN WHOLESTRING) - 1));   
     SUSPEND;  
     I = I + 1; 
     WHOLESTRING = TRIM(SUBSTRING(WHOLESTRING FROM POSITION(TRIM(SEPARATOR) IN WHOLESTRING) + 1)); 
    END 
    IF (CHAR_LENGTH(WHOLESTRING) > 0) THEN 
    BEGIN 
     ROWID = I; 
     DATA = WHOLESTRING; 
     SUSPEND; 
    END 
END 

下面是代码调用,我使用的执行块展示传入分隔字符串

EXECUTE BLOCK 
RETURNS (
    LABEL_ID INTEGER) 
AS 
DECLARE VARIABLE PARAMETERS VARCHAR(50); 
BEGIN 
    PARAMETERS = 'Apple,Peach,Pear'; 

    FOR WITH CTE 
    AS (SELECT ROWID, 
      DATA 
     FROM SPLIT_BY_DELIMITER(:PARAMETERS, ',')) 
    SELECT ID 
    FROM TABLE1 
    WHERE LABELS IN (SELECT DATA 
        FROM CTE) 
    GROUP BY ID 
    HAVING COUNT(*) = (SELECT COUNT(*) 
        FROM CTE) 
    INTO :LABEL_ID 
    DO 
    SUSPEND; 
END