it-swarm.dev

Wykonanie SQL Wybierz zajmuje zbyt dużo czasu

Jest to prosty wybór z tabeli tymczasowej, pozostawiając dołączenie do istniejącej tabeli na jej kluczu podstawowym, z dwoma podselekcjami przy użyciu góry 1 odnoszącej się do połączonej tabeli.

W kodzie:

SELECT
    TempTable.Col1,
    TempTable.Col2,
    TempTable.Col3,
    JoinedTable.Col1,
    JoinedTable.Col2,
    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1,
    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2,
FROM
    #TempTable as TempTable
LEFT JOIN
    JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn1 AND 
    TempTable.PKColumn2 = JoinedTable.PKColumn2)
WHERE
    JoinedTable.WhereColumn IN  (1, 3)

To jest dokładna replika mojego zapytania.

Jeśli usunę dwa zaznaczenia podrzędne, będzie działać dobrze i szybko. Dzięki dwóm podselekcjom uzyskuję około 100 rekordów na sekundę, co jest niezwykle wolne dla tego zapytania, ponieważ powinno zwrócić prawie milion rekordów.

Sprawdziłem, czy każda tabela ma klucz podstawowy, wszystkie mają. Wszystkie mają Indeksy ORAZ statystyki dla swoich ważnych kolumn, takich jak te w tych klauzulach WHERE i te w klauzuli JOIN. Jedyną tabelą bez zdefiniowanego klucza podstawowego ani indeksu jest tabela tymczasowa, ale to też nie jest problem, ponieważ nie jest to ta związana z powolnymi selekcjami podrzędnymi, a jak już wspomniałem, bez podselekcji działa dobrze.

Bez tych TOP 1 zwraca więcej niż jeden wynik i wywołuje błąd.

Pomocy, ktoś?

EDYCJA :

Plan wykonania powiedział mi, że brakuje mi indeksu. Stworzyłem go i odtworzyłem niektóre inne indeksy. Po pewnym czasie z nich skorzystał plan wykonania, a teraz zapytanie działa szybko. Jedynym problemem jest to, że nie udało mi się tego powtórzyć na innym serwerze dla tego samego zapytania. Więc moim rozwiązaniem będzie WSKAZÓWKA, którego indeksu użyje SQL Server.

9
Smur

Myślę, że w zapytaniu o milion rekordów musisz unikać rzeczy takich jak OUTER JOINS. Sugeruję użycie UNION ALL Zamiast LEFT JOIN. Tak długo, jak myślę, że CROSS APPLY Jest bardziej wydajny niż pod-zapytanie w klauzuli select, zmodyfikuję zapytanie napisane przez Conarda Frixa, co moim zdaniem jest poprawne.

teraz: kiedy zacząłem modyfikować zapytanie, zauważyłem, że masz klauzulę WHERE: JoinedTable.WhereColumn IN (1, 3). w takim przypadku, jeśli pole jest puste, warunek stanie się fałszywy. to dlaczego używasz LEFT JOIN podczas filtrowania wierszy o wartości zerowej? wystarczy zastąpić LEFT JOININNER JOIN, gwarantuję, że będzie szybciej.

około INDEKS:

pamiętaj, że jeśli masz indeks na stole, powiedzmy

table1(a int, b nvarchar)

a twój indeks to:

nonclustered index ix1 on table1(a)

i chcesz zrobić coś takiego:

select a,b from table1
where a < 10

w swoim indeksie nie uwzględniłeś kolumny b, więc co się stanie?

jeśli serwer sql używa twojego indeksu, będzie musiał przeszukać indeks o nazwie „Index Seek”, a następnie odwołać się do głównej tabeli, aby uzyskać kolumnę b, o nazwie „Wyszukaj”. Ta procedura może potrwać znacznie dłużej niż skanowanie samej tabeli: „Skanowanie tabeli”.

ale w oparciu o statystyki serwera sql-serwer w takich sytuacjach może w ogóle nie używać Twojego indeksu.

więc najpierw sprawdź Execution Plan, aby zobaczyć, czy indeks jest w ogóle używany.

jeśli tak lub nie oba, zmień indeks, aby uwzględnić wszystkie wybrane kolumny. powiedz jak:

nonclustered index ix1 on table1(a) include(b)

w takim przypadku wyszukiwanie nie będzie potrzebne, a zapytanie zostanie wykonane o wiele szybciej.

7
Maziar Taheri

To pod-zaznaczenie w zaznaczeniu kolumny powoduje powolny powrót. Powinieneś spróbować użyć podselekcji w lewych złączeniach lub użyć pochodnej tabeli, jak zdefiniowałem poniżej.

żywanie lewych połączeń do dwóch wystąpień Trzeciego stoł

