2014-09-05 10 views
6

Ich benutze Firebird 2.1.Firebird wie IDs, die alle Elemente in einem Set übereinstimmen

Es gibt einen Tisch: IDs, Labels

Es können mehrere Etiketten für die gleiche ID sein:

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

Lassen Sie uns sagen, dass ich eine Reihe von Etiketten, dh .: (Apfel, Birne, Pfirsich) .

Wie kann ich eine einzelne Auswahl schreiben, um alle IDs zurückzugeben, die alle Labels in einem bestimmten Satz zugeordnet haben? Vorzugsweise möchte ich die Menge in einer Zeichenfolge angeben, getrennt durch Kommas, wie: ('Apple', 'Birne', 'Pfirsich') -> dies sollte ID = 10 zurückgeben.

Vielen Dank!

Antwort

2

Wie gebeten, posten ich meine einfachere Version von picrows Antwort. Ich habe dies auf meinem Firebird getestet, Version 2.5, aber das OP (Steve) hat es auf 2.1 getestet und es funktioniert auch.

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

Diese Lösung hat den gleichen Nachteil wie Pilcrow ist ... Sie müssen wissen, wie viele Werte, die Sie suchen, wie die HAVING = Bedingung der WHERE IN-Bedingung übereinstimmen müssen. In dieser Hinsicht ist die Antwort von Ed flexibler, da sie den verketteten Wert-String-Parameter aufteilt und die Werte zählt. Sie müssen nur den einen Parameter ändern, anstatt die 2 Bedingungen, die ich und pilcrow verwenden.

OTOH, wenn Effizienz wichtig ist, würde ich lieber denken (aber ich bin absolut nicht sicher), dass Eds CTE-Ansatz von der Firebird-Engine weniger optimierbar sein könnte als der, den ich vorschlage. Firebird ist sehr gut in der Optimierung von Abfragen, aber ich weiß nicht wirklich, ob es in der Lage ist, dies zu tun, wenn Sie CTE auf diese Weise verwenden. Aber die WHERE + GROUP BY + HAVING sollte optimierbar sein, indem man einfach einen Index auf (id, label) hat.

Zum Schluss, wenn die Ausführungszeiten der Besorgnis in Ihrem Fall sind, dann müssen Sie wahrscheinlich einige Pläne erklären, um zu sehen, was geschieht, je nachdem welche Lösung Sie wählen;)

+0

Es gibt kein CTE ("common table expression") in Ihrer (oder pilcrows) Abfrage –

+0

Dieser Kommentar wurde auf Eds Antwort bezogen, die zwar schön und flexibel ist, aber ** CTE verwendet. Ich werde es klarer machen.Danke – Frazz

+0

Funktioniert auch mit FB2.1. Ich nehme das als Antwort, da dies die einfachste Abfrage ist. Vielen Dank! – Steve

2

Es ist am einfachsten, die Zeichenfolge in Code zu spalten und dann abfragen

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

Was ist, wenn (id, Label) ist nicht eindeutig? Ich würde eine DISTINCT in den Subselect hinzufügen ... nur für den Fall;) – Frazz

+0

@ Frazz, ja, danke und habe es. – pilcrow

+0

Ich habe Firebird in einer Weile nicht benutzt, und ich habe es nicht verwendet, um diese Art von Abfrage zu machen. Kann das ohne den SUBSELECT in FireBird nicht gemacht werden? Ich meine ... benutze ein HAVING anstelle der WHERE in der äußeren Auswahl? – Frazz

1

Wenn es akzeptabel ist, einen Helfer gespeicherte Prozedur zu erstellen, die von den primären aufgerufen werden, wählen Sie dann die folgende prüfen.

Das Helper gespeicherten Prozedur in einem getrennten Zeichenfolge nimmt zusammen mit dem Trennzeichen und gibt eine Zeile für jede Zeichenfolge begrenzt

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 

Unten ist der Code aufzurufen, verwende ich Block Execute in dem begrenzten zu demonstrieren Passieren string

Verwandte Themen