mysql基于2列获取最接近的值

mysql基于2列获取最接近的值,mysql,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-201

给定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行进行部分匹配并不重要。插值函数能够处理这个问题

我可以用一个变量来做

(select * from db where n >= N order by n limit 1)
union
(select * from db where n < N order by n desc limit 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)

似乎有一种更简单的方法,当你说两个值“最接近”时,你基本上说的是两个二维向量之间的最接近距离。因此,为了让它发挥作用,你需要对它们定义一个规范

一个好的起点是为日期使用unix时间戳

大概是这样的:

SQRT(n*n + unix_timestamp(dt)*unix_timestamp(dt))
SQRT(a*n*n + b*unix_timestamp(dt)*unix_timestamp(dt))
然后,您可以使用计算出的范数作为比较值,而不是N

考虑到unix_时间戳仅适用于YYYY-MM-DD格式的日期

此外,还应该为n添加一个系数,为dt添加另一个系数,以使值正常化。如果其中任何一个比另一个大,那么你的标准值将趋向于最大分量的值,我相信你的时间戳将比你的大得多。所以你应该这样做:

SQRT(n*n + unix_timestamp(dt)*unix_timestamp(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))
和他们一起玩,直到你的成绩足够好

编辑:详细说明答案 从数学上讲,您面临的问题是:给定一组元组n,dt和一个特定的元组n',dt',其中n和n'是整数,dt和dt'是日期,返回距离n',dt'最短的M个元组的集合

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

要么你按照你的推理来阐述某种算法,从你的问题中选择我不知道的4个例子 或者在元组集合上定义一个数学距离。我将对此进行详细说明。 如果在笛卡尔平面中表示元组,您将看到以下内容:

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

现在,这个距离可以用几种方式定义。最常见的是欧几里德距离,由以下表达式定义:

d([n,dt],[n',dt'])= sqrt( (n-n')^2 + (dt-dt')^2 )
现在,您希望所有M个结果都能最小化该距离,让我们构建一个查询

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

DATEDIFF(dt, dt')
现在,DATEDIFF需要遵循YYYY-MM-DD格式的日期字段,但您的日期是反向的,因此我们需要对其进行格式化以供使用。在这里,我将计算您的固定值dt'将通过手工正确引入

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替换为N'值,DT替换为DT'值,M替换为所需的最近元组数,TABLE_name替换为表名

一些考虑

由于DATEDIFF返回以天为单位的差值,距离公式中dt'^2部分的值通常会比n-n'^2部分的值大得多。这意味着距离值将主要由距离值中具有更多决策权的日期组成。如果此结果不方便您,您可以只向组件添加权重,并使用这些值,直到获得足够好的结果。具有权重的查询如下所示:

选择*,SQRTA*POWt.n-n,2+B*POWDATEFIFSTR_to_datedate_format.dt,'%d-%m-%Y','%d-%m-%Y',dt,2作为与表名的距离,按距离ASC限制m排序

你需要用A和B代替你的体重。我建议值介于0和1之间,两者之和为1。即[A=0.9,B=0.1]。将更大的值指定给a将导致N对距离值的影响更大,这与B对DT的影响相同

这种距离不是唯一的。事实上,没有什么距离是独一无二的。例如,如果您只处理N的值,并且需要在示例表中找到离第二行更近的4行,您会发现第一行和第三行离它有1个单位的距离。但这不会影响你的问题,对吗

此距离无法预先计算,因此无法使用s 以一种有效的方式进行。如果表中有X个条目,则需要存储每行到其每个伙伴的距离。这意味着您需要为每一行添加X-1额外字段。无论如何,这都是一个糟糕的实现。如果您真的对此感兴趣,您可以找到一种方法,使另一个表具有每对记录的距离,并使用该表的联接执行此查询

此查询使用了大量本机函数和数学运算,因此它不是最快的查询。在我的本地环境中,执行平面选择所需的时间比执行平面选择所需的时间少一倍

距离还有其他定义,您可以研究并使用最适合您的问题的定义。但是这个查询背后的想法仍然是最小化距离,不管你如何定义它


我的数学不好,如果我错了,请纠正我。但这似乎不是唯一的?我可以有两个不同的n,dt对,给出相同的欧几里德范数?而且数据库非常大,这会非常耗时吗?没错,它不是唯一的。我不知道你的问题的本质,但如果你需要最接近的解决方案,在大多数情况下都不会产生影响。首先,我会尝试从数据库中获取准确的记录,如果至少有一个组件是唯一的,那么它将是唯一的,如果没有匹配,则使用最接近的4的标准。这肯定会增加你的次数。一个解决方法是预先计算可以在程序代码中执行的操作(如果有),并将其保存到每个记录的一列中。这会增加您的插入时间,但从mysql的角度来看,选择的问题会容易得多。关于唯一性,如果您只使用N,则可以有2条记录,距离目标值X个单位。因此,即使对于单个参数,它也不是唯一的:作为一个广义的例子。如果我想要n=5/dt=1,我希望调用返回n=6/dt=2作为最近的。如果调用返回n=2和dt=6,这将是一个问题。这似乎是一种使用预计算范数的可能性,或者我在某个地方错了吗?这种可能性存在于预计算和动态替代方案中。但这也可能发生在单参数示例中。如果你寻找n=3,但没有匹配,那么当你等待n=4时,你可以得到一个n=2,因为它们都离n=3有1个单位。不管怎样,我认为你不能预计算。距离是差异的标准值,您可以高效地存储所有这些值。让我停止工作,我会详细解释一下答案。