SQL: изучение того, как использовать функции процентиля в SQL

Я работаю с Netezza SQL.

У меня есть следующая таблица:

CREATE TABLE MY_TABLE (
    country VARCHAR(50),
    gender CHAR(1),
    age INTEGER,
    height FLOAT,
    owns_bicycle VARCHAR(3)
);

INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'M', 25, 175.99, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 63, 163.65, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 83, 166.01, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 94, 178.92, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'M', 63, 173.24, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 87, 156.27, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 89, 159.26, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 61, 179.14, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 49, 167.55, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 53, 172.82, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 62, 161.18, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 31, 173.08, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 33, 166.13, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 50, 177.42, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 57, 155.56, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'M', 57, 158.47, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 27, 152.85, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 89, 156.31, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 99, 179.31, 'No');

Моя проблема:

  • Во-первых, я хочу разбить высоту на 3 группы одинакового размера по значению их высоты (например, 0%-33%, 33%-66%, 66%-99%).
  • Затем я хочу разбить возраст на 5 групп одинакового размера по значению их возраста (например, 0%-20%, 20%-40% и т. д.)
  • Затем для каждой уникальной комбинации страны, пола, возрастной_группы и ростовой_группы я хочу узнать процент владельцев велосипедов.

Окончательный результат должен выглядеть примерно так (т. е. каждый человек должен быть назначен только одному конечному подмножеству):

  country gender height_group    age_group   bicycle_proportion counts
  <chr>   <chr>  <fct>           <fct>                    <dbl>  <int>
1 Canada  F      150.84 - 158.49 18 - 31                  0          2
2 Canada  F      150.84 - 158.49 31 - 45.2                0.333      3
3 Canada  F      150.84 - 158.49 62.4 - 78.4              0          2
4 Canada  F      150.84 - 158.49 78.4 - 99                0          1
5 Canada  F      158.49 - 169.33 18 - 31                  0          1
6 Canada  F      158.49 - 169.33 31 - 45.2                1          1

Я знаю, как это сделать с помощью языка программирования R:

library(dplyr)



set.seed(123)
n <- 100
country <- sample(c("USA", "Canada", "UK"), n, replace = TRUE)
gender <- sample(c("M", "F"), n, replace = TRUE)
age <- sample(18:100, n, replace = TRUE)
height <- runif (n, min = 150, max = 180)
owns_bicycle <- sample(c("Yes", "No"), n, replace = TRUE)

df <- data.frame(country, gender, age, height, owns_bicycle)


height_breaks <- quantile(df$height, probs = seq(0, 1, by = 1/3))
age_breaks <- quantile(df$age, probs = seq(0, 1, by = 1/5))

height_breaks <- round(height_breaks, 2)

height_labels <- paste0(height_breaks[-length(height_breaks)], " - ", height_breaks[-1])
age_labels <- paste0(age_breaks[-length(age_breaks)], " - ", age_breaks[-1])


df$height_group <- cut(df$height, breaks = height_breaks, labels = height_labels, include.lowest = TRUE)
df$age_group <- cut(df$age, breaks = age_breaks, labels = age_labels, include.lowest = TRUE)

final = df %>%
  group_by(country, gender, height_group, age_group) %>%
  summarise(bicycle_proportion = mean(owns_bicycle == "Yes"),
            counts = n())

Теперь я пытаюсь преобразовать это в Netezza SQL.

Я не уверен, как это сделать:

CREATE TABLE height_groups AS
  SELECT
    NTILE(3) OVER (ORDER BY height) AS height_group,
    MIN(height) AS min_height,
    MAX(height) AS max_height
  FROM MY_TABLE;

CREATE TABLE age_groups AS
  SELECT
    NTILE(5) OVER (ORDER BY age) AS age_group,
    MIN(age) AS min_age,
    MAX(age) AS max_age
  FROM MY_TABLE;

Но я не думаю, что делаю это правильно. Может кто-нибудь показать мне, как это сделать?

Спасибо!

Вы пробовали функции percentile_cont или percentile_disc? Вот ссылка на документацию PERCENTILE_CONT возвращает значение, соответствующее указанному процентилю с учетом спецификации сортировки с использованием модели непрерывного распределения. PERCENTILE_DISC возвращает значение, соответствующее указанному процентилю с учетом спецификации сортировки с использованием модели дискретного распределения.

Rajshekar Iyer 09.06.2023 05:40

@ Rajshekar Iyer: Большое спасибо за ответ! Эта ссылка выглядит очень полезной! Если у вас есть время, не могли бы вы показать мне, как я могу использовать эти функции для достижения желаемого результата? Спасибо!

