如何计算所有满足MySQL条件的不同值?

时间:2017-11-06 14:31:46

标签: mysql sql group-by count distinct

我尝试编写查询以查找特定字段中的不同值,计算该值的出现次数,对于该特定值的所有实例,满足另一列值,然后将结果显示为如下(更多解释如下):

示例db:

RowId    Status       MemberIdentifier
-----    ------       ----------------
1       In Progress   111111111
2       Complete      123456789
3       Not Started   146782452
4       Complete      111111111
5       Complete      123456789
6       Not Started   146782452
7       Complete      111111111

期望的结果:

Status         MemberIdentifierCount 
------         ---------------------- 
Not Started    1
In Progress    1
Complete       1

在上面的查询中,计算并显示具有给定状态的不同MemberIdentifier的数量。如果MemberIdentifier有两行Status' Complete'但有一个状态正在进行中,'它被分组并计入进行中(即,MemberIdentifier = 111111111)。要将MemberIdentifier分组并计为完成,其所有行的状态必须为“完成”状态。 (即,MemberIdentifier = 123456789)。任何见解都将受到赞赏(MySQL新手)。

8 个答案:

答案 0 :(得分:10)

每个MemberIdentifier找到您认为合适的状态,例如'In Progress'胜过'Complete''Not Started''Not Started'胜过'Complete'。为此使用条件聚合。

select status, count(*)
from
(
  select 
    case when sum(status = 'In Progress') > 0 then 'In Progress'
         when sum(status = 'Not Started') > 0 then 'Not Started'
         else 'Complete'
    end as status
  from mytable
  group by memberidentifier
) statuses
group by status;

答案 1 :(得分:6)

SELECT max_status AS Status
     , COUNT(*) AS ct
    FROM (
        SELECT MAX(Status) AS max_status
            FROM tbl
            GROUP BY MemberIdentifier
         ) AS a
    GROUP BY max_status;

这利用了这些字符串的比较方式:“进行中”> “完成”。在这样做时,它会对具有多个状态的任何其他成员执行随机操作。

答案 2 :(得分:5)

<强> SQL

SELECT AdjustedStatus AS Status,
       COUNT(*) AS MemberIdentifierCount
FROM
(SELECT IF(Status='Complete',
           IF(EXISTS(SELECT Status
                     FROM tbl t2
                     WHERE t2.Status = 'In Progress'
                       AND t2.MemberIdentifier = t1.MemberIdentifier),
              'In Progress',
              'Complete'),
           Status) AS AdjustedStatus,
        MemberIdentifier
 FROM tbl t1
 GROUP BY AdjustedStatus, MemberIdentifier) subq
GROUP BY AdjustedStatus;

在线演示

http://rextester.com/FFGM6300

<强>解释

第一个IF() function检查状态是否为“完成”,如果是,则检查是否存在具有相同MemberIdentifier但状态为“正在进行”的另一个记录:这样做通过IF(EXISTS(SELECT...)))。如果找到,则会将“正在进行”状态分配给AdjustedStatus字段,否则AdjustedStatus将根据(未经调整的)Status值进行设置。

