MySQL限制功能的解决方法

时间:2013-12-17 13:05:53

标签: mysql

我的问题最好用一个简单的例子来解释:你按照身高排列学校的所有孩子。你想打破这条线,这样休息的一边就有十个女孩。你不知道会有多少男孩,因为这取决于他们的身高。

我的表格包含用户名的地理位置,每个用户名可以包含多个位置。我的下面的代码使用自联接查找地理上最接近输入用户名的用户名。通常这样的连接将包括WHERE input_username <> input_username,但我不这样做,因为我发现将input_username作为结果集的一部分很有用。但是,我想从输入用户名返回10个结果APART。

如果我理解正确你不能用sql限制子句这样做,因为不允许使用函数,但还有另一种方法吗?

SELECT
t1.`username`,
t2.`title`,
t2.`lat`,
t2.`long`,
DATE_FORMAT(greatest(t1.`from`, t2.`from`), '%d/%m/%Y') `begin`,
DATE_FORMAT(least(t1.`to`, t2.`to`), '%d/%m/%Y') `end`,     
CAST((format((POWER(POWER(((t1.`long` - t2.`long`)*1112), 2) + POWER(((t1.`lat` - t2.`lat`)*1112), 2), 0.5)), 2))*0.1 AS DECIMAL(5,3)) distance,
t2.`username`,
IF(t2.`username`=t1.`username`, 'inquirer', 'neighbour') `type`
FROM
entries t1,
entries t2
WHERE
t1.`username` = username AND greatest(t1.`from`, t2.`from`) < least(t1.`to`, t2.`to`)
ORDER BY distance
LIMIT 10;

好的,有没有办法按距离排序但是限制结果集,以便它包含许多需要包含10个'邻居''类型的?

目前,输出限制为10,其中一些将是“查询者”或输入用户名,有些将是“邻居”,或者是用户名,接近用户名进行查询。我想限制结果集,以便输入用户名以外有10个返回。

上面的代码返回如下:

username    title   lat long    begin   end distance    username    type
uv  "Title of entry 168"    51.595024   -0.128904   07/04/1919  07/04/1929  0.000   uv  inquirer
uv  "Title of entry 723"    51.610653   0.028761    26/09/1917  26/09/1927  0.000   uv  inquirer
uv  "Title of entry 444"    51.613369   0.034306    26/09/1917  21/10/1923  0.687   ee  neighbour
uv  "Title of entry 565"    51.588711   -0.129161   22/10/1919  07/04/1929  0.703   fh  neighbour
uv  "Title of entry 364"    51.601067   -0.121640   14/10/1923  07/04/1929  1.051   kg  neighbour
uv  "Title of entry 157"    51.620327   0.029985    12/12/1926  26/09/1927  1.084   ub  neighbour
uv  "Title of entry 1625"   51.608444   0.018629    30/08/1926  26/09/1927  1.153   wm  neighbour
uv  "Title of entry 554"    51.611248   -0.129845   07/04/1919  11/01/1928  1.807   gw  neighbour
uv  "Title of entry 1087"   51.599979   -0.113263   29/10/1924  07/04/1929  1.824   es  neighbour
uv  "Title of entry 1223"   51.620193   0.043415    26/09/1917  16/07/1922  1.944   bm  neighbour

fancypants提出的代码返回如下:

inusername  title   lat long    begin   end distance    outusername type    my_neighbour_counter
uv  "Title of entry 16" 51.649853   -0.012949   05/12/1925  26/09/1927  6.365   sh  neighbour   5
uv  "Title of entry 31" 51.569290   -0.206963   07/04/1919  02/07/1923  9.140   mj  neighbour   10
uv  "Title of entry 22" 51.532021   -0.223247   07/01/1925  07/04/1929  12.615  pn  neighbour   6
uv  "Title of entry 16" 51.649853   -0.012949   05/12/1925  07/04/1929  14.263  sh  neighbour   4
uv  "Title of entry 10" 51.473103   -0.203539   07/04/1919  21/05/1925  15.896  jx  neighbour   2
uv  "Title of entry 22" 51.532021   -0.223247   07/01/1925  26/09/1927  29.356  pn  neighbour   7
uv  "Title of entry 10" 51.473103   -0.203539   26/09/1917  21/05/1925  30.021  jx  neighbour   3
uv  "Title of entry 28" 51.354542   -0.259209   07/04/1919  31/08/1927  30.415  ui  neighbour   8
uv  "Title of entry 28" 51.354542   -0.259209   26/09/1917  31/08/1927  42.855  ui  neighbour   9

可以看出,这仍然限于10行。我想做的是下面的Strawerberry所做的,除了适用于我的代码。

最终工作的代码如下,然后是结果返回。它可能可以更有效地完成。我要感谢fancyPants和Strawerberry。

SET @x := 'st';
SET @rank=0;
SELECT * FROM (SELECT *, if(inqu = neig, @rank:=@rank, @rank:=@rank + 1) `rank`
FROM (SELECT t1.`username` inqu, t2.`title`, t2.`lat`, t2.`long`,
        DATE_FORMAT(greatest(t1.`from`, t2.`from`), '%d/%m/%Y') `begin`,
        DATE_FORMAT(least(t1.`to`, t2.`to`), '%d/%m/%Y') `end`,
        CAST((format((POWER(POWER(((t1.`long` - t2.`long`) * 1112), 2) +
POWER(((t1.`lat` - t2.`lat`) * 1112), 2), 0.5)), 2)) * 0.1
AS DECIMAL (5 , 3 )) distance,
AS DECIMAL (5 , 3 )) distance,
        t2.`username` neig,
        IF(t2.`username` = t1.`username`, 'inquirer', 'neighbour') `type`
FROM
    entries t1, entries t2
WHERE
    t1.`username` = @x
        AND greatest(t1.`from`, t2.`from`) < least(t1.`to`, t2.`to`)
ORDER BY distance) m) n WHERE `rank` <= 10;



