2014-09-05 38 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 
相關問題