对于表格中的每一行,GROUP BY AdjustedStatusMemberIdentifier已经调整后的状态是这样得出的,以获得这两个字段值的所有唯一组合。然后将其制作为子查询 - 别名为subq。然后汇总(GROUP BYAdjustedStatus并计算出现次数,即每次出现的唯一MemberIdentifier次数。

答案 3 :(得分:5)

我假设您有2个表格如下

CREATE TABLE table1 (RowId INT PRIMARY KEY, MemberIdentifier VARCHAR(255));
INSERT INTO table1 (RowId, MemberIdentifier)
VALUES
(1,'111111111'), (2, '123456789'), (3, '146782452'), (4, '111111111'),(5,'123456789'), (6,'146782452'), (7,'111111111');


CREATE TABLE table2 (RowId INT PRIMARY KEY, Status VARCHAR(255));
INSERT INTO table2 (RowId, Status)
VALUES
(1,'In Progress'), (2,'Complete'   ), (3,'Not Started'), (4,'Complete'   ), (5,'Complete'   ), (6,'Not Started'), (7,'Complete'   );

假设您在这些表格中没有数百万条记录,您可以使用下面的查询来实现您想要的效果。

SELECT CASE WHEN not_started.Status = 'Not Started' 
            THEN 'Not Started' 
            WHEN in_progress.Status = 'In Progress' 
            THEN 'In Progress' 
            WHEN complete.Status = 'Complete' 
            THEN 'Complete' 
       END AS over_all_status,
       COUNT(*) AS MemberIdentifierCount
  FROM  (SELECT DISTINCT t1.MemberIdentifier
          FROM table1 t1) main
        LEFT OUTER JOIN   
            (SELECT DISTINCT t1.MemberIdentifier, t2.Status
              FROM table1 t1,
                   table2 t2 
             WHERE t1.RowId = t2.RowId
               AND t2.Status = 'In Progress') in_progress
            ON (main.MemberIdentifier = in_progress.MemberIdentifier)
        LEFT OUTER JOIN
            (SELECT DISTINCT t1.MemberIdentifier, t2.Status
              FROM table1 t1,
                   table2 t2 
             WHERE t1.RowId = t2.RowId
               AND t2.Status = 'Not Started') not_started
        ON (main.MemberIdentifier = not_started.MemberIdentifier)
        LEFT OUTER JOIN
            (SELECT DISTINCT t1.MemberIdentifier, t2.Status
              FROM table1 t1,
                   table2 t2 
             WHERE t1.RowId = t2.RowId
               AND t2.Status = 'Complete') complete
        ON (main.MemberIdentifier = complete.MemberIdentifier)
GROUP BY over_all_status;

基本上,查询为每个MemberIdentifier创建一条记录,其中包含所有可能的三种状态。然后,它根据总体状态对结果进行分组并输出计数。

查询的输出是

enter image description here

答案 4 :(得分:3)

使用以下代码获取MemberIdentifier

的状态
select MemberIdentifier
,case 
when total = cn then 'Complete' 
when total < cn then 'In Progress' 
when total is null then 'Not Started' END as Fstatus
 from 
(
select sum(stat) total,MemberIdentifier,(select count(MemberIdentifier) as cnt from tbldata t1
     where t1.MemberIdentifier = C.MemberIdentifier
     group by MemberIdentifier) as cn
from (
select MemberIdentifier,case status when 'In Progress' then -1 
                                    when 'Complete' Then 1 
                                    when 'Not Started' then null End as Stat from tbldata 
 ) C
 group by MemberIdentifier

 ) as f1

使用以下代码获取特定状态的MemberIdentifier的数量。

Select count(fstatus) counts,fstatus from (
select MemberIdentifier
,case when total = cn then 'Complete' 
      when total < cn then 'In Progress' 
      when total is null then 'Not Started' END as Fstatus
 from 
(
select sum(stat) total,MemberIdentifier,(select count(MemberIdentifier) as cnt from tbldata t1
     where t1.MemberIdentifier = C.MemberIdentifier
     group by MemberIdentifier) as cn
from (
select MemberIdentifier
,case status when 'In Progress' then -1 when 'Complete' Then 1 when 'Not Started' then null End as Stat from tbldata 
 ) C
 group by MemberIdentifier

 ) as f1

 ) f2 group by fstatus
输出:
counts  fstatus
1       Complete
1       In Progress
1       Not Started

答案 5 :(得分:2)

如果status的优先顺序是

 Not Started
 In Progress
 Complete

我们可以使用快捷方式......

   SELECT t.memberIdentifier
        , MAX(t.status) AS status
     FROM mytable t
    GROUP BY t.MemberIdentifier

这使我们得到了独特的memberIdentifier

如果某个成员的行有'In Progress''Complete'状态,则查询将返回'In Progress'作为状态。

如果该成员的有任何状态大于'Complete'的行,我们将为成员返回状态'Complete'

要从该结果中获取计数,我们可以将该查询引用为内联视图:

 SELECT q.status
      , COUNT(q.memberIdentifier) 
   FROM ( 
          SELECT t.memberIdentifier
               , MAX(t.status) AS status
            FROM mytable t
           GROUP BY t.MemberIdentifier
        ) q
  ORDER BY q.status

想想是否这样...... MySQL首先在parens之间运行查询(MySQL称之为&#34;派生表&#34;。查询的结果是一组行,可以像查询一样查询表

我们可以做COUNT(DISTINCT q.memberIdentifier)或假设memberIdentifier保证非NULL,我们可以COUNT(1)SUM(1)获得相同的结果。 (内联视图中的GROUP BY保证memberIdentifier将是唯一的。)

在更一般的情况下,我们没有方便的状态优先顺序的字母排序快捷方式......我们可以使用一个表达式返回&#34;顺序&#34; ;。这使查询更复杂,但它的工作方式相同。

我们可以用以下内容替换t.status

  CASE t.status
  WHEN 'Complete'    THEN 1
  WHEN 'In Progress' THEN 2
  WHEN 'Not Started' THEN 3
  ELSE 4
  END AS `status_priority`

用反向替换q.status,转换回字符串:

  CASE q.status_priority
  WHEN 1 THEN 'Complete'
  WHEN 2 THEN 'In Progress'
  WHEN 3 THEN 'Not Started'
  ELSE NULL
  END AS `status`

我们需要决定如何处理状态值,这些值不是三个中的一个......是否会被忽略,处理的优先级高于其他任何一个。 (测试用例包含status = 'Unknown'行和status = 'Abracadabra行。

答案 6 :(得分:2)

我刚刚修改@ thorsten-kettner的解决方案,因为你在加入桌子时遇到了问题。我假设你有2个表,table1 - 至少有2行(RowID和MemberIdentifier)和table2 - 至少有2行(RowID和Status)

select Status, count(*)
from(
  select 
    case when sum(newTable.Status = 'In Progress') > 0 then 'In Progress'
         when sum(newTable.Status = 'Not Started') > 0 then 'Not Started'
         else 'Complete'
    end as status
  from (
    select table1.RowId as RowId, table1.MemberIdentifier as MemberIdentifier, table2.Status as Status from table1 INNER JOIN table2 ON table1.RowId = table2.RowId
  )newTable
  group by newTable.MemberIdentifier
) statuses
group by Status;

答案 7 :(得分:1)

使用特定表配置顺序的另一种方法(映射到两个整数的幂)。

此映射允许bit_or聚合简单地转置数据。

http://rextester.com/edit/ZSG98543

-- Table bit_progression to determine priority

CREATE TABLE bit_progression (bit_status int PRIMARY KEY, Status VARCHAR(255));
INSERT INTO bit_progression (bit_status, Status)
VALUES
(1,       'Not Started'),  
(2,       'Complete'   ),      
(4,       'In Progress');

select
    Status,
    count(*)
from
    (
    select
         MemberIdentifier,max(bit_status) bit_status
    from
        tbl natural join bit_progression
    group by
        MemberIdentifier
    ) Maxi natural join bit_progression
group by
    Status
;

生产

Status  count(*)

1   Complete    1
2   In Progress 1
3   Not Started 1

额外:

select
    MemberIdentifier,
    bit_or(bit_status) bits_status,
    case when bit_or(bit_status) & 4 = 4 then true end as withStatusInProgress,
    case when bit_or(bit_status) & 2 = 2 then true end as withStatusComplete,
    case when bit_or(bit_status) & 1 = 1 then true end as withStatusNotStarted
from
    tbl natural join bit_progression
group by
    MemberIdentifier
;

制作它:

MemberIdentifier bits_status    withStatusInProgress    withStatusComplete  withStatusNotStarted

111111111   6   1       1       NULL
123456789   2   NULL    1       NULL
146782452   1   NULL    NULL    1