簡體   English   中英

MySQL基於2列獲取最接近的值

[英]mysql getting closest value based on 2 columns

給定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)

但是在兩個變量上都很難做到這一點。 我嘗試做兩次以上,但基本上你得到錯誤的行,因為只有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))

那么您可以使用計算出的范數作為您的值進行比較,而不是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',dt'),其中n和n'是整數,而dt和dt'是日期,則返回該集合與(n',dt')距離最短的S個M元組

也就是說,您需要定義您的距離。 您有兩種機會:

  • 根據您從問題中選擇4個示例的理由(我不知道),您可以闡述某種算法
  • 或者,您可以在元組集中定義數學距離。 我將對此進行詳細說明。

如果您在笛卡爾平面中表示元組,則會看到以下內容:

笛卡爾平面

垂直邊緣代表n,水平邊緣代表dt。 藍色箭頭表示從一個特定的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'。

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替換為表的名稱。

一些注意事項

  1. 當DATEDIFF返回以天為單位的差時,距離公式的(dt-dt')^2部分的值通常將大大大於(n-n')^2 這意味着距離值將主要由日期組成(距離值有更多決定權)。 如果此結果不能滿足您的要求,則可以將權重添加到組件中並使用這些值,直到獲得足夠適合您的結果。 具有權重的查詢如下:

    SELECT *, SQRT(A*POW((tn-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;

您需要用A和B代替您的體重。 我建議值在0到1之間,兩者之和為1。即[A = 0.9,B = 0.1]。 為A分配更大的值將導致N對距離值的影響更大,對於DT而言,B與B相同。

  1. 這個距離不是唯一的。 實際上,沒有距離是唯一的。 例如,如果僅使用N的值。並且需要在示例表中找到更接近第二行的4行,則會發現第一行和第三行相距1個單位。 但這不會影響您的問題,對嗎?

  2. 無法有效計算並存儲此距離。 如果表中有X個條目,則需要存儲每一行​​到每個伙伴的距離。 這意味着您需要為每行增加X-1個額外的字段(無論如何,這都是不好的實現)。 如果您對此真的很感興趣,則可以找到一種方法,使另一張表具有每對記錄的距離,並使用對該表的聯接來執行此查詢。

  3. 該查詢使用許多本機函數和數學運算,因此它不是最快的查詢。 在我的本地環境中,執行平面SELECT所需的時間少於兩倍。

  4. 距離還有其他定義,您可以研究並使用最適合您的問題的距離。 但是,無論您如何定義該查詢,其背后的思想仍然是最小化距離。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM