#mysql #sql
#mysql #sql
Вопрос:
Учитывая следующую (значительно упрощенную) таблицу примеров:
CREATE TABLE `permissions` (
`name` varchar(64) NOT NULL DEFAULT '',
`access` enum('read_only','read_write') NOT NULL DEFAULT 'read_only'
);
И следующее содержимое примера:
| name | access |
=====================
| foo | read_only |
| foo | read_write |
| bar | read_only |
Что я хочу сделать, так это запустить SELECT
запрос, который извлекает одну строку для каждого уникального значения в name
, отдавая предпочтение тем, у access
которых есть значение read_write
, есть ли способ, которым это можно сделать? т.е. Так, чтобы результаты, которые я получу, были:
foo | read_write |
bar | read_only |
Возможно, в будущем мне потребуется добавить новые параметры в access
столбец, но они всегда будут в порядке важности (от наименьшего к наибольшему), поэтому, если возможно, решение, которое может справиться с этим, было бы особенно полезно.
Кроме того, чтобы уточнить, моя фактическая таблица включает в себя другие поля, кроме этих, поэтому я не использую уникальный ключ в name
столбце; по дизайну будет несколько строк по имени в соответствии с различными критериями.
Комментарии:
1. Лучший способ сделать это действительно зависит от того, как могут выглядеть «будущие» данные в access и что вы хотели бы вернуть. Например, вы хотели бы, чтобы доступ был отсортирован в алфавитном порядке, если нет записи «read_write»?
Ответ №1:
С вашими данными будет работать следующее:
select name, max(access)
from permissions
group by name;
Однако это упорядочивает по строковым значениям, а не по индексам. Вот еще один метод:
select name,
substring_index(group_concat(access order by access desc), ',') as access
from permissions
group by name;
Довольно сложно использовать индекс, order by
но min()
max()
использовать символьное значение. Некоторые могут даже назвать это ошибкой.
Ответ №2:
Вы можете создать другую таблицу с приоритетом access
(чтобы вы могли добавлять новые параметры), а затем сгруппировать и найти MIN()
значение таблицы приоритетов:
Например. создайте таблицу, вызываемую Priority
со значениями
| PriorityID| access |
========================
| 1 | read_write |
| 2 | read_only |
И затем,
SELECT A.Name, B.Access
FROM (
SELECT A.name, MIN(B.PriorityID) AS Most_Valued_Option -- This will be 1 if there is a read_write for that name
FROM permissions A
INNER JOIN Priority B
ON A.Access = B.Access
GROUP BY A.Name ) A
INNER JOIN Priority B
ON A.Most_Valued_Option = B.PriorityID
-- Join that ID with the actual access
-- (and we will select the value of the access in the select statement)
Комментарии:
1. Я не вижу необходимости создавать торнадо только для того, чтобы задуть спичку.
2. Как бы вы решили проблему «добавления новых параметров в
access
столбец в будущем»? (Я обеспокоен тем, что новые параметры не будут упорядочены по важности, соответствующей алфавитному порядку)3. Как бы вы решили проблему необходимости другого порядка приоритета для другого запроса? Я обеспокоен тем, что этот «приоритет» для доступа больше связан с запросом , чем с разрешением.
4. Если у вас другой порядок приоритетов, вы должны изменить порядок на основе PriorityID.
5. Если для первого запроса требуется определенный порядок предпочтений / приоритетов
access
, а для второго запроса требуется другой порядок предпочтений / приоритетов… как бы это предложенное решение решило эту проблему? На вопрос об удовлетворении возможных будущих требований для указания порядка, который не является алфавитным, и для возврата не только предпочтительного / приоритетногоaccess
, но и других столбцов в той же строке, я опубликовал ответ, в котором используются функцииFIELD
andELT
. Запрос в моем ответе является излишним для текущих требований; решения от Гордона достаточно, дополнительная таблица не требуется..
Ответ №3:
Решение, предложенное Гордоном, достаточно для текущих требований.
Если мы ожидаем, что в будущем порядок приоритетов будет отличаться от алфавитного порядка строк (или по значению индекса перечисления)…
В качестве модифицированной версии ответа Гордона у меня возникнет соблазн использовать функцию MySQL FIELD
и (ее обратную) ELT
функцию, что-то вроде этого:
SELECT p.name
, ELT(
MIN(
FIELD(p.access
,'read_only','read_write','read_some'
)
)
,'read_only','read_write','read_some'
) AS access
FROM `permissions` p
GROUP BY p.name
Если спецификация должна извлекать всю строку, а не только значение access
столбца, мы могли бы использовать запрос встроенного представления, чтобы найти предпочтительный access
, и соединение с preferences
таблицей, чтобы извлечь всю строку…
SELECT p.*
FROM ( -- inline view, to get the highest priority value of access
SELECT r.name
, MIN(FIELD(r.access,'read_only','read_write','read_some')) AS ax
FROM `permissions` r
GROUP BY r.name
) q
JOIN `permissions` p
ON p.name = q.name
AND p.access = ELT(q.ax,'read_only','read_write','read_some')
Обратите внимание, что этот запрос возвращает не только access
с наивысшим приоритетом, но также может возвращать любые столбцы из этой строки.
С FIELD
ELT
помощью функций и мы можем реализовать любое произвольное упорядочение списка конкретных известных значений. Не просто алфавитный порядок или порядок по значению индекса перечисления.
Эта логика для «приоритета» может содержаться в запросе и не будет зависеть от дополнительных столбцов в permissions
таблице или содержимого любых других таблиц.
Чтобы получить поведение, которое мы ищем, просто указав приоритет для access
, «список значений», используемый в FIELD
функции, должен соответствовать «списку значений» в ELT
функции, в том же порядке, и списки должны включать все возможные значения access
.
Ссылка:
http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_elt
http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_field
РАСШИРЕННОЕ ИСПОЛЬЗОВАНИЕ
Не то, чтобы у вас было требование сделать это, но учитывая возможные будущие требования… мы отмечаем, что…
Другой порядок «списка значений» приведет к другому порядку приоритета access
. Таким образом, каждый из множества запросов может реализовывать свои собственные правила для «приоритета». Какое access
значение искать первым, вторым и так далее, путем изменения порядка полного «списка значений».
Помимо простого изменения порядка, также возможно исключить возможное значение из «списка значений» в функциях FIELD
и ELT
. Рассмотрим, например, исключение 'read_only'
значения из списка в этой строке:
, MIN(FIELD(r.access,'read_write','read_some')) AS ax
и из этой строки:
AND p.access = ELT(q.ax,'read_write','read_some')
Это эффективно ограничит name
возвращаемые строки. Только name
те, которые имеют access
значение 'read_write'
или 'read_some'
. Другой способ взглянуть на это, a name
, который имеет только a 'read_only'
for access
, не будет возвращен запросом.
Также возможны другие модификации «списка значений», где списки не «совпадают», для реализации еще более мощных правил. Например, мы могли бы исключить a name
, у которого есть строка с 'read_only'
.
Например, в ELT
функции вместо 'read_only'
значения мы используем значение, которое, как мы знаем, не существует (и не может существовать) ни в каких строках. Для иллюстрации,
мы можем включить 'read_only'
в качестве «наивысшего приоритета» в этой строке…
, MIN(FIELD(r.access,'read_only','read_write','read_some')) AS ax
^^^^^^^^^^^
поэтому, если найдена строка с 'read_only'
, это будет иметь приоритет. Но в ELT
функции во внешнем запросе мы можем преобразовать это обратно в другое значение…
AND p.access = ELT(q.ax,'eXcluDe','read_write','read_some')
^^^^^^^^^
Если мы знаем, что 'eXcluDe'
это значение не существует в access
столбце, мы фактически исключили любое name
значение, имеющее 'read_only'
строку, даже если 'read_write'
строка есть.
Не то, чтобы у вас была спецификация или текущее требование для выполнения чего-либо из этого. Что-то, что нужно иметь в виду для будущих запросов, которые имеют такие требования.
Комментарии:
1. Я отметил ответ Гордона как правильный, поскольку это самый простой ответ, который решает проблему, как указано. Однако я хотел бы поблагодарить вас за детализацию вашего ответа, поскольку я действительно обнаружил, что пример на основе встроенного представления лучше всего подходит для реальной проблемы, которую я хочу решить (я думаю, что я слишком упростил вопрос).
2. @Haravikk: Я согласен, что Гордон ‘
Ответ №4:
Вы можете использовать оператор distinct (или Group by) ВЫБЕРИТЕ отдельное имя, доступ ИЗ вкладки;
Комментарии:
1. OP мог бы это сделать, но
SELECT DISTINCT name, access FROM ...
вернул бы обе строки дляname='foo'
. Это вернет обаread_only
иread_write
. (DISTINCT
Ключевое слово применяется ко всем выражениям в списке ВЫБОРА.)
Ответ №5:
Это тоже работает:
SELECT name, MAX(access)
FROM permissions
GROUP BY name ORDER BY MAX(access) desc