查找不同列数的总和

时间:2013-11-19 13:00:32

标签: mysql sql sum

我有这样的表:

value1   value2     value3    value4    value5   constant
1          2          3           4         5     2
8          2          8           3         5     2
1          5          3           4         5     3
1          2          6           4         5     3

现在我想做的是,我想在列中找到只有那些列数的总和作为常量字段中给出的值:

例如,如果常量值为2,则在row1中, 我需要找到value1+value2的总和。

如果常量中的值为3,我需要找到总和value 1+ value2 + value3

抱歉英语不好。有什么合适的方法吗?我一直在谷歌搜索它,但找不到合适的方式

3 个答案:

答案 0 :(得分:3)

值列固定为1到5?如果不是,则需要生成动态查询。

试试这个:

SELECT
  IF(constant = 1, value1, 
    IF (constant = 2, value1 + value2,
      IF (constant = 3, value1 + value2 + value3,
        IF (constant = 4, value1 + value2 + value3 + value4, value1 + value2 + value3 + value4 + value5)
      )
    )
  )
FROM tab;

<强>已更新

如果我是你,我会这样设计。

tbl1(id, constant),
value_tbl1(tbl1_id, column_seq, value);

SELECT SUM(value)
FROM tbl1 t, value_tbl1 v
WHERE t.id = v.tbl1_id
  AND column_seq BETWEEN 1 AND tbl1.constant

答案 1 :(得分:2)

<强>简介

解决这个问题的正常方法是:选择正确的结构。如果你有24个字段,你需要在SQL中动态循环,那么出错了。此外,您的表没有任何主键(或者您没有提到),这是很糟糕的。

非常重要的注意事项

不管我描述的方式是否有效。由于在MySQL中使用了一些特殊的东西,这仍然是不好的做法。您可以自担风险使用它 - 如果可能的话,再次重新考虑您的结构。

黑客

实际上,你可以使用MySQL INFORMATION_SCHEMA表做一些技巧。通过这个,您可以创建“text”SQL,稍后可以在prepared statement中使用。

我的表

它被称为test。这是:

+----------+---------+------+-----+---------+-------+
| Field    | Type    | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| value1   | int(11) | YES  |     | NULL    |       |
| value2   | int(11) | YES  |     | NULL    |       |
| value3   | int(11) | YES  |     | NULL    |       |
| value4   | int(11) | YES  |     | NULL    |       |
| constant | int(11) | YES  |     | NULL    |       |
+----------+---------+------+-----+---------+-------+

- 我有4个“值”字段,没有主键列(这会导致麻烦,但我已经解决了这个问题)。现在,我的数据:

+--------+--------+--------+--------+----------+
| value1 | value2 | value3 | value4 | constant |
+--------+--------+--------+--------+----------+
|      2 |      5 |      6 |      0 |        2 |
|      1 |   -100 |      0 |      0 |        1 |
|      3 |     10 |    -10 |      0 |        3 |
|      4 |      0 |     -1 |      5 |        3 |
|     -1 |      1 |     -1 |      1 |        4 |
+--------+--------+--------+--------+----------+

技巧

它是关于从MySQL中提到的服务模式中选择数据并使用GROUP_CONCAT函数:

select 
  concat('SELECT CASE(seq) ', 
    group_concat(groupcase separator ''), 
    ' END AS result FROM (select *, @j:=@j+1 as seq from test cross join (select @j:=0) as initj) as inittest') 
from 
  (select 
    concat(' WHEN ', rownum, ' THEN ', groupvalue) as groupcase 
   from 
     (select 
       rownum, 
       group_concat(COLUMN_NAME SEPARATOR '+') as groupvalue 
      from 
       (select 
         *, 
         @row:=@row+1 as rownum 
        from test 
          cross join (select @row:=0) as initrow) as tablestruct 
        left join 
          (select 
             COLUMN_NAME, 
             @num:=@num+1 as num 
           from 
             INFORMATION_SCHEMA.COLUMNS cross join (select @num:=0) as init 
           where 
             TABLE_SCHEMA='test' && 
             TABLE_NAME='test' && 
             COLUMN_NAME!='constant') as struct 
          on tablestruct.constant>=struct.num 
        group by 
          rownum) as groupvalues) as groupscase

- 这会怎么做?实际上,我建议逐步执行它(即将更复杂的层添加到您已经理解的层) - 我怀疑是否有简短的方法来描述正在发生的事情。它不是一个巫术,它是关于从输入条件构造有效的文本SQL。最终结果如下:

