#mysql
#mysql
Вопрос:
У меня есть таблица фамилий, и я хочу подсчитать количество фамилий в каждом алфавитном диапазоне A-D или E-H и т.д.
Я придумал следующий запрос, который работает, и я хотел бы услышать мнения людей об этом и, возможно, способы сделать это лучше.
select count(*) FROM people
group by surname REGEXP '^[a-d].*',
surname REGEXP '^[e-h].*',
surname REGEXP '^[i-l].*',
surname REGEXP '^[m-p].*',
surname REGEXP '^[q-t].*',
surname REGEXP '^[u-z].*';
Ответ №1:
Вот лучший способ добиться этого (в любом случае используя регулярное выражение):
select
sum(surname REGEXP '^[a-dA-D].*') as ad_count,
sum(surname REGEXP '^[e-hE-H].*') as eh_count,
sum(surname REGEXP '^[i-lI-L].*') as il_count,
sum(surname REGEXP '^[m-pM-P].*') as mp_count,
sum(surname REGEXP '^[q-tQ-T].*') as qd_count,
sum(surname REGEXP '^[u-zU-Z].*') as uz_count
from people
Эта элегантная краткость работает из-за того, что в mysql true
is 1
и false
is 0
, таким образом sum(some condition)
, подсчитывается, сколько раз это верно.
кстати, я добавил верхний регистр в ваше регулярное выражение.
Вы получите лучшую производительность, выбрав из внутреннего select, который выполняет работу по более эффективному вычислению группы (например, используя регистр для substr(фамилия, 1, 1)), а затем суммируя тесты с этим вычисленным значением vale.
Комментарии:
1. Это решение было бы лучше, поскольку оно не потребует от mysql использования временной таблицы и сортировки файлов, как при использовании GROUP BY will.
2. Вряд ли «лучший способ». Все эти ненужные вызовы регулярных выражений * содрогаются*
Ответ №2:
Регулярное выражение является излишним и здесь совершенно не нужно.
Возможно, что-то вроде этого, используя базовую алгебру строк:
SELECT
SUM(CASE WHEN SUBSTR(`surname`, 1, 1) BETWEEN 'a' AND 'd' THEN 1 ELSE 0 END) AS `SUM_a-d`,
SUM(CASE WHEN SUBSTR(`surname`, 1, 1) BETWEEN 'e' AND 'h' THEN 1 ELSE 0 END) AS `SUM_e-h`,
SUM(CASE WHEN SUBSTR(`surname`, 1, 1) BETWEEN 'i' AND 'l' THEN 1 ELSE 0 END) AS `SUM_i-l`,
SUM(CASE WHEN SUBSTR(`surname`, 1, 1) BETWEEN 'm' AND 'p' THEN 1 ELSE 0 END) AS `SUM_m-p`,
SUM(CASE WHEN SUBSTR(`surname`, 1, 1) BETWEEN 'q' AND 't' THEN 1 ELSE 0 END) AS `SUM_q-t`,
SUM(CASE WHEN SUBSTR(`surname`, 1, 1) BETWEEN 'u' AND 'z' THEN 1 ELSE 0 END) AS `SUM_u-z`
FROM `people`
Комментарии:
1. Рассмотрите возможность более элегантного выражения как
sum(substr(surname, 1, 1) between 'a' and 'd')
etc. Меньше значит больше.
Ответ №3:
Вы можете сделать запрос немного более явным, например:
SELECT
SUM(CASE WHEN surname REGEXP '^[a-d].*' THEN 1 ELSE 0 END) AS a_d_count
,SUM(CASE WHEN surname REGEXP '^[e-h].*' THEN 1 ELSE 0 END) AS e_h_count
,SUM(CASE WHEN surname REGEXP '^[i-l].*' THEN 1 ELSE 0 END) AS i_l_count
,SUM(CASE WHEN surname REGEXP '^[m-p].*' THEN 1 ELSE 0 END) AS m_p_count
,SUM(CASE WHEN surname REGEXP '^[q-t].*' THEN 1 ELSE 0 END) AS q_t_count
,SUM(CASE WHEN surname REGEXP '^[u-z].*' THEN 1 ELSE 0 END) AS u_z_count
FROM (SELECT surname FROM people ORDER BY surname ASC) p
Ответ №4:
Избегая регулярных выражений и условных выражений, вы можете сделать это:
SELECT CONCAT(LEFT(UPPER(surname),1), '-', CHAR(ASCII(UPPER(surname)) 3)) AS r,
count(id)
FROM people
GROUP BY ROUND((ASCII(UPPER(surname)-65)/4),0);
Это устанавливает ваши диапазоны длиной в 4 буквы, что означает, что последний диапазон равен «yz», но вы можете изменить это с помощью немного большей математики.