stats_noob 09.06.2023 06:13
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
2
108
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вот пример использования некоторых функций для получения эквивалента вашего кода R. Он использует percentile_cont для получения двух граничных значений между квантилями, которые затем можно использовать в выражениях case для получения меток роста или возраста. «Перекрестное соединение» просто добавляет эти значения в каждую строку таблицы для простоты использования в выражениях case. (Это показано далее в упомянутой скрипте ниже.)

SELECT
      t.country
    , t.gender
    , CASE WHEN t.height <= height_quantiles.q1 THEN 'short' 
           WHEN t.height <= height_quantiles.q2 THEN 'medium' 
           ELSE 'tall' 
      END AS height_group
    , CASE WHEN t.age <= age_quantiles.q1 THEN 'young' 
           WHEN t.age <= age_quantiles.q2 THEN 'middle_age' 
           ELSE 'old' 
      END AS age_group
    , AVG(CASE WHEN t.owns_bicycle = 'Yes' THEN 1 ELSE 0 END) AS bicycle_proportion
    , COUNT(*) AS counts
FROM MY_TABLE t
CROSS JOIN (
    SELECT
          PERCENTILE_CONT(0.33) WITHIN GROUP (ORDER BY height) AS q1
        , PERCENTILE_CONT(0.67) WITHIN GROUP (ORDER BY height) AS q2
    FROM MY_TABLE t
    ) height_quantiles
CROSS JOIN (
    SELECT
          PERCENTILE_CONT(0.2) WITHIN GROUP (ORDER BY age) AS q1
        , PERCENTILE_CONT(0.4) WITHIN GROUP (ORDER BY age) AS q2
    FROM MY_TABLE t
    ) age_quantiles
GROUP BY
      t.country
    , t.gender
    , height_group
    , age_group
ORDER BY
      t.country
    , t.gender
    , height_group
    , age_group
страна пол высота_группа возрастная группа велосипед_пропорция считает Канада Ф середина старый 0,000000000000000000000 1 Канада Ф середина молодой 0,000000000000000000000 1 Канада Ф короткий старый 0,000000000000000000000 1 Канада Ф высокий средние века 0,000000000000000000000 1 Канада М короткий старый 1.000000000000000000000 1 Канада М высокий старый 1.000000000000000000000 2 Великобритания Ф середина средние века 0,000000000000000000000 1 Великобритания Ф середина старый 0,500000000000000000000 2 Великобритания Ф короткий средние века 0,000000000000000000000 1 Великобритания Ф короткий старый 1.000000000000000000000 1 Великобритания Ф короткий молодой 0,000000000000000000000 1 Великобритания М высокий старый 1.000000000000000000000 1 Великобритания М высокий молодой 0,000000000000000000000 1 США Ф середина средние века 0,000000000000000000000 1 США Ф середина молодой 1.000000000000000000000 1 США Ф высокий старый 0,000000000000000000000 1 США М короткий средние века 1.000000000000000000000 1

рабочий пример

Re: "адаптируемый" вариант. SQL не является языком программирования, он имеет дело с наборами данных, поэтому имитация кода R может быть возможна, если вы используете «процедурное расширение» для SQL (например, plsql в Postgres), но поскольку я не могу ничего запустить в Netazza лучшее, что я могу предложить, это что-то в этом роде.

with CTE as (
    SELECT
      *
     , NTILE(4) OVER (ORDER BY height) AS height_quantile
     , NTILE(5) OVER (ORDER BY age) AS age_quantile
    FROM my_table
 )
, height_labels as (
  select
       height_quantile hq
     , concat(floor(min(round(height::decimal,2))) , ' to '
            , max(ceiling(round(height::decimal,2)) ) ) height_label
  from CTE
 group by
       height_quantile
  )
, age_labels as (
  select
       age_quantile aq
     , concat(min(age ) , ' to '
            , max(age ) ) age_label
  from CTE
 group by
       age_quantile
  )
select
  *
from CTE 
inner join height_labels h on cte.height_quantile=h.hq
inner join age_labels    a on cte.age_quantile=a.aq
order by
    height, age
страна пол возраст высота owns_bicycle высота_квантиль age_quantile штаб-квартира метка_высоты вода age_label Великобритания Ф 27 152,85 Нет 1 1 1 от 152 до 159 1 от 25 до 33 Великобритания Ф 57 155,56 Нет 1 3 1 от 152 до 159 3 от 57 до 63 Канада М 87 156,27 Да 1 4 1 от 152 до 159 4 от 63 до 89 Великобритания Ф 89 156,31 Да 1 5 1 от 152 до 159 5 от 89 до 99 США М 57 158,47 Да 1 2 1 от 152 до 159 2 от 49 до 57 Канада Ф 89 159,26 Нет 2 4 2 от 159 до 167 4 от 63 до 89 Канада Ф 62 161,18 Нет 2 3 2 от 159 до 167 3 от 57 до 63 Великобритания Ф 63 163,65 Нет 2 3 2 от 159 до 167 3 от 57 до 63 Великобритания Ф 83 166.01 Да 2 4 2 от 159 до 167 4 от 63 до 89 США Ф 33 166,13 Да 2 1 2 от 159 до 167 1 от 25 до 33 Великобритания Ф 49 167,55 Нет 3 2 3 от 167 до 176 2 от 49 до 57 США Ф 53 172,82 Нет 3 2 3 от 167 до 176 2 от 49 до 57 Канада Ф 31 173,08 Нет 3 1 3 от 167 до 176 1 от 25 до 33 Великобритания М 63 173,24 Да 3 4 3 от 167 до 176 4 от 63 до 89 Великобритания М 25 175,99 Нет 3 1 3 от 167 до 176 1 от 25 до 33 Канада Ф 50 177,42 Нет 4 2 4 от 177 до 180 2 от 49 до 57 Канада М 94 178,92 Да 4 5 4 от 177 до 180 5 от 89 до 99 Канада М 61 179,14 Да 4 3 4 от 177 до 180 3 от 57 до 63 США Ф 99 179,31 Нет 4 5 4 от 177 до 180 5 от 89 до 99

Третья рабочий пример

В этом подходе метки генерируются из пар минимум/максимум каждой NTILE, а затем из тех, которые используются в окончательном выводе. Для упрощения создания этикетки я ввел функции пол и потолок.

@ Пол Максвелл: Спасибо за ответ! Можно ли сделать это более «общим ответом», например. вместо того, чтобы вручную писать старые, молодые и т. д., если бы я хотел создать 10 групп по возрасту, я мог бы просто написать group_1, group_2 и т. д.? большое спасибо!

stats_noob 09.06.2023 08:36

@ Пол Максвелл: Спасибо за ответ! Что я имел в виду - есть ли способ избежать оператора CASE WHEN в вашем коде? Если бы я мог просто сделать это как age_group = 1,2,3,4 ... а затем иметь легенду/таблицу поиска, чтобы узнать минимальный/максимальный диапазон для каждой возрастной группы, height_group?

stats_noob 09.06.2023 08:43

В отличие от того, что вы сделали в R, вы можете конкатенировать строки, но вам нужны элементы для конкатенации в запросе. Или вы можете построить CTE или таблицу этих меток. Возможностей слишком много, чтобы описать их все в комментарии.

Paul Maxwell 09.06.2023 08:45

Вы можете создавать таблицы граничных значений (как я сделал в своем примере), и они могут содержать метки, а затем те, которые присоединяются к исходным данным, возможно, используя «между» в качестве условия соединения, но будьте осторожны, чтобы не умножить нумеровать исходные строки путем присоединения к многострочным таблицам, т. е. исходная строка должна присоединяться только к строке граничной таблицы.

Paul Maxwell 09.06.2023 08:51

Кстати: «CASE WHEN» не является «утверждением», это «выражения case», поскольку они оцениваются как одно значение (т.е. «выражение»)

Paul Maxwell 09.06.2023 08:51

Я добавил (неполный) второй вариант, который позволяет легко изменить количество квантилей (используя NTILE (n), просто измените n), а затем создайте метки. Точность математики, используемой в ярлыках, я оставляю вам в качестве упражнения.

Paul Maxwell 10.06.2023 06:16

@ Пол Максвелл: Большое спасибо за ваши обновления! Я только что опубликовал новый ответ («второй») на свой вопрос (см. Ниже) — не могли бы вы взглянуть на него? Спасибо!

stats_noob 15.06.2023 01:49

Вот простой пример

CREATE TABLE students (
  id INT,
  name VARCHAR(255),
  marks INT
);

INSERT INTO students VALUES
(1, 'John', 90),
(2, 'Jane', 80),
(3, 'Mike', 70),
(4, 'Peter', 60);

SELECT
  id,
  name,
  marks,
  PERCENTILE_CONT(marks, 0.25) AS 25th_percentile,
  PERCENTILE_CONT(marks, 0.50) AS 50th_percentile,
  PERCENTILE_CONT(marks, 0.75) AS 75th_percentile
FROM students;

@ Rajshekar Iyer: Большое спасибо за ответ! Я опубликовал новый ответ на свой вопрос (см. Ниже) - не могли бы вы взглянуть на него? Спасибо!

