6

我正在使用火鸟 2.1。

这里有张桌子:IDs, Labels

同一个 ID 可以有多个标签:

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

假设我有一组标签,即:(Apple,Pear,Peach)。

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

谢谢!

4

3 回答 3

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 
于 2014-09-05T20:17:59.760 回答
2

如被问及,我正在发布我更简单的 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

此解决方案与 pilcrow 具有相同的缺点......您需要知道您正在寻找多少个值,因为 HAVING = 条件必须与 WHERE IN 条件匹配。在这方面,Ed 的回答更加灵活,因为它拆分了串联值字符串参数并计算了值。所以你只需要改变一个参数,而不是我和 pilcrow 使用的 2 个条件。

OTOH,如果关注效率,我宁愿认为(但我绝对不确定)Ed 的 CTE 方法可能不如我建议的那样被 Firebird 引擎优化。Firebird 非常擅长优化查询,但是当您以这种方式使用 CTE 时,我现在真的不知道它是否能够这样做。但是 WHERE + GROUP BY + HAVING 应该可以通过简单地在 (id,label) 上设置一个索引来优化。

总之,如果您的情况涉及执行时间,那么您可能需要一些解释计划来了解正在发生的事情,无论您选择哪种解决方案;)

于 2014-09-06T11:40:30.127 回答
1

如果可以接受创建将从主选择调用的辅助存储过程,则考虑以下事项。

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 块来演示传入分隔字符串

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
于 2014-09-05T20:55:24.070 回答