2011-07-21 211 views
1

我有一個像下面SQL參數化查詢CTE

select * 
from  (
       select * 
       from  callTableFunction(@paramPrev) 
       .....< a whole load of other joins, wheres , etc >........ 
      ) prevValues 
      full join 
      (
       select * 
       from callTableFunction(@paramCurr) 
       .....< a whole load of other joins, wheres , etc >........ 
      ) currValues    on prevValues.Field1 = currValues.Field1 
      ....<other joins with the same subselect as the above two with different parameters passed in 
where  ........ 
group by .... 

以下子選擇是共同在查詢欄中所有的子查詢的@參數表函數的查詢。

 select * 
     from callTableFunction(@param) 
      .....< a whole load of other joins, wheres , etc >........ 

一種選擇是對我來說,這個轉換成一個功能,調用函數,但我不喜歡這樣,因爲我可能會經常改變 子選擇查詢或.....我想知道有喜歡

with sometable(@param1) as 
(
     select * 
     from callTableFunction(@param) 
       .....< a whole load of other joins, wheres , etc >........ 
) 
select  
     sometable(@paramPrev)  prevValues 
     full join sometable(@currPrev) currValues on prevValues.Field1 = currValues.Field1 
where  ........ 
group by .... 

使用CTE 另一種方法是有這樣或技術,我可以使用任何這樣的語法。

這是在SQL Server 2008 R2中

謝謝。

回答

2

你想要做的是不支持的語法 - CTE's不能以這種方式參數化。

在線看書 - http://msdn.microsoft.com/en-us/library/ms175972.aspx

(在CTE名稱後括號中的數值是輸出列名的可選列表)

如果只有兩個參數值(paramPrevcurrPrev),你也許可以使代碼更容易一些,以通過拆分他們讀成兩個熱膨脹係數 - 是這樣的:

with prevCTE as (
      select * 
      from callTableFunction(@paramPrev) 
        .....< a whole load of other joins, wheres , etc 
........) 
,curCTE as (
      select * 
      from callTableFunction(@currPrev) 
        .....< a whole load of other joins, wheres , etc 
........), 
select  
      prevCTE  prevValues 
      full join curCTE currValues on 
prevValues.Field1 = currValues.Field1 where 
........ group by 
.... 
+0

這是我首先,但它沒有解決任何問題。它只是將子選擇符移動到CTE部分,我仍然重複了代碼。我想要做的是使用普通子查詢(使用表函數調用)來減少整個查詢,所以如果我想對子查詢中的邏輯進行更改,我可以在一個地方完成。我現在可以想到的最好的方法是將其封裝在一個函數中,但我不喜歡這種方法。 – ManiP

1

你應該能夠包裹起來的子查詢作爲參數內嵌表值函數,然後用外使用它們JOIN:

CREATE FUNCTION wrapped_subquery(@param int) -- assuming it's an int type, change if necessary... 
RETURNS TABLE 
RETURN 
    SELECT * FROM callTableFunction(@param) 
    .....< a whole load of other joins, wheres , etc ........ 
GO 

SELECT * 
FROM 
    wrapped_subquery(@paramPrev) prevValues 
     FULL OUTER JOIN wrapped_subquery(@currPrev) currValues ON prevValues.Field1 = currValues.Field1 
WHERE  ........ 
GROUP BY .... 
+0

對於我的用例,我沒有使用CREATE FUNCTION所需的權限級別。仍然想知道最好的解決方案是什麼。 –

1

根據您的後續ON語句的制定方式,我的示例和您想要的可能存在一些差異。既然你沒有指定,我認爲所有後來的連接都是針對第一個表。 在我的示例中,我使用的是文字而不是@ prev,@ current,但是您可以輕鬆地用變量代替文字來實現您想要的內容。

-- Standin function for your table function to create working example. 
CREATE FUNCTION TestMe(
    @parm int) 
RETURNS TABLE 
AS 
RETURN 
    (SELECT @parm AS N, 'a' AS V UNION ALL 
    SELECT @parm + 1, 'b'  UNION ALL 
    SELECT @parm + 2, 'c'  UNION ALL 
    SELECT @parm + 2, 'd'  UNION ALL 
    SELECT @parm + 3, 'e'); 
go   
-- This calls TestMe first with 2 then 4 then 6... (what you don't want) 
-- Compare these results with those below 
SELECT t1.N AS AN, t1.V as AV, 
     t2.N AS BN, t2.V as BV, 
     t3.N AS CN, t3.V as CV 
    FROM TestMe(2)AS t1 
    FULL JOIN TestMe(4)AS t2 ON t1.N = t2.N 
    FULL JOIN TestMe(6)AS t3 ON t1.N = t3.N; 

-- Put your @vars in place of 2,4,6 adding select statements as needed 
WITH params 
    AS (SELECT 2 AS p UNION ALL 
     SELECT 4 AS p UNION ALL 
     SELECT 6 AS p) 
    -- This CTE encapsulates the call to TestMe (and any other joins) 
    ,AllData 
    AS (SELECT * 
      FROM params AS p 
      OUTER APPLY TestMe(p.p)) -- See! only coded once 
      -- Add any other necessary joins here 

    -- Select needs to deal with all the columns with identical names 
    SELECT d1.N AS AN, d1.V as AV, 
      d2.N AS BN, d2.V as BV, 
      d3.N AS CN, d3.V as CV 
     -- d1 gets limited to values where p = 2 in the where clause below 
     FROM AllData AS d1 
     -- Outer joins require the ANDs to restrict row multiplication 
     FULL JOIN AllData AS d2 ON d1.N = d2.N 
          AND d1.p = 2 AND d2.p = 4 
     FULL JOIN AllData AS d3 ON d1.N = d3.N 
          AND d1.p = 2 AND d2.p = 4 AND d3.p = 6 
     -- Since AllData actually contains all the rows we must limit the results 
     WHERE(d1.p = 2 OR d1.p IS NULL) 
     AND (d2.p = 4 OR d2.p IS NULL) 
     AND (d3.p = 6 OR d3.p IS NULL); 

你想要做的是類似於一個支點和因此所需要查詢的複雜相似,不使用樞軸語句創建一個透視的結果。
如果您使用Pivot,則重複行(例如本示例中包含的I)會被聚合。這也是在不需要聚合的情況下進行樞紐的解決方案。

0

未能with之前指定的標量變量後,我終於得到了使用存儲過程和一個臨時表中的工作液:

create proc hours_absent(@wid nvarchar(30), @start date, @end date) 
as 
with T1 as(
    select c from t 
), 
T2 as(
    select c from T1 
) 
select c from T2 
order by 1, 2 
OPTION(MAXRECURSION 365) 

調用存儲過程:

if object_id('tempdb..#t') is not null drop table #t 

create table #t([month] date, hours float) 
insert into #t exec hours_absent '9001', '2014-01-01', '2015-01-01' 

select * from #t