stats_noob 15.06.2023 01:48

ОП здесь - вот моя собственная попытка решить мою проблему.

Я думал, что могу сделать это в 3 шага:

  • Шаг 1: Сначала рассчитайте необходимые категории процентилей (например, 3 группы, 5 групп) на основе роста и возраста.
  • Шаг 2: Затем замените значения роста и возраста в исходной таблице этими категориями.
  • Шаг 3: Наконец, создайте легенду/справочную таблицу, содержащую минимальные/максимальные значения для каждой категории.

Я не уверен, что это правильно (хотя это выглядит правильно?).

Может ли кто-нибудь высказать свое мнение по этому поводу?

CREATE TABLE MY_TABLE (
    country VARCHAR(50),
    gender CHAR(1),
    age INTEGER,
    height FLOAT,
    owns_bicycle VARCHAR(3)
);

INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'M', 25, 175.99, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 63, 163.65, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 83, 166.01, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 94, 178.92, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'M', 63, 173.24, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 87, 156.27, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 89, 159.26, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 61, 179.14, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 49, 167.55, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 53, 172.82, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 62, 161.18, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 31, 173.08, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 33, 166.13, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 50, 177.42, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 57, 155.56, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'M', 57, 158.47, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 27, 152.85, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 89, 156.31, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 99, 179.31, 'No');

CREATE TABLE height_groups AS
SELECT
  country,
  gender,
  NTILE(3) OVER (ORDER BY height) AS height_group,
  MIN(height) AS min_height,
  MAX(height) AS max_height
FROM MY_TABLE
GROUP BY country, gender, height;

CREATE TABLE age_groups AS
SELECT
  country,
  gender,
  NTILE(5) OVER (ORDER BY age) AS age_group,
  MIN(age) AS min_age,
  MAX(age) AS max_age
FROM MY_TABLE
GROUP BY country, gender, age;

CREATE TABLE final AS
SELECT
  h.country,
  h.gender,
  h.height_group,
  a.age_group,
  AVG(CASE WHEN t.owns_bicycle = 'Yes' THEN 1 ELSE 0 END) AS bicycle_proportion,
  COUNT(*) AS counts
FROM height_groups h
JOIN age_groups a ON h.country = a.country AND h.gender = a.gender
JOIN MY_TABLE t ON h.country = t.country AND h.gender = t.gender AND t.height BETWEEN h.min_height AND h.max_height AND t.age BETWEEN a.min_age AND a.max_age
GROUP BY h.country, h.gender, h.height_group, a.age_group;


CREATE TABLE lookup AS
SELECT
  h.country,
  h.gender,
  h.height_group,
  a.age_group,
  h.min_height,
  h.max_height,
  a.min_age,
  a.max_age
FROM height_groups h
JOIN age_groups a ON h.country = a.country AND h.gender = a.gender;

Правильно ли я сделал?

Снова OP - я понял, что возрастные группы и группы роста не совпадают в моем предыдущем ответе (например, иногда age_group = 1 будет иметь разный минимальный/максимальный рост).

Вот моя вторая попытка - более ручная попытка (на этот раз для 5 возрастных групп и 5 ростовых групп):

CREATE TABLE MY_TABLE (
    country VARCHAR(50),
    gender CHAR(1),
    age INTEGER,
    height FLOAT,
    owns_bicycle VARCHAR(3)
);

INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'M', 25, 175.99, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 63, 163.65, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 83, 166.01, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 94, 178.92, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'M', 63, 173.24, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 87, 156.27, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 89, 159.26, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'M', 61, 179.14, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 49, 167.55, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 53, 172.82, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 62, 161.18, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 31, 173.08, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 33, 166.13, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('Canada', 'F', 50, 177.42, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 57, 155.56, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'M', 57, 158.47, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 27, 152.85, 'No');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('UK', 'F', 89, 156.31, 'Yes');
INSERT INTO MY_TABLE (country, gender, age, height, owns_bicycle)
VALUES ('USA', 'F', 99, 179.31, 'No');

CREATE TABLE age_groups AS
SELECT country, gender, age, height, owns_bicycle,
NTILE(5) OVER (ORDER BY age) AS age_group
FROM MY_TABLE;

CREATE TABLE height_groups AS
SELECT country, gender, age, height, owns_bicycle,
NTILE(5) OVER (ORDER BY height) AS height_group
FROM MY_TABLE;