inqu    title   lat long    begin   end distance    neig    type    rank
st  "Title of entry 482"    51.511539   -0.034709   20/11/1976  21/11/1986  0.000   st  inquirer    0
st  "Title of entry 144"    51.523846   -0.188672   17/04/1959  17/04/1969  0.000   st  inquirer    0
st  "Title of entry 1034"   51.504379   -0.007122   22/02/1901  23/02/1911  0.000   st  inquirer    0
st  "Title of entry 956"    51.388729   -0.149454   26/09/1954  25/09/1964  0.000   st  inquirer    0
st  "Title of entry 1432"   51.391411   -0.149341   26/09/1954  08/07/1960  0.298   vg  neighbour   1
st  "Title of entry 1074"   51.519535   -0.182533   17/04/1959  29/04/1965  0.834   sw  neighbour   2
st  "Title of entry 742"    51.526321   -0.180818   03/08/1967  17/04/1969  0.916   ig  neighbour   3
st  "Title of entry 863"    51.519028   -0.179766   17/04/1959  05/04/1966  1.126   ad  neighbour   4
st  "Title of entry 728"    51.520554   -0.179007   21/10/1960  17/04/1969  1.135   pu  neighbour   5
st  "Title of entry 597"    51.526016   -0.177974   09/03/1966  17/04/1969  1.214   xj  neighbour   6
st  "Title of entry 1527"   51.514561   -0.045765   09/07/1986  21/11/1986  1.275   bh  neighbour   7
st  "Title of entry 892"    51.497967   -0.016889   26/01/1911  23/02/1911  1.299   kv  neighbour   8
st  "Title of entry 1004"   51.527172   -0.177020   17/04/1959  24/04/1960  1.347   li  neighbour   9
st  "Title of entry 1325"   51.517700   -0.199251   17/04/1959  11/01/1966  1.360   nj  neighbour   10    

2 个答案:

答案 0 :(得分:0)

理解它就像你想让用户等按距离排序。有多少并不重要,但最多应该有10个邻居,对吗?

SELECT * FROM (
    SELECT
    t1.`username`,
    t2.`title`,
    t2.`lat`,
    t2.`long`,
    DATE_FORMAT(greatest(t1.`from`, t2.`from`), '%d/%m/%Y') `begin`,
    DATE_FORMAT(least(t1.`to`, t2.`to`), '%d/%m/%Y') `end`,     
    CAST((format((POWER(POWER(((t1.`long` - t2.`long`)*1112), 2) + POWER(((t1.`lat` - t2.`lat`)*1112), 2), 0.5)), 2))*0.1 AS DECIMAL(5,3)) distance,
    t2.`username`,
    IF(t2.`username`=t1.`username`, 'inquirer', 'neighbour') `type`,
    @neighbour_counter := IF(t2.`username`=t1.`username`, @neighbour_counter, @neighbour_counter + 1) AS my_neighbour_counter
    FROM
    entries t1,
    entries t2,
    (SELECT @neighbour_counter := 1) my_variables
    WHERE
    t1.`username` = username AND greatest(t1.`from`, t2.`from`) < least(t1.`to`, t2.`to`)
    ORDER BY distance
) sq WHERE my_neighbour_counter <= 10

编辑:修正错误,在查询者而不是邻居上增加变量...

答案 1 :(得分:0)

SELECT * FROM children;
+------------+--------+--------+
| name       | height | gender |
+------------+--------+--------+
| Adam       |    126 | M      |
| Alice      |    103 | F      |
| Ben        |    106 | M      |
| Bernadette |    133 | F      |
| Charlie    |    101 | M      |
| Claire     |    102 | F      |
| Dan        |    139 | M      |
| Donna      |     96 | F      |
| Edward     |    141 | M      |
| Erica      |    119 | F      |
| Fiona      |    133 | F      |
| Frank      |    139 | M      |
| George     |    122 | M      |
| Georgina   |    134 | F      |
| Harriet    |    135 | F      |
| Henry      |    141 | M      |
| Inigo      |    139 | M      |
| Irene      |     96 | F      |
| Jessica    |    103 | F      |
| John       |    122 | M      |
| Kenneth    |    144 | M      |
| Kimberley  |    132 | F      |
| Lee        |    103 | M      |
| Lorraine   |    134 | F      |
| Michael    |    134 | M      |
| Michelle   |    102 | F      |
+------------+--------+--------+

SET @rank=0;

SELECT name
     , height
     , gender
  FROM 
     ( SELECT *
            , CASE WHEN gender = 'f' THEN @rank:=@rank+1 ELSE @rank:=@rank END rank 
         FROM children 
        ORDER 
           BY height
     ) m
 WHERE rank <= 10;
+------------+--------+--------+
| name       | height | gender |
+------------+--------+--------+
| Donna      |     96 | F      |
| Irene      |     96 | F      |
| Charlie    |    101 | M      |
| Michelle   |    102 | F      |
| Claire     |    102 | F      |
| Lee        |    103 | M      |
| Alice      |    103 | F      |
| Jessica    |    103 | F      |
| Ben        |    106 | M      |
| Erica      |    119 | F      |
| John       |    122 | M      |
| George     |    122 | M      |
| Adam       |    126 | M      |
| Kimberley  |    132 | F      |
| Fiona      |    133 | F      |
| Bernadette |    133 | F      |
+------------+--------+--------+