SQL将列的每个值与另一个表中的行的所有值进行比较

时间:2015-09-01 09:38:43

标签: java mysql sqlite

我有两张表StudentTableLevelsTable。我使用的是Java和Sqlite。

我的第一张桌子:StudentTable

+----+-----------+-------------+-----------+---------------+
| idS| firstName | famillyName |  age      |     pushUps   |     
+----+-----------+-------------+-----------+---------------+
|  1 |     a     |      d      |    17     |     20        |
|  2 |     b     |      e      |    18     |     30        | 
|  3 |     c     |      f      |    19     |     50        | 
+----+-----------+-------------+-----------+---------------+  

我的第二张桌子:LevelsTable

+----+-----------+--------+-----------+--------+--------------+
| idP| veryWeak  | weak   |  average  |  good  |   veryGood   |
+----+-----------+--------+-----------+--------+--------------+
|  1 |     10    |   15   |    20     |   30   |     40       |
+----+-----------+--------+-----------+--------+--------------+

我想计算每个级别的学生人数,具体取决于每个级别的俯卧撑数量。 例如:如果我有1000名学生,我想得到这样的结果:

  • 100名学生∈[10,15 [ - >非常弱
  • 250名学生∈[15,20 [ - >弱
  • 400名学生∈[20,30 [ - >平均
  • 150名学生∈[30,40 [ - >好
  • 100名学生> 40 - >很好。

您对解决方案有何建议?

3 个答案:

答案 0 :(得分:1)

使用纯SQL,你可以这样做:

O(n * T * R)

<强>更新

正如我在评论中提到的,您的SELECT CASE x.studentLevel WHEN 0 THEN 'super-duper weak' WHEN 1 THEN 'very weak' WHEN 2 THEN 'weak' WHEN 3 THEN 'average' WHEN 4 THEN 'good' WHEN 5 THEN 'very good' END AS "level" , COUNT(*) AS "count" FROM ( SELECT CASE WHEN s.pushUps >= lvl.veryGood THEN 5 WHEN s.pushUps >= lvl.good THEN 4 WHEN s.pushUps >= lvl.average THEN 3 WHEN s.pushUps >= lvl.weak THEN 2 WHEN s.pushUps >= lvl.veryWeak THEN 1 ELSE 0 END AS studentLevel FROM StudentTable s , LevelsTable lvl WHERE lvl.idP = 1/*pushUps*/ ) x GROUP BY x.studentLevel ORDER BY x.studentLevel 对SQL来说不是很方便。 Strawberry 在另一个答案中建议的表是朝着正确方向迈出的一步,但需要两个变化:它需要多组水平,水平应该是范围,上下边界。

对于多组范围,您需要一个标识该组的列。我们称之为LevelsTable,并保持简单易用,让它成为命名该类型的文本列,例如levelType

对于范围边界,一种方式是低位和高位,例如'pushUps'0-9,依此类推。如果你的值可以是浮点数,那将不会起作用,因为10-19将介于范围之间,所以最好使边界包含更低且更高的范围,就像你在问题

如果需要,您可以保留9.5列,但不需要。

idP

现在,如果你想列出学生并展示他们的水平,那很简单:

CREATE TABLE LevelsTable (
    levelType   VARCHAR(30)  NOT NULL,
    lowerLevel  INTEGER      NOT NULL,
    upperLevel  INTEGER      NULL,
    levelDesc   VARCHAR(30)  NOT NULL,
    CONSTRAINT PK_LevelsTable PRIMARY KEY ( levelType, lowerLevel )
);

INSERT INTO LevelsTable VALUES ( 'pushUps',  0, 10  , 'pathetic'  );
INSERT INTO LevelsTable VALUES ( 'pushUps', 10, 15  , 'very weak' );
INSERT INTO LevelsTable VALUES ( 'pushUps', 15, 20  , 'weak'      );
INSERT INTO LevelsTable VALUES ( 'pushUps', 20, 30  , 'average'   );
INSERT INTO LevelsTable VALUES ( 'pushUps', 30, 40  , 'good'      );
INSERT INTO LevelsTable VALUES ( 'pushUps', 40, NULL, 'very good' );
INSERT INTO LevelsTable VALUES ( 'age'    ,  0, 13  , 'child'     );
INSERT INTO LevelsTable VALUES ( 'age'    , 13, 20  , 'teenager'  );
INSERT INTO LevelsTable VALUES ( 'age'    , 20, 55  , 'adult'     );
INSERT INTO LevelsTable VALUES ( 'age'    , 55, NULL, 'senior'    );

输出将是:

SELECT s.idS, s.firstName, s.famillyName
     , s.age, a.levelDesc AS ageLevel
     , s.pushUps, p.levelDesc AS pushUpLevel
  FROM StudentTable s
  JOIN LevelsTable a ON a.levelType = 'age'
                    AND a.lowerLevel <= s.age
                    AND (a.upperLevel > s.age OR a.upperLevel IS NULL)
  JOIN LevelsTable p ON p.levelType = 'pushUps'
                    AND p.lowerLevel <= s.pushUps
                    AND (p.upperLevel > s.pushUps OR p.upperLevel IS NULL)
 ORDER BY s.idS;

俯卧撑组计数的查询是:

+----+-----------+-------------+-----+----------+---------+-------------+
| idS| firstName | famillyName | age | ageLevel | pushUps | pushUpLevel |
+----+-----------+-------------+-----+----------+---------+-------------+
|  1 |     a     |      d      |  17 | teenager |    20   | average     |
|  2 |     b     |      e      |  18 | teenager |    30   | good        |
|  3 |     c     |      f      |  19 | teenager |    50   | very good   |
+----+-----------+-------------+-----+----------+---------+-------------+

输出将是:

SELECT lvl.lowerLevel AS "from", lvl.upperLevel AS "to"
     , lvl.levelDesc AS "level", COUNT(*) AS "students"
  FROM StudentTable s
  JOIN LevelsTable lvl ON lvl.levelType = 'pushUps'
                      AND lvl.lowerLevel <= s.pushUps
                      AND (lvl.upperLevel > s.pushUps OR lvl.upperLevel
 GROUP BY lvl.lowerLevel, lvl.upperLevel, lvl.levelDesc
 ORDER BY lvl.lowerLevel;

答案 1 :(得分:0)

我不知道sqlite,但MySQL中的方法可能看起来像这样......

DROP TABLE IF EXISTS student_pushups;

CREATE TABLE student_pushups
(student_id INT NOT NULL
,pushups INT NOT NULL
,PRIMARY KEY(student_id)
);

INSERT INTO student_pushups VALUES
(1,20),
(2,30),
(3,50);

DROP TABLE IF EXISTS pushup_levels;

CREATE TABLE pushup_levels
(pushup_quantity INT NOT NULL PRIMARY KEY,level VARCHAR(20) NOT NULL UNIQUE);

INSERT INTO pushup_levels VALUES
(0,'pathetic'),
(10,'very poor'),
(15,'poor'),
(20,'satisfactory'),
(30,'good'),
(40,'very good'),
(50,'excellent');

以下为我们提供了范围...

SELECT x.pushup_quantity range_start
     , MIN(COALESCE(y.pushup_quantity,1000))-1 range_end
     , x.level 
  FROM pushup_levels x 
  LEFT 
  JOIN pushup_levels y 
    ON y.pushup_quantity > x.pushup_quantity
 GROUP 
    BY range_start;
+-------------+-----------+--------------+
| range_start | range_end | level        |
+-------------+-----------+--------------+
|           0 |         9 | pathetic     |
|          10 |        14 | very poor    |
|          15 |        19 | poor         |
|          20 |        29 | satisfactory |
|          30 |        39 | good         |
|          40 |        49 | very good    |
|          50 |       999 | excellent    |
+-------------+-----------+--------------+

......我们可以加入我们的学生,给我们的结果......

SELECT a.*
     , COUNT(b.student_id) total
  FROM 
     ( SELECT x.pushup_quantity range_start
            , MIN(COALESCE(y.pushup_quantity,1000))-1 range_end
            , x.level 
         FROM pushup_levels x 
         LEFT 
         JOIN pushup_levels y 
           ON y.pushup_quantity > x.pushup_quantity
        GROUP 
           BY range_start
     ) a    
  LEFT 
  JOIN student_pushups b
    ON b.pushups BETWEEN a.range_start AND a.range_end
 GROUP 
    BY range_start;

+-------------+-----------+--------------+-------+
| range_start | range_end | level        | total |
+-------------+-----------+--------------+-------+
|           0 |         9 | pathetic     |     0 |
|          10 |        14 | very poor    |     0 |
|          15 |        19 | poor         |     0 |
|          20 |        29 | satisfactory |     1 |
|          30 |        39 | good         |     1 |
|          40 |        49 | very good    |     0 |
|          50 |       999 | excellent    |     1 |
+-------------+-----------+--------------+-------+

答案 2 :(得分:-1)

将您的levelstable更改为更像是 id - - 金额 - - 级别
1 - - 10 - - - - - - veryWeak
2 - - 15 - - - - - - 弱

然后只需编写一个简单的查询来选择正确的标签

或者您可以将levelstable放在java枚举中,只需在需要时选择正确的标签。

enum Level{
  VeryWeak(10),
  Weak(15);
  private Integer amount;

  Level(Integer amount) {
    this.amount= amount;
  }
 }

根据您的使用情况,可能不需要在表格中定义这些级别。