MySQL Select 匹配分组依据值的整行

MySQL Select entire row from matching Group By value

假设我有 2 个这样的表:

MyTable1:
Name    ID      Timestamp            TestNum    Grade
Alex    1101    2020-10-01 12:00:00  1          85
Alex    1101    2020-10-02 13:00:00  2          90
Alex    1101    2020-10-03  8:00:00  3          95
Alex    1101    2020-10-04 10:00:00  4          90

MyTable2:
ID      Avg  StDev
1101    90   4.08

我正在尝试获取第一个(时间戳)实例的行,其中成绩相差 X 个标准差。

ExpectedResults:
Name    ID      Timestamp            TestNum    StDevsAway
Alex    1101    2020-10-01 12:00:00  1          -1.23
Alex    1101    2020-10-02 13:00:00  2          0
Alex    1101    2020-10-03  8:00:00  3          1.23

不应返回第 4 行,因为它的 Standard Deviations Away 已经在之前的时间戳中找到。

我对 MySQL 还是很陌生,但这是我目前所处的位置:

select a.Name
     , a.ID
     , a.Timestamp
     , a.TestNum
     , round( ( a.Grade - b.Avg ) / b.StDev, 2 ) as StDevsAway 
  from MyTable1 as a 
  join MyTable2 as b 
    on a.ID = b.ID 
 group 
    by round( ( a.Grade - b.Avg ) / b.StDev, 2 );

我认为问题只是为每个 id/grade tupe 找到“第一”行。所以(假设 MySQL 8.0):

select t1.*
from (
    select t1.*, row_number() over(partition by id, grade order by timestamp) rn
    from mytable1 t1
) t1
where rn = 1

然后,如果您愿意,可以将第二个 table 带入连接:

select t1.*, round(t1.grade - t2.avg) / t2.stdev, 2) stdevsaway
from (
    select t1.*, row_number() over(partition by id, grade order by timestamp) rn
    from mytable1 t1
) t1
inner join mytable2 t2 on t2.id = t1.id
where rn = 1

在早期版本中,您可以使用子查询进行过滤:

select t1.*, round(t1.grade - t2.avg) / t2.stdev, 2) stdevsaway
from mytable1 t1
inner join mytable2 t2 on t2.id = t1.id
where t1.timestamp = (
    select min(t11.timestamp) from mytable1 t11 where t11.id = t1.id and t11.grade = t1.grade
)

在以前的 Versin 中,当然在 mysql8 中你也可以这样做。

这将排除该用户的每个测试编号,即 gas 标准偏差,第一个除外

架构 (MySQL v5.5)

CREATE TABLE MyTable1 (
  `Name` VARCHAR(4),
  `ID` INTEGER,
  `Timestamp` DATETIME,
  `TestNum` VARCHAR(7),
  `Grade` INTEGER
);

INSERT INTO MyTable1
  (`Name`, `ID`, `Timestamp`, `TestNum`, `Grade`)
VALUES
  ('Alex', '1101', '2020-10-01 12:00:00', '1', '85'),
  ('Alex', '1101', '2020-10-02 13:00:00', '2', '90'),
  ('Alex', '1101', '2020-10-03 08:00:00', '3','95'),
  ('Alex', '1101', '2020-10-04 10:00:00', '4', '90');
  
  CREATE TABLE MyTable2 (
  `ID` INTEGER,
  `Avg` INTEGER,
  `StDev` FLOAT
);

INSERT INTO MyTable2
  (`ID`, `Avg`, `StDev`)
VALUES
  ('1101', '90', '4.08');

查询 #1

select 
    a.Name
    , a.ID
    , a.Timestamp
    , a.TestNum
    , round( ( a.Grade - b.Avg ) / b.StDev, 2 ) as StDevsAway 
from MyTable1 as a join MyTable2 as b on a.ID = b.ID 
WHERE 
TestNum NOT IN (SELECT TestNum
           FROM MyTable1 c
           WHERE c.`ID` = a.`ID` 
               AND c.`Grade` = b.Avg
           AND c.`TestNum`<> (SELECT MIN(TestNum)
                         FROM MyTable1 d
                          WHERE d.`ID` = a.`ID` 
                           AND d.`Grade` = b.Avg)
           );

| Name | ID   | Timestamp           | TestNum | StDevsAway |
| ---- | ---- | ------------------- | ------- | ---------- |
| Alex | 1101 | 2020-10-01 12:00:00 | 1       | -1.23      |
| Alex | 1101 | 2020-10-02 13:00:00 | 2       | 0          |
| Alex | 1101 | 2020-10-03 08:00:00 | 3       | 1.23       |

View on DB Fiddle