2013-01-02 8 views
6

Mam zapytanie SQL napisane przez kolegę, którego już nie ma. Kwerenda działa jako część pracy SSIS, a od tego miesiąca rozpoczęła się niepowodzeniem z powodu następującego błędu:SQL - Date Query Issue - konwersja varchar do datetime spowodowała przekroczenie zakresu wartości

The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.

Samo zapytanie jest po prostu podstawowy wybrać z warunkiem WHERE który szuka wartości w określonym czasie Range (zakres dat pomiędzy @startdate i @enddate)

Kod określić zakres czasu jest poniżej:

DECLARE  @RunDateTime datetime 
DECLARE  @RunDate datetime 
DECLARE  @StartDate datetime 
DECLARE  @EndDate datetime 
DECLARE  @Month int 
DECLARE  @Year int 
DECLARE  @strStartDate varchar(10) 

SET  @RunDateTime = GetDate() 
SET  @RunDate = cast(round(convert(real, @RunDateTime),0,1) as datetime) 

IF     DATEPART(d, @RunDate) = 16 
    BEGIN  
        SET @StartDate = DATEADD(d, -15, @RunDate) 
        SET @EndDate = @RunDate 
    END 
ELSE 
    BEGIN 
        IF  Month(@RunDate) = 1 
          SET @Month = 12 
        ELSE  
          SET @Month = Month(@RunDate) - 1 

        IF  Month(@RunDate) = 1 
          SET @Year = Year(@RunDate) - 1 
        ELSE 
          SET @Year = Year(@RunDate) 

        SET @strStartDate = CONVERT(varchar(2), @Month)+ '/16/' + CONVERT(varchar(4), @Year) 
        SET @StartDate = CONVERT(datetime, @strStartDate, 101) 
        SET @EndDate = @RunDate 
    END 

Ta praca przebiega dwa razy w miesiącu. Raz na 16 na dane od 1 do 15 dnia miesiąca, a raz na 1 na następny miesiąc dla danych od 16 do końca poprzedniego miesiąca.

Z tego, co mogę znaleźć w sieci, najprawdopodobniej winien korzystać z varchar dla strStartDate? Nie jestem wystarczająco zaznajomiony z SQL, aby wiedzieć, jak zastąpić wszystko, co się tam dzieje? Czy istnieje lepszy sposób określenia daty końca miesiąca niż uzyskanie czasu pracy? Jakakolwiek pomoc w ogóle byłaby bardzo doceniana.

(PS, uruchamiamy to zadanie w SQL Server 2008 R2). Sprawdziłem z DBA i nie powiedział nic o lokalizacji lub zmianie ustawień regionalnych na serwerze SQL.

+1

Wygląda na to, że próbujesz obliczyć 16 z ostatniego miesiąca. Zamiast manipulować ciągami, wypróbuj 'SET @StartDate = DATEADD (miesiąc, DATEDIFF (miesiąc, '20010101', @ RunDate), '20001216')', który powinien zawsze go znaleźć (zachować dwie stałe łańcuchowe dokładnie takie jakie są) . –

+1

Czy jesteś pewien, że błąd znajduje się w sekcji opublikowanego kodu? 101 na linii CONVERT (datetime, @strStartDate, 101) wskazuje, że strStartDate powinien być w formacie mm/dd/rrrr, który to jest, więc nie powinien być zależny od ustawień regionalnych Serwera. – sgmoore

+0

@Damien_The_Unbeliever Tak, zapytanie to będzie zawsze uruchamiane od 1 do 16 dnia miesiąca. Jeśli nie jest uruchamiany 16 dnia miesiąca, musi być pierwszy, więc chcemy, aby wartość StartDate reprezentowała 16 dzień poprzedniego miesiąca. (Zasadniczo chcemy tylko uzyskać dzień # 16 poprzedniego miesiąca, jeśli zapytanie jest uruchamiane w dniu 1) Nie jestem językiem SQL, ale czy ten kod, który podałeś, działa przez miesiąc? Lub po prostu zrobić na grudzień? – Oryx

Odpowiedz

4

Istnieje wiele formatów obsługiwanych przez SQL Server - patrz: MSDN Books Online on CAST and CONVERT. Większość z tych formatów to zależnych od zależnie od ustawień - dlatego te ustawienia mogą działać czasem - a czasami nie.

Sposobem rozwiązania tego problemu jest użycie (nieco dostosowany) ISO-8601 format daty, który jest obsługiwany przez SQL Server - działa ten format zawsze - niezależnie od ustawień językowych i dateformat SQL Server.

ISO-8601 format jest obsługiwany przez SQL Server jest dostępny w dwóch smakach:

  • YYYYMMDD tylko dla dat (bez części czasu); zanotuj tutaj: bez kreski!, to bardzo ważne! YYYY-MM-DD jest NOT niezależnie od ustawień daty w twoim Serwerze SQL i będzie działać w każdej sytuacji!

czyli

  • YYYY-MM-DDTHH:MM:SS dla dat i czasów - uwaga tutaj: ten format ma kreski (ale może zostać pominięte) oraz stałą T jako separatora pomiędzy datą i godziną część twojego DATETIME.

Dotyczy to programów SQL Server 2000 i nowszych.

Jeśli używasz SQL Server 2008 lub nowszy i DATE typ danych (tylko DATE - nieDATETIME), to rzeczywiście może także używać formatu na YYYY-MM-DD i że będzie działać także z wszelkimi ustawieniami w programie SQL Server .

Ponadto: w SQL Server 2008 zaleca się używanie DATETIME2 (zamiast DATETIME), jeśli jest to w ogóle możliwe. DATETIME2 parsowanie ciągów jest dużo bardziej wybaczające dla błędów i/lub różnych formatów (np. Formatowanie AM/PM w USA itp.).

Nie pytaj mnie, dlaczego ten cały temat jest tak trudny i nieco zagmatwany - tak po prostu jest. Ale z formatem YYYYMMDD powinieneś być w porządku dla każdej wersji SQL Server oraz dla dowolnego języka i ustawienia daty w twoim Serwerze SQL.

+0

Dzięki za tak dokładną odpowiedź. Pracuję nad czytaniem linków, ale myślę, że nie jestem tak pewien praktycznej strony jak modyfikować SQL, który napisałem. Czy po prostu zmieniam parametr strStartDateTime na datetime2? Jeśli tak, czy muszę powtórzyć logikę, aby znaleźć 16 dzień miesiąca, skoro wydaje się, że zależy to od tego, że jest to varchar? Przepraszam, jeśli są one głupie? S: – Oryx

+1

@Oryx: jeśli potrzebujesz skonstruować ciąg do przetworzenia na 'DATETIME', po prostu upewnij się, aby użyć jednego z ** formatów ** safe - więc zamień to:' SET @strStartDate = CONVERT (varchar (2), @Month) + '/ 16 /' + CONVERT (varchar (4), @Year) "z czymś takim:' YYYYMM16', a następnie konwertuj - jest to bezpieczne i niezależne od języka format, który zawsze konwertuje się dobrze. –

+1

@Oryx: jeśli potrzebujesz skonstruować ciągi reprezentacji dat, użyj bezpiecznych formatów, np. skonstruuj '20130116' na 16 stycznia 2013 r. - a następnie przeanalizuj to. To zostanie przekształcone na 'DATETIME' (lub' DATE' lub 'DATETIME2') na dowolnej instalacji SQL Server, z dowolnym językiem i/lub ustawieniem formatu daty - bez względu na wszystko. –