mysql根据2列获得最接近的值

时间:2017-06-27 02:47:49

标签: mysql

给定N和DT的输入值。我需要选择n = N且dt = DT的行 如果这是一个完全匹配,它很容易,但如果没有完全匹配,我需要最近的4行,以便为我的程序计算v的插值。

| n | dt        | v |
| 1 | 06-08-2017| 1 |    
| 2 | 06-08-2017| 2 |
| 3 | 06-08-2017| 3 |
| 5 | 06-08-2017| 4 |
| 7 | 06-08-2017| 5 |
| 1 | 06-10-2017| 2 |
| 2 | 06-10-2017| 3 |
| 3 | 06-10-2017| 4 |
| 5 | 06-10-2017| 5 |
| 8 | 06-10-2017| 6 |

使用上面的简化表。如果N = 6且DT = 06-09-2017。我需要输出

| 5 | 06-08-2017| 4 |
| 7 | 06-08-2017| 5 |
| 5 | 06-10-2017| 5 |
| 8 | 06-10-2017| 6 |

如果完全匹配。如果你返回4行或1或3行(部分匹配)并不重要。插值函数能够处理这个问题。

我可以为1变量

做到这一点
(select * from db where n >= N order by n limit 1)
union
(select * from db where n < N order by n desc limit 1)

但是对于这两个变量都有很多困难。我试过上面两次,但基本上你得到错误的行,因为只有1个变量是正确的。

非常感谢任何帮助

*编辑*

最后,我设法做了我想做的事。

(select * from db from n>=N and dt = (select dt from db where dt >= DT order 
by dt limit 1) order by n limit 1)
union distinct
(select * from db from n<=N and dt = (select dt from db where dt >= DT order 
by dt limit 1) order by n desc limit 1)
union distinct
(select * from db from n>=N and dt = (select dt from db where dt <= DT order 
by dt desc limit 1) order by n limit 1)
union distinct
(select * from db from n<=N and dt = (select dt from db where dt <= DT order 
by dt desc limit 1) order by n desc limit 1)

似乎必须有一个更简单的方法

1 个答案:

答案 0 :(得分:1)

当你说&#39;最接近的&#39;使用两个值,您基本上是在谈论两个二维向量之间的最近距离。因此,为了实现这一目标,您需要为它们定义一个规范。

一个好的起点是euclidean norm用于日期的unix时间戳。

这样的事情:

SQRT(n*n + unix_timestamp(dt)*unix_timestamp(dt))

然后您可以使用该计算的范数作为比较而不是N的值。

记住unix_timestamp仅适用于YYYY-MM-DD格式的日期。

此外,您应该为n添加系数,为dt添加另一个系数以标准化值。如果它们中的任何一个比另一个更大,那么你的标准将倾向于最大组件的价值(我确定你的时间戳会比你的时间更大)。所以你应该做这样的事情:

SQRT(a*n*n + b*unix_timestamp(dt)*unix_timestamp(dt))

其中a和b是0..1

范围内的实数值

例如

SQRT(0.9*n*n + 0.1*unix_timestamp(dt)*unix_timestamp(dt))

与他们一起玩,直到你的结果足够好。

编辑:阐述答案

您面临的问题是,数学上:给定一组S元组(n,dt)和一个特定元组(n&#39;,dt&#39;)其中n和n&#39 ;是整数,dt和dt&#39;是日期,返回距离(n&#39;,dt&#39;)最短距离的S元组的集合

说,你需要定义你的距离。你有两次机会:

  • 你要么按照你的选择从你的问题中选择4个例子(我不知道)来详细说明某种算法。
  • 或者您定义了一组元组的数学距离。我将详细说明这一点。

如果您在笛卡尔平面上代表您的元组,您将看到以下内容:

cartesian_plane

垂直边表示n,水平表示dt。蓝色箭头表示从一个特定tupla到另一个特定tupla的距离。

现在,可以通过多种方式定义此距离。最常见的是欧几里德距离,它由以下表达式定义:

d([n,dt],[n',dt'])= sqrt( (n-n')^2 + (dt-dt')^2 )

现在,您希望所有M结果最小化该距离,让我们构建一个查询。

首先,您需要计算dt和dt之间的差异。这是日期。您可以为每个日期指定标量值,也可以使用某些MySQL函数直接获取天数差异。让我们来做吧。

DATEDIFF(dt, dt')

现在,DATEDIFF需要DATE字段(格式为YYYY-MM-DD,但你的日期是相反的,所以我们需要格式化它们才能使用。在这里我会假设你的固定值dt&#39;将手动正确介绍。

DATEDIFF(str_to_date(date_format(dt, '%d-%m-%Y'), '%d-%m-%Y'), dt')

现在我们有了日期差异,可以构建整个距离:

SQRT(POW((n-n'),2)+POW(DATEDIFF(str_to_date(date_format(dt, '%d-%m-%Y'), dt'), '2017-05-05'),2))

我们现在可以调整一些变量,只需创建一个选择最近值的SQL查询:

SELECT *, SQRT(POW((t.n-N),2)+POW(DATEDIFF(str_to_date(date_format(t.dt, '%d-%m-%Y'), '%d-%m-%Y'), DT),2)) as distance FROM TABLE_NAME t ORDER BY distance ASC LIMIT M;

你需要为你的n&#39;取代N.价值,DT为你的dt&#39; value,M表示您需要的最接近的元组数,TABLE_name表示表的名称。

一些注意事项

  1. 由于DATEDIFF返回天数差异,距离公式的(dt-dt')^2部分的值通常会比部分(n-n')^2大得多。这意味着距离的值将主要由日期组成(在距离值中有更多的决定)。如果这个结果不能让您满意,您可以只为组件添加权重并使用这些值,直到获得足够好的结果。具有权重的查询如下:

    SELECT *, SQRT(A*POW((t.n-N),2)+B*POW(DATEDIFF(str_to_date(date_format(t.dt, '%d-%m-%Y'), '%d-%m-%Y'), DT),2)) as distance FROM TABLE_NAME t ORDER BY distance ASC LIMIT M;

  2. 你需要用A和B代替你的体重。我推荐介于0和1之间的值,其中两者之和为1。 [A = 0.9,B = 0.1]。为A分配更大的值将导致N对距离值的影响更大,与B的DT相同。

    1. 此距离不是唯一的。事实上,没有距离是独一无二的。例如,如果你只使用N的值。你需要在示例表中找到更接近第二行的4行,你会发现第一行和第三行距离它是1个单位。但它不会影响你的问题,不是吗?

    2. 无法以有效的方式预先计算和存储此距离。如果表中有X条目,则需要将每行的距离存储到每个合作伙伴中。这意味着每行需要X-1个额外字段(无论如何都是一个糟糕的实现)。如果你真的对此感兴趣,你可以找到一种方法来为每对记录创建一个具有距离的表,并使用该表的连接来执行该查询。

    3. 此查询使用了大量本机函数和数学运算,因此它不会成为最快的查询。在我的本地环境中,它花费的时间少于执行平面SELECT所需时间的两倍。

    4. 还有其他距离定义,您可以调查并使用最适合您的问题。但无论你如何定义,这个查询背后的想法仍然是最小化距离。

相关问题