[英]SQL compare each value of column to all values of row from another table
我有兩個表StudentTable
和LevelsTable
。 我正在使用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名學生,我想得到這樣的結果:
您對解決方案有何建議?
使用純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-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;
輸出為:
+------+----+-----------+----------+
| 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.