2016-09-05 7 views
9

I zostały debugowanie oświadczenie poniżej godzinami:Dziwne zachowanie SQL Server - losowy wybór

SELECT 
(
SELECT t1.anotherColumn 
FROM table1 t1 
WHERE t1.aColumn=(1+ABS(Checksum(NewId()))%54) 
) res, * 
FROM 
(
SELECT TOP 200 * --PLEASE NOTICE HERE 
FROM table2 
)RESULT 

i problem jest to, że zawsze res zawiera taką samą wartość dla każdego wiersza. Teraz, jeśli zmienię 200, która jest podświetlona w instrukcji zapytania do dowolnej liczby poniżej 176, pokazuje losowe wiersze table1, co jest pożądanym wynikiem!
Należy pamiętać, że 54 nie ma wpływu na wyniki i jest tam tylko dlatego, że moja tabela ma 54 różnych wartości od 1 do 54 dla aColumn.
Próbowałem tego zapytania na różnych tabelach i to dziwne zachowanie się powtarza!

+0

To brzmi jak może działać optymalizator zapytań. –

+0

@TimBiegeleisen To szwy, że SQL Server robi coś za kulisami, i być może to jest optymalizator zapytań, nie jestem pewien, chyba że wiesz jakoś. BTW, powinien istnieć sposób, by podziękować, proszę, nie rób tego dla mnie! – mok

+0

Być może trzeba będzie wymusić na silniku wykonanie pierwszego podzapytania dla każdego wiersza wyników. Spróbuj dodać obojętny warunek w pierwszym podzapytaniu, który sprawia, że ​​jest to zapytanie korelacyjne, coś takiego jak "AND result.id nie jest puste". – trincot

Odpowiedz

4

Różnice w wynikach można wyjaśnić różnicami w planach kwerend.

Optymalizator SQL może wybrać użycie operatora Spool/Lazy Spool. W takim przypadku funkcja NEWID() jest wywoływana jeden raz, identyfikator GUID jest przechowywany w tabeli tymczasowej i używany dla wszystkich pozostałych wierszy.

https://technet.microsoft.com/en-us/library/ms191221(v=sql.105).aspx

... UPDATE: plan kwerend może być ustalona przez dodanie poniżej linii na dole:

option(use plan 
N' 
<your XML plan> 
') 

złapać "dobry" plan XML, uruchom

SET SHOWPLAN_XML ON 

i wykonaj zapytanie pokazujące oczekiwany wynik. Kopiuj-wklej go do wariantu planu zagospodarowania (...)

Rozwiązanie to działa na mnie za dużą liczbę wierszy (mln)

+0

Musiałem usunąć rozwiązanie (GETDATE()> '2011-01-01') tymczasowo, ponieważ nie zawsze działa, gdy gromadzisz zbyt wiele rekordów (tysiące) I kiedy masz tysiące wierszy w tabeli1. Jutro dostarczę kolejną, jeśli nadal będzie potrzebna. To naprawdę zależy od liczby rzędów, więc jeśli Twój stół jest mały, możesz go nadal używać. – Anton

+0

Dzięki. Teraz już wiem, na czym polega problem. Spróbuję zrobić to jutro, bo czasami stół staje się zbyt duży. – mok

+0

Aby naprawić plan zapytania, można zapisać "dobry" plan, a następnie użyć go ponownie z podpowiedziami "USE PLAN". https://technet.microsoft.com/en-us/library/ms186954(v=sql.105).aspx – Anton

2

Zmusi to podzapytanie być reevaulated na każdym rzędzie i unika skomplikowane logika losowa.

SELECT top 200 
    (SELECT top 1 t1.anotherColumn from table1 t1 with(nolock) where t2.t2Id is not null order by newid() ) res, 
    * 
FROM table2 t2 

To działa, ponieważ newid() jest już losowo unikalny identyfikator i porównanie sił Table2 na każdy wiersz tabela1 być porównywane z rzędu w tabela2.