SELECT
  TempTable.Col1,
  TempTable.Col2,
  TempTable.Col3,
  JoinedTable.Col1,
  JoinedTable.Col2,
  ThirdTable.Col1 AS ThirdTableColumn1,
  ThirdTable2.Col1 AS ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN JoinedTable ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND 
    TempTable.PKColumn 2 = JoinedTable.PKColumn2)
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
WHERE
    JoinedTable.WhereColumn IN  (1, 3)

Korzystanie z tabeli pochodnej

 SELECT 
      TempTable.Col1,
      TempTable.Col2,
      TempTable.Col3,
      DerivedTable.Col1,
      DerivedTable.Col2,
      DerivedTable.ThirdTableColumn1,
      DerivedTable.ThirdTableColumn2
 FROM #TempTable as TempTable
    LEFT JOIN (SELECT
                 JoinedTable.PKColumn2,
                 JoinedTable.Col1,
                 JoinedTable.Col2,
                 JoinedTable.WhereColumn,
                 ThirdTable.Col1 AS ThirdTableColumn1,
                 ThirdTable2.Col1 AS ThirdTableColumn2
               FROM JoinedTable
               LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
               LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn) 
        DerivedTable ON (TempTable.PKColumn1 = DerivedTable .PKColumn2 AND 
        TempTable.PKColumn2 = DerivedTable.PKColumn2)
    WHERE
        DerivedTable.WhereColumn IN  (1, 3)
6
John Hartsock

Zamiast tego spróbuj zastosować krzyżyk

SELECT
    TempTable.Col1,
    TempTable.Col2,
    TempTable.Col3,
    JoinedTable.Col1,
    JoinedTable.Col2,
    ThirdTableColumn1.col1,
    ThirdTableColumn2.col1

FROM
    #TempTable as TempTable
LEFT JOIN
    JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND 
    TempTable.PKColumn 2 = JoinedTablePKColumn2)

CROSS APPLY
(
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1
CROSS APPLY    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2,
WHERE
    JoinedTable.WhereColumn IN  (1, 3)

Możesz także użyć CTE i numer_wiersza lub zapytania wbudowanego za pomocą MIN

2
Conrad Frix

Przenieś bity JOIN z głównej części zdania i umieść je jako podselekcja. Przeniesienie go do sekcji GDZIE i DOŁĄCZ gwarantuje, że nie musisz wybierać TOP 1 od nowa, co moim zdaniem jest przyczyną powolności. Jeśli chcesz to sprawdzić, sprawdź plan wykonania.

2
Gregory A Beamer

Referencje ThirdTable (w twoim przykładzie sub wybiera) wymagają takiej samej uwagi indeksu jak każda inna część zapytania.

Bez względu na to, czy korzystasz z podselekcji:

(
    SELECT TOP 1
        ThirdTable.Col1 -- Which is ThirdTable's Primary Key
    FROM
        ThirdTable
    WHERE
        ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
    SELECT TOP 1
        ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
    FROM
        ThirdTable
    WHERE
        ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,

LEFT JOINS (zgodnie z propozycją Johna Hartsocka):

LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn

ZASTOSOWANIE KRZYŻOWE (zgodnie z propozycją Conrada Frixa):

CROSS APPLY
(
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1
CROSS APPLY    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2

Musisz zapewnić covering indexes są zdefiniowane dla ThirdTable.SomeColumn i ThirdTable.SomeOtherColumn i indeksy są unikalne. Oznacza to, że będziesz musiał dodatkowo zakwalifikować odwołania ThirdTable, aby wyeliminować wybór wielu wierszy i poprawić wydajność. Wybór sub selects, LEFT JOIN lub CROSS APPLY nie będzie miało znaczenia, dopóki nie poprawisz selektywności dla ThirdTable.SomeColumn i ThirdTable.SomeOtherColumn poprzez dodanie większej liczby kolumn, aby zapewnić unikalną selektywność. Do tego czasu oczekuję, że twoje wyniki będą nadal cierpieć.

covering index temat jest ładnie wprowadzony przez Maziar Taheri; nie powtarzając swojej pracy, podkreślam potrzebę wzięcia pod uwagę użycia indeksów obejmujących.

W skrócie: Popraw selektywność dla ThirdTable.SomeColumn i ThirdTable.SomeOtherColumn zapytania (lub złączenia) poprzez dodanie powiązanych kolumn w tabeli, aby zapewnić unikalne dopasowanie wiersza. Jeśli nie jest to możliwe, nadal będziesz mieć problemy z wydajnością, ponieważ silnik jest zajęty ciągnięciem rzędów, które są następnie wyrzucane. Wpływa to na Twoje operacje we/wy, procesor i ostatecznie na plan wykonania.

2
Robert Miller