1

给定一个 OriginalValues 表,我想返回 ValueFrom 大于或等于 OriginalValue(最近等于或高于)的第一个 #Conversion 记录,以及 ValueFrom 小于 OriginalValue 的第一个 #Conversion 记录(最近下面),到每个 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, 0, 0, 2, 1。

除非在查询中可以提供多个 PeriodId,否则不需要 PeriodId 列。例如。插入 #Values VALUES (1, 0.01),(2, 0.1) (如果不是此时,我不会太介意)。

4

2 回答 2

1

试试这个查询(小提琴):

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)
于 2013-07-02T09:48:24.143 回答
1

让它在 MSSQL 2008 中为额外的 PeriodIds 工作,并正确返回

insert into c_Values (2,1.5)

当值超出范围时,现在还返回最小值和最大值,而不是 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.

复制到上述小提琴并设置为“MS SQL Server 2008”时将起作用

于 2013-07-04T01:18:20.553 回答