SELECT CASE(seq)  WHEN 1 THEN value1+value2 WHEN 2 THEN value1 WHEN 3 THEN value3+value2+value1 WHEN 4 THEN value3+value2+value1 WHEN 5 THEN value2+value1+value4+value3 END AS result FROM (select *, @j:=@j+1 as seq from test cross join (select @j:=0) as initj) as inittest

(我没有添加格式,因为SQL是生成的字符串,而不是你自己编写的字符串。)

最后一步

现在怎么办?只需分配:

mysql> set @s=(select concat('SELECT CASE(seq) ', group_concat(groupcase separator ''), ' END AS result FROM (select *, @j:=@j+1 as seq from test cross join (select @j:=0) as initj) as inittest') from (select concat(' WHEN ', rownum, ' THEN ', groupvalue) as groupcase from (select rownum, group_concat(COLUMN_NAME SEPARATOR '+') as groupvalue from (select *, @row:=@row+1 as rownum from test cross join (select @row:=0) as initrow) as tablestruct left join (select COLUMN_NAME, @num:=@num+1 as num from INFORMATION_SCHEMA.COLUMNS cross join (select @num:=0) as init where TABLE_SCHEMA='test' && TABLE_NAME='test' and COLUMN_NAME!='constant') as struct on tablestruct.constant>=struct.num group by rownum) as groupvalues) as groupscase);
Query OK, 0 rows affected (0.00 sec)

mysql> prepare stmt from @s;
Query OK, 0 rows affected (0.00 sec)
Statement prepared

- 最后:

mysql> execute stmt;

您将得到以下结果:

+--------+
| result |
+--------+
|      7 |
|      1 |
|      3 |
|      3 |
|      0 |
+--------+

为什么这很糟糕

因为它为整个表生成字符串。即每行 !想象一下,如果你有1000行 - 这将是令人讨厌的。 MySQL在GROUP_CONCATgroup_concat_max_len中也有限制 - 显然会限制这种方式。

那我为什么这样做?

因为我很好奇是否存在没有额外DDL和隐含重新计算表字段的方法。我找到了,所以把它留在这里。

答案 2 :(得分:1)

正确的做法可能就是这样......

DROP TABLE IF EXISTS variables;

CREATE TABLE variables
(constant_id INT NOT NULL
,sequence_id INT NOT NULL
,value INT NOT NULL
,PRIMARY KEY(constant_id,sequence_id)
);

INSERT INTO variables VALUES
(1,1,1),
(1,2,2),
(1,3,3),
(1,4,4),
(1,5,5),
(2,1,8),
(2,2,2),
(2,3,8),
(2,4,3),
(2,5,5),
(3,1,1),
(3,2,5),
(3,3,3),
(3,4,4),
(3,5,5),
(4,1,1),
(4,2,2),
(4,3,6),
(4,4,4),
(4,5,5);

DROP TABLE IF EXISTS constants;

CREATE TABLE constants
(constant_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,constant INT NOT NULL
);

INSERT INTO constants VALUES (1,2),(2,2),(3,3),(4,3);

SELECT * FROM constants;
+-------------+----------+
| constant_id | constant |
+-------------+----------+
|           1 |        2 |
|           2 |        2 |
|           3 |        3 |
|           4 |        3 |
+-------------+----------+

SELECT * FROM variables;
+-------------+-------------+-------+
| constant_id | sequence_id | value |
+-------------+-------------+-------+
|           1 |           1 |     1 |
|           1 |           2 |     2 |
|           1 |           3 |     3 |
|           1 |           4 |     4 |
|           1 |           5 |     5 |
|           2 |           1 |     8 |
|           2 |           2 |     2 |
|           2 |           3 |     8 |
|           2 |           4 |     3 |
|           2 |           5 |     5 |
|           3 |           1 |     1 |
|           3 |           2 |     5 |
|           3 |           3 |     3 |
|           3 |           4 |     4 |
|           3 |           5 |     5 |
|           4 |           1 |     1 |
|           4 |           2 |     2 |
|           4 |           3 |     6 |
|           4 |           4 |     4 |
|           4 |           5 |     5 |
+-------------+-------------+-------+

SELECT c.constant_id
     , SUM(v.value) 
  FROM constants c 
  JOIN variables v 
    ON v.constant_id = c.constant_id 
   AND v.sequence_id <= c.constant 
 GROUP 
    BY c.constant_id;
+-------------+--------------+
| constant_id | SUM(v.value) |
+-------------+--------------+
|           1 |            3 |
|           2 |           10 |
|           3 |            9 |
|           4 |            9 |
+-------------+--------------+