将子查询放入offset子句

时间:2019-06-30 15:33:18

标签: mysql

假设我有一个表(数字)只有一列:

 n
---
 4
 5
67
23
 7
89

,我想获取中位数(仅数字列表)。我以为这很容易,所以我写道:

SELECT SUM(ord.n)/2
FROM (
      SELECT n 
      FROM numbers 
      ORDER BY n ASC
      LIMIT 2 OFFSET (SELECT COUNT(n)/2-1 FROM numbers)
     ) AS ord

但是当然会抛出语法错误。我想我不能将子查询插入offset中,但我想知道要做什么才能得到预期的结果?我知道写查询以获取中位数的方法有很多,但我需要知道是否有可能将“变量”插入偏移量而不是放置一些数字?

3 个答案:

答案 0 :(得分:1)

我认为您正在寻找下面的查询,该查询应该适用于MySQL 5.1及更高版本

SELECT 
 AVG(filter.n)
FROM (

SELECT 
   *
 , (@position := @position + 1) AS init_position  
FROM 
 t
CROSS JOIN (
   SELECT
      @position := 0
    , @max := (SELECT COUNT(t.n) FROM t)
    , @median_mode := (CASE WHEN ((@max % 2) = 0) THEN 'even' ELSE 'odd' END)
 ) AS init_user_param
ORDER BY 
 t.n ASC

) AS filter
WHERE 
 CASE 
  WHEN @median_mode = 'even' 
  THEN filter.init_position BETWEEN (@max / 2) AND ((@max / 2) + 1)

  WHEN @median_mode = 'odd' 
  THEN filter.init_position = ((@max + 1) / 2)
 END

结果

| AVG(filter.n) |
| ------------- |
| 15            |

请参阅demo

当89不在列表中时的结果。

| AVG(filter.n) |
| ------------- |
| 7             |

请参阅demo

答案 1 :(得分:0)

您可以使用ROW_NUMBER()窗口功能:

try {
  const fetched = await message.channel.fetchMessages({ limit: 100 });
  const notPinned = fetched.filter(fetchedMsg => !fetchedMsg.pinned);

  await message.channel.bulkDelete(notPinned, true);
} catch(err) {
  console.error(err);
}

请参见demo
结果:

WITH cte AS (SELECT COUNT(*) counter FROM numbers)
SELECT AVG(n) median
FROM (
  SELECT 
    row_number() over (order by n) ordinal,
    n
  FROM numbers
) t
WHERE (SELECT counter FROM cte) IN (2 * ordinal, 2 * (ordinal - 1))

答案 2 :(得分:0)

(可能)与算法最相似的解决方案是使用两个查询。首先获取偏移量。然后将其插入查询中。仅限SQL的方式是使用准备好的语句:

set @offset = (SELECT COUNT(n)/2-1 FROM numbers);

set @sql = "
  SELECT SUM(ord.n)/2
  FROM (
        SELECT n 
        FROM numbers 
        ORDER BY n ASC
        LIMIT 2 OFFSET ?
       ) AS ord
";

prepare stmt from @sql;
execute stmt using @offset;

db-fiddle