CREATE TABLE age_height_group_replacements AS
SELECT
    a.country,
    a.gender,
    a.age,
    a.height,
    a.owns_bicycle,
    CASE
        WHEN a.age BETWEEN (SELECT MIN(age) FROM age_groups WHERE age_group = 1) AND (SELECT MAX(age) FROM age_groups WHERE age_group = 1) THEN 'Group 1'
        WHEN a.age BETWEEN (SELECT MIN(age) FROM age_groups WHERE age_group = 2) AND (SELECT MAX(age) FROM age_groups WHERE age_group = 2) THEN 'Group 2'
        WHEN a.age BETWEEN (SELECT MIN(age) FROM age_groups WHERE age_group = 3) AND (SELECT MAX(age) FROM age_groups WHERE age_group = 3) THEN 'Group 3'
        WHEN a.age BETWEEN (SELECT MIN(age) FROM age_groups WHERE age_group = 4) AND (SELECT MAX(age) FROM age_groups WHERE age_group = 4) THEN 'Group 4'
        ELSE 'Group 5'
    END AS age_group_replacement,
    CASE
        WHEN a.height BETWEEN (SELECT MIN(height) FROM height_groups WHERE height_group = 1) AND (SELECT MAX(height) FROM height_groups WHERE height_group = 1) THEN 'Group 1'
        WHEN a.height BETWEEN (SELECT MIN(height) FROM height_groups WHERE height_group = 2) AND (SELECT MAX(height) FROM height_groups WHERE height_group = 2) THEN 'Group 2'
        WHEN a.height BETWEEN (SELECT MIN(height) FROM height_groups WHERE height_group = 3) AND (SELECT MAX(height) FROM height_groups WHERE height_group = 3) THEN 'Group 3'
        WHEN a.height BETWEEN (SELECT MIN(height) FROM height_groups WHERE height_group = 4) AND (SELECT MAX(height) FROM height_groups WHERE height_group = 4) THEN 'Group 4'
        ELSE 'Group 5'
    END AS height_group_replacement,
    (SELECT MIN(age) FROM age_groups WHERE age_group = (SELECT age_group FROM age_groups WHERE age = a.age)) AS min_age,
    (SELECT MAX(age) FROM age_groups WHERE age_group = (SELECT age_group FROM age_groups WHERE age = a.age)) AS max_age,
    (SELECT MIN(height) FROM height_groups WHERE height_group = (SELECT height_group FROM height_groups WHERE height = a.height)) AS min_height,
    (SELECT MAX(height) FROM height_groups WHERE height_group = (SELECT height_group FROM height_groups WHERE height = a.height)) AS max_height
FROM
    MY_TABLE a;

 CREATE TABLE BIKE_OWNERSHIP AS SELECT
    min_age,
    max_age,
    min_height,
    max_height,
    country,
    gender,
    COUNT(CASE WHEN owns_bicycle = 'Yes' THEN 1 END) * 100.0 / COUNT(*) AS percentage_owns_bicycle
FROM
    age_height_group_replacements
GROUP BY
    min_age,
    max_age,
    min_height,
    max_height,
    country,
    gender;

У кого-нибудь есть комментарии по этому поводу?

Спасибо!

Примечание: вы можете попробовать выполнить этот код SQL здесь: https://sqliteonline.com/

Я думаю, вы упустили суть NTILE. Пересмотрите dbfiddle.uk/lPtQcL1M Вы можете использовать обе NTILE одновременно непосредственно в исходной таблице, и значения, заданные этой функцией, могут затем использоваться в случае выражения БЕЗ необходимости использовать (неудобно/неэффективно) «коррелированные подзапросы», чтобы выяснить пары минимального/максимального значения. Вам действительно нужно только вычислить метки, что можно сделать с помощью «общих табличных выражений» (cte), чтобы избежать создания постоянных таблиц.

Paul Maxwell 15.06.2023 05:09

Я обновил часть своего ответа, основанную на NTILE, чтобы метки были немного чище (с использованием функций пола/потолка). Надеюсь, вы видите, что это не только более гибко (поскольку вам не нужно жестко кодировать метки), но и более эффективно, чем многие «коррелированные подзапросы»

Paul Maxwell 15.06.2023 05:25

@ Пол Максвелл: Большое спасибо за ответ! Я думаю, что смог преобразовать свой SQL в CTE (Common Table Expression). Как вы думаете, я должен опубликовать новый вопрос и предоставить вам ссылку на мою попытку написать CTE?

stats_noob 15.06.2023 05:32

Ненормально предлагать ответы и запрашивать отзывы, как если бы это был дискуссионный форум. Обычно вы ждете ответа и выбираете лучший. Это особенно важно, поскольку вы также добавили баллы за вознаграждение, чтобы привлечь больше ответов.

Paul Maxwell 15.06.2023 06:25

Другие вопросы по теме