汇总后保存总计和小计行位置

时间:2011-10-06 10:48:28

标签: mysql

这是来自带汇总的mysql的示例结果

|country | region | value  |
|--------|--------|--------|
|   us   |  west  |   5    |
|   us   |  east  |   15   |
|   us   |  north |   10   |
|   us   |  total |   30   |
|   uk   |  west  |   3    |
|   uk   |  east  |   2    |
|   uk   |  north |   1    |
|   uk   |  total |   6    |
|  total |  total |   36   |

我想像这样排序值列,保留总行位置

ASC

|   us   |  west  |   5    |
|   us   |  north |   10   |
|   us   |  east  |   15   |
|   us   |  total |   30   |
|   uk   |  north |   1    |
|   uk   |  east  |   2    |
|   uk   |  west  |   3    |
|   uk   |  total |   6    |
|  total |  total |   36   |

DESC

|   us   |  east  |   15   |
|   us   |  north |   10   |
|   us   |  west  |   5    |
|   us   |  total |   30   |
|   uk   |  west  |   3    |
|   uk   |  east  |   2    |
|   uk   |  north |   1    |
|   uk   |  total |   6    |
|  total |  total |   36   |

这是一个查询示例:

SELECT *
FROM (
  SELECT 
  COALESCE(country, 'total') AS country, 
  COALESCE(region, 'total' ) AS region, 
  SUM('value'), 
  FROM table 
  GROUP BY country ASC, region ASC 
  WITH ROLLUP
) t
ORDER BY ... maybe something to do here ...

也许基于正则表达式的排序顺序? 或者使用mysql字符串函数?

我不知道如何解决我的问题

感谢提前寻求帮助

...

在你的帮助和经过一些个人反思之后 我想我现在有一个很好的方法来做这件事

SELECT *
FROM (
   SELECT 
   IF(IFNULL(country, 1),'total',NULL) AS country,
   IF(IFNULL(region, 1),'total',NULL) AS region,
   COALESCE(country, 'Total') AS country1, 
   COALESCE(region, 'Total') AS region1, 
   SUM(value) as `value to sort`
   FROM data_table
   GROUP BY country, region WITH ROLLUP
) t 
ORDER BY  
country IS NULL DESC, 
country1 ASC, 
region IS NULL DESC, ...

 ... `value to sort` DESC

(or)

 ... `value to sort` ASC

即使使用DESC排序,我总是在聚合和子聚合值之后总计和子总数: - )

你认为这是一个好方法吗? 它在所有情况下都适合我

3 个答案:

答案 0 :(得分:2)

基于Johan's answer的原始版本:

SELECT *
FROM (
  SELECT 
    COALESCE(country, 'total') AS country,
    COALESCE(region, 'total' ) AS region,
    SUM(`value`) as `value`, 
  FROM `table` 
  GROUP BY country, region WITH ROLLUP
) t
ORDER BY country = 'total', country, region = 'total', `value`

这个技巧的工作方式是,如果country = 'total'列等于country,则表达式'total'的计算结果为1(真),否则计算结果为0(假)。按升序排列,1在0之后。因此,按该表达式排序会强制country列等于'total'的任何行在任何其他列之后排序。

同样,在region = 'total'之前按表达式value排序会强制'total'中值region的任何行在具有相同{{1}的任何其他行之后排序}},无论他们的country列如何。

同样的技巧也适用于其他comparison operators。例如,如果您想强制使用负值对正值进行排序,则可以按value对行进行排序。

答案 1 :(得分:0)

with rollup生成的总计行具有非聚合行的所有零值 您可以利用它来强制该行为最后一个(或第一个)。

试试这个,没有测试过,所以我不是百分百确定:

SELECT 
  ti2.country
  , ti2.region
  ,ti2.`value`
FROM table1 to1
INNER JOIN (SELECT 
              t2.country
              ,IFNULL(ti1.region,'subtotal') as country
              ,ti1.`value` 
            FROM (
             SELECT 
              t1.country
              , t1.region
              , SUM(t1.`value`) as `value`
             FROM table1 t1
             WHERE t1.country = to1.country 
             GROUP BY country, region WITH ROLLUP) ti1 ) ti2
ON (ti2.country = to1.country)
GROUP BY country, region WITH ROLLUP
ORDER BY country, region = 'subtotal', region 

请注意,order by子句应该进入外部(!)选择,而不是内部选择 (我知道内部选择的顺序在大多数情况下都有效,但是当查询变得复杂时,它会搞砸并让你偏离轨道)
还要注意,依赖行为构建到MySQL的group by子句中的非标准隐式顺序并不是一个好主意;最好坚持使用标准SQL,除非获得明显的好处。

答案 2 :(得分:0)

这很棘手!我做了一个(相当大的)假设你有一个countryId列可以在你的源表中使用,它唯一地标识表中的每个国家(所以'us'有countryId = 1,'uk'有countryId = 2等等)。如果你不这样做,你总是可以添加一个:

-- Descending
SELECT *
FROM (
  SELECT 
  COALESCE(country, 'total') AS pays,
  COALESCE(region, 'total' ) AS region,
  SUM(value) as value, 
  (SUM(value) * (CASE WHEN COALESCE(region, 'total') = 'total' THEN 1 ELSE -1 END)   
    + countryId*(SELECT SUM(value) FROM test_table) 
    + CASE WHEN COALESCE(country, 'total') = 'total' 
           THEN (SELECT SUM(value) FROM test_table) ELSE 0 END) as rank1
  FROM test_table 
  GROUP BY country, region WITH ROLLUP
) t
ORDER BY rank1 asc;


-- Ascending
SELECT *
FROM (
  SELECT 
  COALESCE(country, 'total') AS pays, 
  COALESCE(region, 'total' ) AS region,
  SUM(value) as value, 
  (SUM(value) + countryId*(SELECT SUM(value) FROM test_table) 
    + CASE WHEN COALESCE(country, 'total') = 'total' 
           THEN (SELECT SUM(value) FROM test_table) ELSE 0 END) as rank1
  FROM table 
  GROUP BY country, region WITH ROLLUP
) t
order by rank1 asc;

不是最漂亮的,当然不是最干净的,但应该帮助你。