簡體   English   中英

SQL將列的每個值與另一個表中的行的所有值進行比較

[英]SQL compare each value of column to all values of row from another table

我有兩個表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->很好。

您對解決方案有何建議?

使用純SQL,您可以這樣做:

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

更新資料

正如我在評論中提到的那樣,您的LevelsTable對於SQL來說不是很方便。 草莓公司在另一個答案中建議的表格是朝正確方向邁出的一步,但需要進行兩個更改:它需要多組級別,並且級別應為范圍,並帶有上下邊界。

對於多組范圍,您需要一列標識該組。 讓我們稱它為levelType ,並使其保持簡單易用,讓它成為命名該類型的文本列,例如'pushUps'

對於范圍邊界,一種方法是大寫和包容性的,例如0-910-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;

輸出為:

+------+----+-----------+----------+
| from | to |   level   | students |
+------+----+-----------+----------+
|  20  | 30 | average   |     1    |
|  30  | 40 | good      |     1    |
|  40  |    | very good |     1    |
+------+----+-----------+----------+

我不知道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 |
+-------------+-----------+--------------+-------+

更改您的水平表,使其看起來更像
id--金額--級別
1--10------veryWeak
2--15------弱

然后只需編寫一個簡單的查詢即可選擇正確的標簽

或者,您可以將levelstable放在java枚舉中,並在需要時選擇正確的標簽。

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

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

根據您的用例,可能不需要在表中定義這些級別。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM