简体   繁体   中英

MySQL Select entire row from matching Group By value

Lets say I have 2 tables like so:

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

I am trying to get the row of the first (Timestamp) instance where the grade was X standard deviations away.

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

The 4th row should not be returned as its Standard Deviations Away was already found at a previous Timestamp.

I'm still fairly new to MySQL, but this is where I'm at so far:

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 );

I think the question is just about finding "first" row for each id/grade tupe. So (asssuming 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

Then, you can bring the second table with a join if you like:

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

In earlier versions, you can filter with a subquery:

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
)

In previous Versin and of Course in mysql8 as well you can do this.

this will exclude every testnum, that gas standard deviation,except the first one, for that user

Schema (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');

Query #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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM