SQL параметризованный запрос cte - PullRequest
1 голос
/ 21 июля 2011

У меня есть запрос, подобный следующему

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    ....

Следующий подвыбор является общим для всех подвыборов в строке запроса @param для табличной функции.

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

Oneя могу преобразовать это в функцию и вызвать функцию, но мне это не нравится, поскольку я могу довольно часто менять запрос на выборку для ..... или мне интересно, есть ли альтернатива, использующая CTE, например

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    ....

Есть ли такой синтаксис или метод, который я могу использовать следующим образом.

Это в SQL Server 2008 R2

Спасибо.

Ответы [ 4 ]

2 голосов
/ 21 июля 2011

То, что вы пытаетесь сделать, не поддерживается синтаксисом - параметры CTE не могут быть таким образом параметризованы.

См. Книги в Интернете - http://msdn.microsoft.com/en-us/library/ms175972.aspx.

(значения в скобках после имени CTEявляются необязательным списком имен выходных столбцов)

Если есть только два значения параметров (paramPrev и currPrev), вы можете сделать код немного проще для чтения, разбив их на дваCTE - что-то вроде этого:

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   
 ....
1 голос
/ 17 декабря 2013

Могут быть некоторые различия между моим примером и тем, что вы хотите, в зависимости от того, как сформулированы ваши последующие операторы 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, дублированные строки (такие как я включил в этот пример) были бы агрегированы. Это также решение для создания центра, где агрегирование нежелательно.

1 голос
/ 16 сентября 2011

Вы должны иметь возможность обернуть подзапросы как параметризованные встроенные табличные функции, а затем использовать их с OUTER 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 голосов
/ 27 мая 2014

После неудачного назначения скалярных переменных до 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
...