2013-02-26 11 views
24

Zrozumiałem, że niektóre wersje Microsoft OLE DB Provider dla SQL Server (głównie w systemie Windows XP) nie obsługują oświadczenia WITH. Postanowiłem więc przenieść instrukcję SQL do funkcji o wartości tabelarycznej i wywołać ją z mojej aplikacji. Teraz utknąłem. Jak powinienem użyć oświadczenia INSERT INTO z WITH? Oto kod mam pochodzą z tak daleko, ale SQL Server nie podoba ... :-(Jak używać instrukcji CTE w funkcji zwracającej tabelę w SQL Server

CREATE FUNCTION GetDistributionTable 
(
    @IntID int, 
    @TestID int, 
    @DateFrom datetime, 
    @DateTo datetime 
) 
RETURNS 
@Table_Var TABLE 
(
    [Count] int, 
    Result float 
) 
AS 
BEGIN 
INSERT INTO @Table_Var ([Count], Result) WITH T(Result) 
    AS (SELECT ROUND(Result - AVG(Result) OVER(), 1) 
     FROM RawResults WHERE IntID = @IntID AND DBTestID = @TestID AND Time >= @DateFrom AND Time <= @DateTo) 
SELECT COUNT(*) AS [Count], 
     Result 
FROM T 
GROUP BY Result 

    RETURN 
END 
GO 

Odpowiedz

33

Można pominąć CTE (WITH Statement), a zamiast tego utworzyć tabelę inline funkcji wartościową że zastosowania podzapytanie:

CREATE FUNCTION GetDistributionTable 
(
    @IntID int, 
    @TestID int, 
    @DateFrom datetime, 
    @DateTo datetime 
) 
RETURNS TABLE 
AS 
RETURN 
(
    SELECT COUNT(*) AS [Count], 
      Result 
    FROM (
       SELECT ROUND(Result - AVG(Result) OVER(), 1) Result 
       FROM RawResults 
       WHERE IntID = @IntID 
       AND DBTestID = @TestID 
       AND Time >= @DateFrom 
       AND Time <= @DateTo  
    ) t 
    GROUP BY 
      Result 
) 
GO 

Składnia CTE w funkcji tabeli wyceniane byłyby:

CREATE FUNCTION GetDistributionTable 
(
    @IntID int, 
    @TestID int, 
    @DateFrom datetime, 
    @DateTo datetime 
) 
RETURNS TABLE 
AS 
RETURN 
(
    WITH cte AS 
    (
     SELECT ROUND(Result - AVG(Result) OVER(), 1) Result 
     FROM RawResults 
     WHERE IntID = @IntID 
     AND DBTestID = @TestID 
     AND Time >= @DateFrom 
     AND Time <= @DateTo  
    ) 

    SELECT COUNT(*) AS [Count], 
      Result 
    FROM cte 
    GROUP BY 
      Result 
) 
GO 

Twój przykład wydaje się być za pomocą TVF multi-statement (insert i select), kiedy masz wybór próbować używając TVF inline ponieważ wielofunkcyjnego rachunku TVF może zapobiec optymalizator kwerendy w wyborze lepszy plan wykonania (różnica wydajności wyjaśnił here)

+0

Jeśli CTE jest rekurencyjne pewnie wygrał nie będzie w stanie przepisać go do postaci podkwerendy, więc forma CTE może być czymś więcej niż tylko kwestią gustu. Oczywiście, rekurencyjne CTE mogą wyłączyć optymalizator, jeśli nie jesteś ostrożny: MattW

+0

Tak używane do używania ';' przed częścią z cte. – JJS

14

tak ..

CREATE FUNCTION GetDistributionTable 
(
    @IntID int, 
    @TestID int, 
    @DateFrom datetime, 
    @DateTo datetime 
) 
RETURNS 
@Table_Var TABLE 
(
    [Count] int, 
    Result float 
) 
AS 
BEGIN 
    WITH T 
    AS ( 
     select Ticket_Id,COUNT(1) Result from 
     Customer_Survey 
     group by MemberID,SiteId,Ticket_Id 
    ) 
    INSERT INTO @Table_Var ([Count], Result) 
    SELECT COUNT(*) AS [Count], 
     Result 
    FROM T 
    GROUP BY Result 
    RETURN 
END 
GO 
+1

Jeśli to możliwe, powinieneś przetestować zarówno funkcję wielostaniową, jak pokazałeś, jak i funkcję pojedynczego zdania 'RETURNS TABLE', ponieważ ta ostatnia może być wstawiona i dlatego ma czasami znacznie lepszą wydajność (patrz link na dole Odpowiedź Ivana G). Jednak, jak zwykle, wszystko zależy - * czasami *, choć rzadko, szybciej jest użyć funkcji wielostaniowej. – ErikE

Powiązane problemy