2013-07-02 1 views
1

Учитывая таблицу OriginalValues, я хотел бы вернуть как первую запись #Conversion, где ValueFrom больше или равно SourceValue (ближайшая равная до или выше) и первая запись #Conversion, где ValueFrom меньше, чем InitialValue (ближайший ниже), к одной выходной записи для каждого оригинала. (например, OriginalValue, ValueFromAbove, ValueToAbove, ValueFromBelow, ValueToBelow).SQL-запрос: как получить ближайшую запись выше и ниже для каждого значения в таблице 2

Несмотря на наличие нижеприведенного единственного результата запроса, я не могу понять, как присоединиться к таблицам #Values ​​и #Conversion вместе для получения желаемого результата. Кто-нибудь есть идеи о том, как это решить?

Также, пожалуйста, дайте мне знать, будет ли курсор или какой-либо другой метод быстрее, я имею дело с очень большими наборами данных (#Value будет намного больше, чем #Conversion).

Пример Таблица структуры:.

CREATE TABLE #Conversion (ConversionPeriodId int, ValueFrom float, ValueTo float) 
INSERT INTO #Conversion 
VALUES (1, 0, 0.001), (1, 1, 0.05), (1, 1.5, 0.5), (1, 2, 1), 
(2,0,0),(2,1,1),(2,2,4) 

CREATE TABLE #Values (PeriodId int, OriginalValue float) 
insert into #Values 
VALUES (1, 0.01),(1, 2), (1, 1.89625), (1, 1.3), (1, 7), (1, -1) 

Пример запроса для одного OriginalValue (ничего не возвращает для вне диапазона, например @OrigValue = 7, но было бы лучше, если бы он мог вернуться (7) 0, 0, 2, 1)

DECLARE @OrigValue float = 1.89625 

SELECT @OrigValue as OriginalValue, a.ValueFrom AS ValueFromAbove, a.ValueTo AS ValueToAbove, 
b.ValueFrom AS ValueFromBelow, b.ValueTo AS ValueToBelow 
    FROM (SELECT TOP 1 * FROM #Conversion 
    WHERE ConversionPeriodId = 1 
    AND ValueFrom >= @OrigValue 
    ORDER BY ValueFrom) a 
FULL OUTER JOIN 
    (SELECT TOP 1 * FROM #Conversion WHERE ConversionPeriodId = 1 
    AND ValueFrom < (SELECT TOP 1 ValueFrom FROM #Conversion 
    WHERE ConversionPeriodId = 1 
    AND ValueFrom >= @OrigValue 
    ORDER BY ValueFrom) 
    ORDER BY ValueFrom DESC) b 
    ON a.ConversionPeriodId = b.ConversionPeriodId 
    AND a.ValueFrom = (SELECT TOP 1 x.ValueFrom FROM #Conversion x 
    WHERE x.ValueFrom > b.ValueFrom ORDER BY ValueFrom) 

--OriginalValue ValueFromAbove ValueToAbove ValueFromBelow ValueToBelow 
--1.89625   2     1    1.5    0.5 

DROP TABLE #Conversion 

DROP TABLE #Values 

(я не уверен, что приведенный выше запрос является лучшим способом пойти об этом. Любые предложения по оптимизации будут оценены!)

Моя цель состоит в том, чтобы получить всю Рез ult для # значений за один раз.

Пример желаемого результата:

PeriodId OriginalValue ValueFromAbove ValueToAbove ValueFromBelow  ValueToBelow 
1   0.01   1    0.05   0     0.001 
1   2    2    1    1.5     0.5 
1   1.89625   2    1    1.5     0.5 
1   1.3    1.5    0.5    1     0.05 
1   7    0    0    0     0 
1   -1    0    0.001   0     0 

Результат для 7 может вместо того, чтобы быть = 1, 7, 0, 0, 2, 1.

В колонке PeriodId не было бы необходимости, если только в запросе можно предоставить несколько периодов PeriodIds. например. вставить в #Values ​​VALUES (1, 0.01), (2, 0.1) (я не против слишком много, если это не так).

ответ

1

Понял работает на дополнительных PeriodIds, в MSSQL 2008, и возвращение правильно для

insert into c_Values (2,1.5) 

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

SELECT cv.PeriodID, cv.OriginalValue, 
    c1.ValueFrom ValueFromAbove, 
    c1.ValueTo ValueToAbove, 
    c2.ValueFrom ValueFromBelow, 
    c2.ValueTo ValueToBelow 
FROM c_values cv 
LEFT JOIN Conversion c1 ON 
    cv.PeriodId = c1.ConversionPeriodId 
    AND c1.ValueFrom = ISNULL(
     (select top 1 ValueFrom FROM Conversion WHERE ConversionPeriodId = cv.PeriodId 
     AND ValueFrom >= cv.OriginalValue order by ValueFrom), 
     (select top 1 ValueFrom From Conversion WHERE ConversionPeriodId = cv.PeriodId 
     ORDER BY ValueFrom desc)) 
    --if c1.ValueFrom IS NULL (above given range), return highest value. 
LEFT JOIN Conversion c2 
    ON 
    cv.PeriodId = c2.ConversionPeriodId 
    AND c2.ValueFrom = ISNULL(
     (select top 1 ValueFrom FROM Conversion WHERE ConversionPeriodId = cv.PeriodId 
     AND ValueFrom < cv.OriginalValue order by ValueFrom desc), 
     c1.ValueFrom) 
    --if c2.ValueFrom IS NULL (below given range) return lowest value. 

будет работать при копировании в вышеуказанную Fiddle и установлен в 'MS SQL Server 2008'

1

Попробуйте этот запрос (Fiddle):

SELECT cv.PeriodID, cv.OriginalValue, 
c1.ValueFrom ValueFromAbove, 
c1.ValueTo ValueToAbove, 
c2.ValueFrom ValueFromBelow, 
c2.ValueTo ValueToBelow 
FROM c_values cv LEFT JOIN Conversion c1 ON 
cv.PeriodId = c1.ConversionPeriodId 
AND c1.ValueFrom >= cv.OriginalValue 
LEFT JOIN Conversion c2 ON 
cv.PeriodId = c2.ConversionPeriodId 
AND c2.ValueFrom <= cv.OriginalValue 
WHERE 
CASE WHEN c1.ValueFrom IS NULL THEN NULL 
ELSE c1.ValueFrom END <= ALL (SELECT ValueFrom from Conversion 
        WHERE ValueFrom >= cv.OriginalValue) 
AND 
CASE WHEN c2.ValueFrom IS NOT NULL THEN c2.ValueFrom 
ELSE NULL END >= ALL (SELECT ValueFrom from Conversion 
        WHERE ValueFrom <= cv.OriginalValue) 
+0

Отлично! Я даже не знал, что вы можете использовать случаи в разделе where, +1 для обучения! Может ли это работать для #Values ​​VALUES (1, 0,01), (2, 0,1)? (более одного периода)? – user1314350

+0

Да, я думаю, он должен работать для всех значений PeriodID. – Mikhail

+0

! Вставить в c_Values ​​VALUES (2,1.5) работает только тогда, когда Conversion не содержит (1, 1,5, 0,5) ?? – user1314350