按字段中的值进行SELECT分组

时间:2016-10-05 20:51:47

标签: 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列上使用唯一键的原因;按设计名称将有多行符合各种标准。

5 个答案:

答案 0 :(得分:3)

以下内容适用于您的数据:

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()使用字符值相当时髦。有些人甚至可能称这是一个错误。

答案 1 :(得分:2)

您可以创建另一个优先级为access的表(这样您可以添加新选项),然后分组并找到优先级表的MIN()值:

E.g。创建一个名为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)

答案 2 :(得分:1)

戈登提出的解决方案足以满足当前的要求。

如果我们预计未来要求优先顺序不是按字母顺序排列(或通过枚举索引值)...

作为Gordon答案的修改版本,我很想使用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,还可以返回该行中的任何列。

使用FIELDELT函数,我们可以实现特定已知值列表的任何临时排序。不仅仅是字母顺序,还是按枚举索引值排序。

“priority”的逻辑可以包含在查询中,并且不依赖于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值。

除了重新排序之外,还可以从FIELDELT函数中的“值列表”中省略一个可能的值。例如,考虑从此行的列表中省略'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'的{​​{1}}。另一种查看方式,name只有 'read_only' access name将{em>

对“值列表”的其他修改也是可能的,其中列表不“匹配”,以实现更强大的规则。例如,我们可以排除有'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',即使有import itertools list(itertools.chain(*[[str(s),'Numpad{}'.format(s)] for s in range(1,10)])) list(itertools.chain.from_iterable([str(s),'Numpad{}'.format‌​(s)] for s in range(0,10))) 行。

并非您有规范或当前要求执行任何此操作。对于具有这些要求的未来查询,请记住一些事项。

答案 3 :(得分:0)

您可以使用不同的声明(或分组依据) 选择不同的名称,访问 FROM标签;

答案 4 :(得分:0)

这也有效:

SELECT name, MAX(access)
  FROM permissions
GROUP BY name ORDER BY MAX(access) desc