Как получить значения, которые> = 25%, 50%, 75% списка из столбца, используя SQL - PullRequest
1 голос
/ 12 января 2020

В моей таблице есть один столбец с именем Speed (целое число), и мне нужно выбрать значения, которые больше 25%, 50%, ... значений в этом списке.

Пример данных:

+-------+
| Speed |
+-------+
|     1 |
|     2 |
|     3 |
|     4 |
|     5 |
|     6 |
|     7 |
|     8 |
|     9 |
|    10 |
+-------+

Желаемый вывод:

+--------+
| OUTPUT |
+--------+
|      3 |
|      5 |
|      8 |
+--------+

Объяснение:

  • 3> = 25% чисел в списке
  • 5> = 50% чисел в списке
  • 8> = 75% чисел в списке

Я думаю, что я должен отсортировать данные и сделать что-то вроде:

SELECT speed 
FROM my_table
WHERE speed IN (ROUND(0.25 * <total_row>), ROUND(0.50 * <total_row>),..) 

но я не знаю, как получить эту ссылку <total_row>. Если бы я мог просто SELECT COUNT(speed) AS total_row и использовать это позже, это было бы здорово.

Большое вам спасибо.

Ответы [ 4 ]

3 голосов
/ 12 января 2020
create table Speed Engine=Memory 
as select number+1 X from numbers(10);

SELECT quantilesExact(0.25, 0.5, 0.75)(X)
FROM Speed

┌─quantilesExact(0.25, 0.5, 0.75)(X)─┐
│ [3,6,8]                            │
└────────────────────────────────────┘


SELECT arrayJoin(quantilesExact(0.25, 0.5, 0.75)(X)) AS q
FROM Speed

┌─q─┐
│ 3 │
│ 6 │
│ 8 │
└───┘

SELECT arrayJoin(quantilesExact(0.25, 0.499999999999, 0.75)(X)) AS q
FROM Speed

┌─q─┐
│ 3 │
│ 5 │
│ 8 │
└───┘

В CH realm Join не применяется, потому что обычно это миллиарды строк.

create table Speed Engine=MergeTree order by X  as select number X from numbers(1000000000);

SELECT quantilesExact(0.25, 0.5, 0.75)(X)
FROM Speed

┌─quantilesExact(0.25, 0.5, 0.75)(X)─┐
│ [250000000,500000000,750000000]    │
└────────────────────────────────────┘

1 rows in set. Elapsed: 7.974 sec. Processed 1.00 billion rows,

SELECT quantiles(0.25, 0.5, 0.75)(X)
FROM Speed

┌─quantiles(0.25, 0.5, 0.75)(X)────────┐
│ [244782599,500713390.5,751014086.75] │
└──────────────────────────────────────┘

1 rows in set. Elapsed: 1.274 sec. Processed 1.00 billion rows
1 голос
/ 12 января 2020

Это немного длинно для комментария.

По сути, для ответа на этот вопрос в SQL есть три подхода:

  1. Оконные функции.
  2. Коррелированные подзапросы для расчета кумулятивных значений.
  3. Самосоединение с неравными условиями и агрегирование для расчета кумулятивных значений.

Первый вариант - ПОБЫТОВЫЙ лучший подход. Но два других можно использовать в базах данных, которые не поддерживают оконные функции.

Увы, Clickhouse не поддерживает:

  • Оконные функции.
  • Коррелированные подзапросы .
  • Non-equijoins.

Может иметь недокументированные функции или расширения, которые поддерживают одну или несколько из этих функций. Однако базовый продукт, по-видимому, не поддерживает достаточно SQL, чтобы сделать это как один запрос.

РЕДАКТИРОВАТЬ:

Казалось бы, есть способ, предполагая, что rowNumberInAllBlocks() подчиняется заказ, указанный в order by:

select t.*
from (select t.*,
             rowNumberInAllBlocks() as seqnum,
             tt.cnt
      from t cross join
           (select count(*) as cnt from t) tt
      order by speed
     ) t
where (t.seqnum <= tt.cnt * 0.25 and t.seqnum + 1 > tt.cnt * 0.25) or
      (t.seqnum <= tt.cnt * 0.50 and t.seqnum + 1 > tt.cnt * 0.50) or
      (t.seqnum <= tt.cnt * 0.75 and t.seqnum + 1 > tt.cnt * 0.75) ;
0 голосов
/ 12 января 2020

Сначала вы выполняете самостоятельное объединение, поэтому каждая строка соединяется со всеми строками, где Speed меньше или равно Speed. Затем перекрестное соединение с запросом, который возвращает общее количество строк таблицы. Наконец, сгруппируйте по проценту строк, каждый из которых Speed больше, округлите до целых значений 25, 50 и 75 и получите минимальное значение Speed для каждой группы:

select min(t.speed) Output 
from (select count(*) total from tablename) c
cross join (
  select t.speed, count(*) counter
  from tablename t inner join tablename tt
  on tt.speed <= t.speed
  group by t.speed
) t 
where 25 * floor(floor(100.0 * t.counter / c.total) / 25) in (25, 50, 75)
group by 25 * floor(floor(100.0 * t.counter / c.total) / 25)

Этот код протестировано и работает для MySql, Postgresql и SQL Server. Смотрите демо . Результаты:

| output |
| ------ |
| 3      |
| 5      |
| 8      |
0 голосов
/ 12 января 2020

Извините за неэффективное, но работающее решение, попробуйте это:

  1. объявите переменную для максимального значения:

    declare @maxspeed int = (select max(speed) from my_table)
    
  2. выберите Для my_table соответствующие значения:

    select speed 
    from my_table
    where speed in ((select top 1 speed 
                     from @my_table
                     where speed > 0.25 * @maxspeed),
                    (select top 1 speed 
                     from @my_table
                     where speed > 0.5 * @maxspeed),
                    (select top 1 speed 
                     from @my_table
                     where speed > 0.75 * @maxspeed))
    
...