2011-08-11 9 views
5

Modyfikuję istniejące zapytanie dla klienta i napotkałem na nieco kłopotliwy problem.SQL Konwersja daty i godziny nie działa, gdy nie ma miejsca konwersja

Nasz klient korzysta z SQL Server 2008 R2, a baza danych, o której mowa, umożliwia użytkownikowi określenie pól niestandardowych dla jednej ze swoich tabel, korzystając ze struktury EAV. Wszystkie wartości przechowywane w tej strukturze to varchar(255), a kilka pól jest przeznaczonych do przechowywania dat. Zapytanie, o którym mowa, jest modyfikowane w celu użycia dwóch z tych pól i porównania ich (jeden jest początkiem, drugi jest końcem) z bieżącą datą w celu ustalenia, który wiersz jest "aktualny".

Problem mam jest to, że część zapytania robi CONVERT(DateTime, eav.Value) aby włączyć varchar w DateTime. Same konwersje wszystko succedd i mogę to wartość jako część klauzuli SELECT, ale część pytania daje mi błąd konwersji:

Conversion failed when converting date and/or time from character string. 

Prawdziwy kicker jest taka: jeśli zdefiniować podstawę do tego zapytanie (uzyskanie listy jednostek z dwiema niestandardowymi wartościami pól spłaszczonymi w jednym wierszu) jako widoku i wybierz z widoku i filtruj widok przez getdate(), to działa poprawnie, ale nie powiedzie się, jeśli dodaję sprzężenie do sekundy tabeli przy użyciu jednego z pól (nieaktualnych) z widoku. Zdaję sobie sprawę, że może to być nieco trudne do naśladowania, więc mogę wysłać przykładową kwerendę w razie potrzeby, ale to pytanie już trwa trochę długo.

Próbowałem odtworzyć podstawową strukturę w innej bazie danych, w tym przykładowe dane, ale nowa baza danych zachowuje się zgodnie z oczekiwaniami, więc jestem tu zagubiony.

EDIT W przypadku, jest to przydatne, oto zestawienie dla widoku:

create view Festival as 
select 
    e.EntityId as FestivalId, 
    e.LookupAs as FestivalName, 
    convert(Date, nvs.Value) as ActivityStart, 
    convert(Date, nve.Value) as ActivityEnd 

from tblEntity e 

left join CustomControl ccs on ccs.ShortName = 'Activity Start Date' 
left join CustomControl cce on cce.ShortName = 'Activity End Date' 
left join tblEntityNameValue nvs on nvs.CustomControlId = ccs.IdCustomControl and nvs.EntityId = e.EntityId 
left join tblEntityNameValue nve on nve.CustomControlId = cce.IdCustomControl and nve.EntityId = e.EntityId 

where e.EntityType = 'Festival' 

niewydolnego zapytania to:

select * 

from Festival f 

join FestivalAttendeeAll fa on fa.FestivalId = f.FestivalId 

where getdate() between f.ActivityStart and f.ActivityEnd 

Jednak to działa:

select * 

from Festival f 

where getdate() between f.ActivityStart and f.ActivityEnd 

(EntityId/FestivalId ar e int columns)

+0

prawdopodobnie ze względu na ustawienia narodowe i daty sformatowane w USA i poza USA ... Sprawdź ustawienia regionalne ... Czy możesz podać przykładowe dane pokazujące zachowanie? –

+0

Może zacznij od pierwszego zapytania, które się nie powiedzie i opublikuj. – Paparazzi

+0

@Mitch: To byłaby moja pierwsza myśl, gdybym też odpowiadała, ale są to wszystkie daty z USA, sformatowane w formacie "1 stycznia 2011 r. 12:00" lub "01.01.2011 00:00 ". Mylącą częścią jest to, że błąd zdaje się być związany z czymś, co (według mojej logiki, która oczywiście może być błędna) nie powinno mieć żadnego wpływu na to, czy konwersja ma miejsce. Założę się, że gdyby dane były rzeczywiście złe, wybranie z samego widoku wygenerowałoby błąd, którego nie robi. –

Odpowiedz

11

Napotkałem tego typu błąd wcześniej, jest to spowodowane "kolejnością operacji" wykonywaną przez plan wykonania.

Otrzymujesz komunikat o błędzie, ponieważ plan wykonania instrukcji (generowany przez optymalizator) wykonuje operację CONVERT() w wierszach zawierających wartości ciągów, których nie można przekonwertować na DATETIME.

Zasadniczo nie masz kontroli nad wierszami, na których optymalizator wykonuje tę konwersję. Wiesz, że potrzebujesz tylko konwersji wykonanej dla niektórych wierszy i masz predykaty (klauzule WHERE lub ON), które wykluczają te wiersze (ogranicz wiersze do tych, które wymagają konwersji), ale twój plan wykonania wykonuje operację CONVERT() w wierszach PRZED tymi wierszami są wykluczone.

(Na przykład, optymalizator może być wyboru do zrobienia skanu tabeli i wykonywania takiej konwersji w każdym rzędzie, zanim jest stosowany każdy orzecznik.)

nie mogę dać konkretnej odpowiedzi, bez konkretne pytanie i określony SQL, który generuje błąd.


Prostym podejściem do rozwiązania tego problemu byłoby użycie funkcji ISDATE(), aby sprawdzić, czy wartość Łańcuch może być przekształcony w terminie.

To wymienić:

CONVERT(DATETIME,eav.Value) 

z:

CASE WHEN ISDATE(eav.Value) > 0 THEN CONVERT(DATETIME, eav.Value) ELSE NULL END 

czyli

CONVERT(DATETIME, CASE WHEN ISDATE(eav.Value) > 0 THEN eav.Value ELSE NULL END) 

Należy zauważyć, że funkcja ISDATE() podlega pewnym znaczących ograniczeń, takich jak wpływ na ustawienia DATEFORMAT i LANGUAGE sesji.


Jeśli istnieje jakieś inne wskazanie w wierszu eav, można użyć innego testu, aby warunkowo wykonać konwersję.

CASE WHEN eav.ValueIsDateTime=1 THEN CONVERT(DATETIME, eav.Value) ELSE NULL END 

Drugie podejście Użyłem jest, aby spróbować zdobyć odrobinę kontroli nad kolejności działania optymalizatora, używając wbudowanych poglądy lub wspólnym stole wyrażeń, z działalności, które zmuszają do optymalizator zmaterializuj je i zastosuj predykaty, tak aby stało się PRZED każdą konwersją w zapytaniu zewnętrznym.

+0

Excellent; wydaje się trochę oczywiste, że jest to możliwe teraz, gdy o tym wspominasz; nawet jeśli wydaje się, że * powinien * zmaterializować wartości jako część widoku, może to uniemożliwić. Zmiana widoku w celu użycia 'ISDATE' w ramach projekcji rozwiązała problem; dzięki! –

+1

+1, ale dla twojego ostatniego akapitu, zauważ, że [to podejście nie zawsze działa] (http://sqlfiddle.com/#!3/0b4c1/1). –

Powiązane problemy