给定一个OriginalValues表,我想返回第一个#Conversion记录,其中ValueFrom大于或等于OriginalValue(最接近等于或大于),以及第一个#Conversion记录,其中ValueFrom小于OriginalValue(最近),每个OriginalValue的一个输出记录。 (例如,OriginalValue,ValueFromAbove,ValueToAbove,ValueFromBelow,ValueToBelow)。
尽管有下面的单个结果查询,但我无法弄清楚如何将#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
(我不确定以上查询是解决此问题的最佳方式。任何优化建议都将不胜感激!)
我的目标是一次性获得#Values的整个结果。
示例所需结果:
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,7,0,2,1。
除非在查询中可以提供多个PeriodId,否则不需要PeriodId列。例如。插入#Values VALUES(1,0.01),(2,0.1)(如果不是在这一点,我不介意太多。)
答案 0 :(得分: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)
答案 1 :(得分:1)
在MSSQL 2008中使用其他PeriodIds,并正确返回
insert into c_Values (2,1.5)
当值超出范围时,现在还返回min和max值而不是null或零。
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”
时可以正常工作