2009-04-10 12 views
5

Używam SQL od lat, ale rzadko coś więcej niż proste wkładki i wybiera etc ... więc nie jestem ekspertem SQL. Zastanawiam się, czy mogę uzyskać pomoc w optymalizacji bardziej złożonych instrukcji SQL, które wykonuję na SQLite, od PHP przez PDO.Optymalizowanie SQLite wkładka wielokrotnego wyboru

Wydaje się, że to polecenie działa poprawnie, wydaje się, że trwa dłużej, niż mogłem się spodziewać (a może po prostu oczekuję zbyt wiele).

Jest to SQL:

INSERT OR IGNORE INTO MailQueue(SubscriberID, TemplateID) 
    SELECT Subscribers.ID, '1' AS TemplateID 
    FROM Subscribers 
    INNER JOIN SubscriberGroups ON Subscribers.ID=SubscriberGroups.SubscriberID 
    WHERE SubscriberGroups.GroupID IN ('1', '2', '3') 
    AND Subscribers.ID NOT IN 
     ( 
     SELECT Subscribers.ID FROM Subscribers 
     INNER JOIN SubscriberGroups ON Subscribers.ID=SubscriberGroups.SubscriberID 
     WHERE SubscriberGroups.GroupID IN ('4', '5', '6') 
     ); 

Co mam jest lista abonentów, w jednej lub kilku grupach. Chcę dodać subskrybentów do kolejki pocztowej, wybierając te, które należą do jednej lub więcej grup (1,2,3), ale wykluczają te, które są również w innym zestawie grup (4,5,6).

Po pierwsze, czy powyższy SQL jest typowy jak to zrobić?

Po drugie, jakie wskazówki powinienem podać, aby działało to tak wydajnie, jak to możliwe?

Obecnie trwa to około 30 sekund, aby uzyskać około 5000 rekordów abonenta (i garść grup) na średniej LAMP.

Pod koniec dnia wydajność nie jest aż tak krytyczna, ale chciałbym to lepiej zrozumieć, aby każdy wgląd był bardzo doceniany.

Brad

Odpowiedz

6

Jest szansa, że ​​dodatkowe sprzężenia cię zabijają. Co zrobić, jeśli:

SELECT Subscribers.ID, '1' AS TemplateID 
FROM Subscribers 
WHERE EXISTS(SELECT * 
       FROM SubscriberGroups 
       WHERE Subscribers.ID=SubscriberGroups.SubscriberID 
           AND SubscriberGroups.GroupID IN ('1', '2', '3')) 

    AND NOT EXISTS(SELECT * 
        FROM SubscriberGroups 
        WHERE Subscribers.ID=SubscriberGroups.SubscriberID 
        AND SubscriberGroups.GroupID IN ('4', '5', '6') 
    ); 

Można by też chcesz się upewnić, że masz indeksu na SubscriberGroups (SubscriberID, GroupID)

Domyślam się, że abonenci ma już indeks na ID, prawda?

EDYCJA: Inna opcja, która może być lub nie być szybsza. Spójrz na plany zapytań każdego, aby zobaczyć ...

Ten może być pojedynczy skan indeksu które mógłby być szybsze niż dwa indeks stara, ale to zależy od optymalizator SQLite za ...

SELECT Subscribers.ID, '1' AS TemplateID 
FROM Subscribers 
INNER JOIN(SELECT SUM(CASE WHEN GroupID IN('1', '2', '3') THEN 1 ELSE 0 END) AS inGroup, 
        SUM(CASE WHEN GroupID IN('4', '5', '6') THEN 1 ELSE 0 END) AS outGroup, 
        SubscriberID 
          FROM SubscriberGroups 
         WHERE SubscriberGroups.GroupID IN ('1', '2', '3', '4', '5', '6') 
     ) SubscriberGroups 
     ON Subscribers.ID=SubscriberGroups.SubscriberID 
     AND inGroup > 0 
     AND outGroup = 0 
+0

Dzięki Matt, świetnie. Twoje pierwsze rozwiązanie otrzymało od 30 sekund do około 5 lub 6, co jest wystarczająco dobre. Nie próbowałem drugiej opcji, ponieważ tak naprawdę jej nie rozumiem, ale będę o tym pamiętał, jeśli stanie się problemem. Jeszcze raz dziękuję –

3

Innym sposobem pisać SQL, które mogłyby być szybsze (nie mam SQLite, na której można przetestować):

SELECT 
    S.ID, 
    '1' AS TemplateID  -- Is this really a string? Does it need to be? 
FROM 
    Subscribers S 
LEFT OUTER JOIN SubscriberGroups SG ON 
    SG.SubscriberID = S.ID 
WHERE 
    SG.SubscriberID IS NULL AND 
    EXISTS 
    (
      SELECT 
       * 
      FROM 
       SubscriberGroups SG2 
      WHERE 
       SG2.SubscriberID = S.ID AND 
       SG2.GroupID IN ('1', '2', '3') -- Again, really strings? 
    ) 

metoda Matta powinny również działać dobrze. Wszystko zależy tylko od tego, w jaki sposób SQLite decyduje się na tworzenie planów zapytań.

Proszę również zwrócić uwagę na moje uwagi. Jeśli są one naprawdę zdefiniowane jako typy danych INT w bazie danych, pojawi się dodatkowe przetwarzanie umożliwiające konwersję między dwoma różnymi typami danych. Jeśli są to ciągi w bazie danych, czy istnieje ku temu powód? Czy w tych kolumnach są wartości nieliczbowe?

+0

Dzięki Tomowi macie rację w cytowanych dowodach ... Nie jestem pewien, dlaczego je tam miałem.Nie próbowałem twojej sugestii, ponieważ macierz wydaje się działać dobrze, a twoja wydaje się tęsknić za częścią grup wykluczonych (4,5,6). Dzięki i tak! –

+0

Patrząc na to dokładniej, zdałem sobie sprawę, że jedna z kolumn ID nie została zadeklarowana jako INTEGER, dlatego potrzebowałem wycen. SQLite nie jest naprawdę wybredny w stosunku do typów, dlatego go przegapiłem. Zmiana na liczbę całkowitą i usunięcie cudzysłowów trwa teraz około 1/2 sekundy. Dzięki! –

+0

Mój powinien zająć się wykluczeniami. Testowałeś to? Na tym polega cały LEFT JOIN. Sprawdź kolumnę NOT NULL z połączonej tabeli w klauzuli WHERE, a jeśli jest NULL, wiesz, że nie istnieje dopasowanie. –

Powiązane problemy