[英]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元組
也就是說,您需要定義您的距離。 您有兩種機會:
如果您在笛卡爾平面中表示元組,則會看到以下內容:
垂直邊緣代表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替換為表的名稱。
一些注意事項
當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相同。
這個距離不是唯一的。 實際上,沒有距離是唯一的。 例如,如果僅使用N的值。並且需要在示例表中找到更接近第二行的4行,則會發現第一行和第三行相距1個單位。 但這不會影響您的問題,對嗎?
無法有效計算並存儲此距離。 如果表中有X個條目,則需要存儲每一行到每個伙伴的距離。 這意味着您需要為每行增加X-1個額外的字段(無論如何,這都是不好的實現)。 如果您對此真的很感興趣,則可以找到一種方法,使另一張表具有每對記錄的距離,並使用對該表的聯接來執行此查詢。
該查詢使用許多本機函數和數學運算,因此它不是最快的查詢。 在我的本地環境中,執行平面SELECT所需的時間少於兩倍。
距離還有其他定義,您可以研究並使用最適合您的問題的距離。 但是,無論您如何定義該查詢,其背后的思想仍然是最小化距離。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.