Sql Firebird如何选择与集合中所有项目匹配的ID

Sql Firebird如何选择与集合中所有项目匹配的ID,sql,firebird,firebird2.1,Sql,Firebird,Firebird2.1,我用的是火鸟2.1 有一个表:IDs,Labels 同一ID可以有多个标签: 10 Peach 10 Pear 10 Apple 11 Apple 12 Pear 13 Peach 13 Apple 假设我有一套标签,例如:(苹果、梨、桃) 如何编写一个select来返回给定集合中所有标签都关联的所有ID?我最好用逗号分隔的字符串来指定集合,比如:('Apple','Pear','Peach')-›这应该返回ID=10 谢谢 最简单的方法是在代码中拆分字符串,然后进行查询 SQL> se

我用的是火鸟2.1

有一个表:
IDs,Labels

同一ID可以有多个标签:

10 Peach
10 Pear
10 Apple
11 Apple
12 Pear
13 Peach
13 Apple
假设我有一套标签,例如:(苹果、梨、桃)

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


谢谢

最简单的方法是在代码中拆分字符串,然后进行查询

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 

如果创建一个从主选择中调用的辅助存储过程是可接受的,那么考虑下面的内容:

Helper存储过程接收带分隔符的字符串和分隔符,并为每个带分隔符的字符串返回一行

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
下面是要调用的代码,我使用Execute块来演示传入分隔字符串的过程

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

正如被问到的,我正在发布piclrow答案的简单版本。我已经在我的Firebird 2.5版上测试过了,但是OP(Steve)已经在2.1版上测试过了,而且效果也不错

SELECT id
FROM table
WHERE label IN ('Apple', 'Pear', 'Peach')
GROUP BY id
HAVING COUNT(DISTINCT label)=3
这种解决方案与皮尔克罗的方案具有相同的缺点。。。您需要知道要查找多少个值,因为HAVING=条件必须与WHERE IN条件匹配。在这方面,Ed的答案更灵活,因为它拆分串联的值字符串参数并计算值。因此,您只需更改一个参数,而不是我和pilcrow使用的两个条件

OTOH,如果效率值得关注,我宁愿认为(但我绝对不确定)Ed的CTE方法可能比我建议的方法更不适合Firebird引擎。Firebird非常擅长优化查询,但我现在不知道当您以这种方式使用CTE时它是否能够做到这一点。但是WHERE+groupby+HAVING应该可以通过在(id,label)上设置索引来优化


总之,如果您的案例关注执行时间,那么您可能需要一些解释计划来了解发生了什么,无论您选择哪种解决方案;)

如果(id、标签)不是唯一的怎么办?我会在子选择中添加一个独特的。。。以防万一;)我已经有一段时间没有使用Firebird了,我也没有使用它来进行这种类型的查询。如果没有FireBird中的SUBSELECT,这不能完成吗?我是说。。。使用HAVING而不是WHERE在外层选择?@Frazz您能发布您的更简单的版本吗?在您(或pilcrow)的查询中没有CTE(“公共表表达式”),该注释参考了Ed的答案,这很好,也很灵活,但确实使用了CTE。我会说清楚的。感谢FB2.1。我将以此作为答案,因为这是最简单的查询。谢谢