Как получить функцию SQL для возврата списка, который будет использоваться оператором IN в предложении WHERE? - PullRequest
1 голос
/ 07 июля 2010

У меня сложный SQL-запрос, который необходимо отфильтровать. Часть предложения WHERE выглядит следующим образом:

Where P.PeriodID in (36, 37)

Мне нужно, чтобы он выглядел так:

Where P.PeriodID in dbo.GetPeriodsInRange(@startDate, @endDate)

Функция должна возвращать список PeriodID, которые будут использоваться оператором IN. Я действительно отстой в написании функций, поэтому мне нужна помощь. Кроме того, я не уверен, как обращаться с крайними случаями, например, если в указанном диапазоне дат нет периодов.

Приведенная выше функция не нуждается в оценке для каждой строки. Он будет одинаковым для каждой строки, поэтому, вероятно, можно выполнить некоторую оптимизацию, возможно, до выполнения запроса.

Я почти уверен, что нарушаю несколько «лучших практик», поэтому, пожалуйста, укажите их мне, если есть лучший способ сделать это. Однако производительность не является проблемой, поэтому я готов пожертвовать производительностью ради простоты.

Мой вопрос относится к T-SQL (MS SQL Server 2000/2005)

Ответы [ 5 ]

2 голосов
/ 07 июля 2010

Мое решение было бы создать функцию, как предложил Муду.Я использую this .

По ссылке:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[ufn_GenerateIntegers] ( @MinValue INT, @MaxValue INT )
RETURNS @Integers TABLE ( [IntValue] INT )
AS
BEGIN
    WHILE @MinValue <= @MaxValue
    BEGIN
        INSERT INTO @Integers ( [IntValue] ) VALUES ( @MinValue )
        SET @MinValue = @MinValue + 1
    END

    RETURN
END
GO

После этого я использую внутреннее соединение, например:

use tempdb

select *into #test from 
(
    select 1 as n, 35 as periodId
    union 
    select 2 as n, 36 as periodId
    union 
    select 1 as n, 36 as periodId
    union 
    select 2 as n, 37 as periodId
) a

select p.* from #test   p
inner join  [dbo].[ufn_GenerateIntegers](36, 37) on [IntValue] = periodId

Youможет сгенерировать строку, а затем выполнить ее, но я думаю, что здесь это не очень рекомендуется.

1 голос
/ 07 июля 2010

Во-первых, вы не «мыслите в наборах».Язык SQL имеет только одну структуру данных, являющуюся таблицей, т.е. строками столбцов.Тип данных столбца должен быть скалярным, чтобы соответствовать первой нормальной форме.Таким образом, нет никаких массивов, списков и т. Д.

Хотя вы можете сгенерировать список значений PeriodID, затем добавить список в текст запроса SQL, а затем использовать возможности динамического SQL для его выполнения, это не так.путь.

Считайте, что ваш

Where P.PeriodID in (36, 37)

... может быть переписан как

Where P.PeriodID IN (
                     SELECT 36
                     UNION ALL
                     SELECT 37
                    )

... или даже

WHERE EXISTS (
              SELECT * 
                FROM (
                      SELECT 36
                      UNION ALL
                      SELECT 37
                     ) AS DT1 (PeriodID)
               WHERE P.PeriodID = DT1.PeriodID
              );

... и, надеюсь, у вас появится идея работать с таблицами, а не со списками идентификаторов.

Тем не менее, делая шаг назад, это похоже на случай, когда вам лучше работать с естественным ключом в течение периодов, являясь составным (StartDate, EndDate), а не искусственным / суррогатным ключом PeriodID.Работа только с датами означает, что вам просто нужно найти совпадение, например

SELECT P1.PeriodID
  FROM Periods AS P1
 WHERE CASE 
          WHEN @StartDate > P1.StartDate THEN @StartDate 
          ELSE P1.StartDate
       END 
       <= 
       CASE 
          WHEN @EndDate > P1.EndDate THEN P1.EndDate 
          ELSE @EndDate 
       END;
1 голос
/ 07 июля 2010

Я бы написал табличную функцию, которая возвращает таблицу из одного столбца с вашими периодами.Затем вы можете присоединиться к этому табличному результату.Таким образом, он должен выполняться только один раз, а не для каждой строки (но я не совсем уверен в этом).

Дополнительная информация: Табличные пользовательские функции (MSDN)

Приветствия Матиаса

1 голос
/ 07 июля 2010

Что ж, если getPeriodsInRange на самом деле является просто запросом SQL, вы можете просто использовать вложенный запрос вместо процедуры (вы сказали, что проще!):

Where P.PeriodID in (Select PeriodID from MyDateTable WHERE pd > SomeMinValue AND pd < SomeMaxValue)
0 голосов
/ 07 июля 2010

Что бы вы ни делали в функции GetPeriodsInRange - вы не можете просто вставить это в основной запрос, как ...

Where P.PeriodID in
(
 select PeriodID from...etc
)

Это не будет работать, если функция делает более сложные вещи! Может быть, вы можете дать нам посмотреть, что в функции?

...