2014-09-05 15 views
6

Firebird 2.1 kullanıyorum. Firebird, bir kümedeki tüm öğelerle eşleşen ids'lerin nasıl seçileceğini gösterir

Orada

bir tablodur: IDs, Labels

aynı kimliği için birden etiket olabilir:

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

ı etiket kümesi var diyelim, yani .: (Elma, Armut, Şeftali) .

Belirli bir kümede ilişkili tüm etiketlere sahip tüm kimlikleri döndürmek için tek bir seçim nasıl yazabilirim? Tercihen virgülle ayrılmış bir dizgede seti belirtmek isterim: ('Elma', 'Armut', 'Şeftali') - ›Bu, ID = 10'u döndürmelidir.

Teşekkürler!

cevap

2

, ben piclrow cevabı benim basit bir sürümünü post ediyorum. Bunu sürüm 2.5 olan Firebird'imde test ettim, ancak OP (Steve) bunu 2.1'de test etti ve aynı zamanda çalışıyor.

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

Bu çözüm HAVING = koşul koşul İÇİNDE WHERE aynı olmalıdır gibi, aradığınız kaç değer bilmek gerekir ... Pilcrow en aynıdır dezavantaja sahiptir. Bu açıdan, Ed'in cevabı daha fazladır, çünkü birleştirilmiş değer dizgesi parametresini böler ve değerleri sayar. Yani sadece I ve pilcrow kullanımı 2 yerine, bir parametre değiştirmeniz gerekir.

OTOH, eğer etkinlik konusunda endişe varsa, Ed'ın CTE yaklaşımının Firebird motoru tarafından önerdiğimden daha az optimize edilebileceğini düşünmeyi tercih ederim (ama kesinlikle emin değilim). Firebird sorguları optimize etmede çok iyidir, ancak şimdi CTE'yi bu şekilde kullandığınızda bunu yapamıyorum. Ancak, WHERE + GROUP BY + HAVING, sadece (id, label) bir indeks alarak optimize edilebilir.

+0

Hiçbir CTE ("ortak tablo ifade") sizin (veya Pilcrow en) sorgusunda O comment güzel ve esnek Ed cevap, sevk edildi –

+0

olmakla ** CTE ** kullanır. Daha net hale getireceğim.Teşekkürler – Frazz

+0

FB2.1 ile de çalışır. Bunu en basit sorgu olduğundan cevap olarak kabul edeceğim. Teşekkürler! – Steve

2

Şifreli dize bölmek ve daha sonra aşağıdaki noktaları göz önünde seçin birincil gelen çağrılacak bir yardımcı saklı yordam oluşturmak için kabul edilebilir ise,

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

Ya (id, etiket) benzersiz değil); yürütme kez sizin durumunuzda endişe varsa seçtiğiniz hangisi çözüm

Sonuç olarak, o zaman muhtemelen ne olduğunu görmek için bazı açıklar planlar gerek? Subselect'e bir DISTINCT ekleyebilirim ... sadece durumda;) – Frazz

+0

@Frazz, evet, teşekkürler ve anladım. – pilcrow

+0

Firebird'i bir süredir kullanmadım ve bu tür bir sorguyu yapmak için kullanmadım. Bu FireBird SUBSELECT olmadan yapılabilir mi? Yani ... dış seçimde WHERE yerine HAVING kullanarak mı? – Frazz

1

sorgulamak için en kolay yoldur.

Yardımcısı saklı yordam sınırlayıcı ile birlikte bir sınırlandırılmış dizede alır ve her sınırlandırılmış bir dize Aşağıda

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 

aramak koddur için bir satır döndürür

, ben sınırlandırılmış geçen göstermek için cümlesinin uygulanması kullanıyorum dize

sorulan gibi
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