2015-09-04 28 views
6

chcę do wyszukiwania wartości wymienione w tabeli temp:dane Pull bez zmiany kolejności pozycji w tabeli referencyjnej

Więc powiedzmy:

Create Table #mylist 
(
eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS, 
refdate datetime 
) 

Insert Into #mylist (eserial, refdate) Values ('A', '2015-09-15') 
Insert Into #mylist (eserial, refdate) Values ('B', '2015-09-14') 
Insert Into #mylist (eserial, refdate) Values ('C', '2015-09-13') 
Insert Into #mylist (eserial, refdate) Values ('D', '2015-09-12') 

muszę wynik będzie data Top 1 mniej niż data referencyjna.
Powinny być zwrócone w tej samej kolejności, co w tabeli tymczasowej.

Co próbowałem:

Select 
    lst.eserial, 
    lst.refdate, 
    app.CREATEDDATETIME, 
From #mylist lst 
Outer Apply 
    (Select Top 1 rec.CREATEDDATETIME, rec.ESERIAL, rec.ITEMID 
    From TableSource rec 
    Where lst.eserial=rec.ESERIAL And rec.CREATEDDATETIME<lst.refdate 
    Order By rec.CREATEDDATETIME Desc 
    ) As app 

To działa, ale jest powolne. Ponadto, jeśli liczba wierszy jest zwiększona, nie zachowuje konsekwentnie sekwencji eserial. Potrzebuję kwerendy, aby zachować kolejność, którą umieściłem w tabeli tymczasowej.

Ponownie mój oczekiwany wynik jest po prostu:

enter image description here

Gdzie eserial jest taka sama jak sekwencja tabeli temp i CREATEDDATETIME jest maksymalna data mniej niż w dniu referencyjnym. Bardziej jak warunkowe Vlookup, jeśli znasz Excel.

+0

Szczerze mówiąc, spodziewałem się, że metoda ZASTOSOWANIA ZEWNĘTRZNEGO zadziała najlepiej w ogólnym przypadku. Czy nie tworzysz żadnych indeksów na swoim stole tymczasowym? Czy możesz określić 'eserial' jako' PRIMARY KEY' w twoim utworze? –

+0

@ BakonBits Próbowałem określić go jako klucz główny, ale ten sam wynik. Drugi kod jest szybszy prawie 10x. – L42

+1

Czy próbowałeś z indeksem na ESERIAL, CREATEDDATETIME desc include (ITEMID), gdzie AREAID = "home" –

Odpowiedz

6

Nie jest całkiem jasne, co masz na myśli przez

zachować kolejność elementów w tabeli tymczasowej

, ale jeśli chcesz uzyskać wynik sortowane według eserial, to masz aby dodać ORDER BY eserial do zapytania. Bez ORDER BY wynikowe wiersze mogą zostać zwrócone w dowolnej kolejności. Dotyczy to każdej wybranej metody.

Więc, biorąc ostatnią kwerendy jako podstawa, będzie to wyglądać tak:

Select 
    lst.eserial 
    ,lst.refdate 
    ,app.CREATEDDATETIME 
From 
    #mylist lst 
    Outer Apply 
    (
     Select Top 1 rec.CREATEDDATETIME 
     From TableSource rec 
     Where lst.eserial=rec.ESERIAL And rec.CREATEDDATETIME<lst.refdate 
     Order By rec.CREATEDDATETIME Desc 
    ) As app 
ORDER BY lst.eserial; 

Aby działać szybko i sprawnie dodać indeks do TableSource na (ESERIAL, CREATEDDATETIME). Kolejność kolumn w indeksie jest ważna.

Ważne jest również, aby wiedzieć, czy istnieją inne kolumny, których używasz w zapytaniu OUTER APPLY i jak ich używasz. Wspomniałeś kolumnę AREAID w pierwszym wariancie pytania, ale nie w ostatnim wariancie. Jeśli masz więcej kolumn, to wyraźnie pokaż, w jaki sposób zamierzasz z nich korzystać, ponieważ właściwy indeks będzie od tego zależał. Indeks na (ESERIAL, CREATEDDATETIME) wystarczy dla zapytania, które napisałem powyżej, ale jeśli masz więcej kolumn, może być wymagany inny indeks.

To pomogłoby także optymalizator jeśli zdefiniowano tabelę temp z PRIMARY KEY:

Create Table #mylist 
(
    eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS PRIMARY KEY, 
    refdate datetime 
) 

klucz podstawowy by stworzyć niepowtarzalny indeksu klastrowego.

Jeszcze jedna ważna uwaga. Jaki jest rodzaj i sortowania kolumn i CREATEDDATETIMEESERIAL w głównym TableSource stole? Upewnij się, że typy i sortowanie kolumn w tabeli tymczasowej jest zgodne z główną tabelą TableSource.Jeśli typ jest inny (varchar vs. nvarchar lub datetime w porównaniu do date) lub sortowanie jest inne, indeks nie może być użyty => będzie wolny.

Edit

użyć zwrotu „samą sekwencję jak tabeli temp” kilka razy w pytaniu, ale to naprawdę nie jest jasne, co masz na myśli przez nią. Twoje przykładowe dane nie pomagają rozwiązać niejednoznaczności. Nazwa kolumny eserial również przyczynia się do zamieszania. Widzę dwa możliwe znaczenia:

  1. Zwraca wiersze z tabeli temp uporządkowanej według wartości w kolumnie eserial.
  2. Zwraca wiersze z tabeli tymczasowej w takiej samej kolejności, w jakiej zostały wstawione.

Moja oryginalna odpowiedź implikuje (1): zwraca wiersze z tabeli temp uporządkowanej według wartości w kolumnie eserial.

Jeśli chcesz zachować kolejność wierszy, które zostały wstawione do tabeli, musisz w jakiś sposób wyraźnie zapamiętać tę kolejność. Najprostszą metodą jest dodanie kolumny IDENTITY do tabeli tymczasowej i późniejszej kolejności w tej kolumnie. W ten sposób:

Create Table #mylist 
(
    ID int IDENTITY PRIMARY KEY, 
    eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS, 
    refdate datetime 
) 

W ostatnim zapytaniu użyj ORDER BY lst.ID.

+0

'Bez ZAMÓWIENIA Przez wynikowe wiersze można zwrócić w dowolnej kolejności. Dotyczy to każdej wybranej metody "Czy to oznacza, że ​​tego, czego chcę, nie można osiągnąć? Jak umieścić go w tabeli temp jest kolejność, którą chcę, aby został zwrócony. – L42

+0

To nie jest prawdziwy problem techniczny, to problem koncepcyjny. to nie jest problem dla 99% programistów SQL Server. Wygląda na to, że już rozwiązałeś swój problem, dodając pole do tabeli, aby wskazać kolejność, i to właśnie musisz zrobić. –

+0

@ Nick.McDermaid Więc to jedyny sposób. Tak jak napisali Eric. Dodanie kolejnego pola, które w unikalny sposób zachowa sekwencję. – L42

3

To łatwe przy użyciu tożsamości. Zapytanie bez Order nie gwarantuje otrzymania zamówienia na serwerze SQL.

Create Table #mylist 
(
    seqId int identity(1,1), 
    eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS, 
    refdate datetime 
) 

Użyj tabeli swobodnie i umieścić Order By seqId na końcu zapytania

Edycja

Używaj MAX() zamiast TOP 1 z rzędu, jeśli nie masz indeks klastra na ESERIAL, CREATEDDATETIME w sprawie TableSource

https://stackoverflow.com/a/21420643/1287352

Select 
    lst.eserial, 
    lst.refdate, 
    app.CREATEDDATETIME, 
From #mylist lst 
Outer Apply 
    (
     Select MAX(rec.CREATEDDATETIME), rec.ESERIAL, rec.ITEMID 
     From TableSource rec 
     Where lst.eserial = rec.ESERIAL And rec.CREATEDDATETIME < lst.refdate 
     GROUP BY rec.ESERIAL, rec.ITEMID 
    ) As app 
ORDER BY lst.seqId 
+0

Jest to podobne do podejścia, którego użyłem, zanim edytuję pytanie, o które prosił mnie VladimirBaranov, z wyjątkiem tego, że używasz 'Identity'. Ale ta sama logika. – L42

1

Być może problem z wydajnością wynika z indeksowania. Spróbuj dodać poniższe indeksy, usuwając UNIQUE, jeśli klucze nie są unikatowe.

CREATE UNIQUE NONCLUSTERED INDEX idx ON #mylist (eserial, refdate); 
CREATE UNIQUE NONCLUSTERED INDEX idx ON TableSource (eserial, CREATEDDATETIME);