Cannot print out the latest results of table - mysql

I have the following table:
NAMES:
Fname | stime | etime | Ver | Rslt
x 4 5 1.01 Pass
x 8 10 1.01 Fail
x 6 7 1.02 Pass
y 4 8 1.01 Fail
y 9 10 1.01 Fail
y 11 12 1.01 Pass
y 10 14 1.02 Fail
m 1 2 1.01 Fail
m 4 6 1.01 Fail
The result I am trying to output is:
x 8 10 1.01 Fail
x 6 7 1.02 Pass
y 11 12 1.01 Pass
y 10 14 1.02 Fail
m 4 6 1.01 Fail
What the result means:
Fnames are an example of tests that are run. Each test was run on different platforms of software (The version numbers) Some tests were run on the same platform twice: It passed the first time and failed the second time or vice versa. My required output is basically the latest result of each case for each version. So basically the results above are all unique by their combination of Fname and Ver(sion), and they are selected by the latest etime from the unique group.
The query I have so far is:
select Fname,stime,max(etime),ver,Rslt from NAMES group by Fname,Rslt;
This however, does not give me the required output.
The output I get is (wrong):
x 4 10 1.01 Fail
x 6 7 1.02 Pass
y 4 12 1.01 Pass
y 10 14 1.02 Fail
m 1 6 1.01 Fail
Basically it takes the max time, but it does not really print the correct data out, it prints the max time, but it prints the initial time of the whole unique group of data, instead of the initial time of that particular test (record).
I have tried so long to fix this, but I seem to be going no where. I have a feeling there is a join somewhere in here, but I tried that too, no luck.
Any help is appreciated,
Thank you.

Use a subquery to get the max ETime by FName and Ver, then join your main table to it:
SELECT
NAMES.FName,
NAMES.STime,
NAMES.ETime,
NAMES.Ver,
NAMES.Rslt
FROM NAMES
INNER JOIN (
SELECT FName, Ver, MAX(ETime) AS MaxETime
FROM NAMES
GROUP BY FName, Ver
) T ON NAMES.FName = T.FName AND NAMES.Ver = T.Ver AND NAMES.ETime = T.MaxETime

You could first find which is the latests=max(etime) for each case for each version ?
select Fname,Ver,max(etime) from NAMES group by Fname,Ver;
From there you would display the whole thing via joining it again?
select *
from
NAMES
inner join
(select Fname,Ver,max(etime) as etime from NAMES group by Fname,Ver ) sub1
using (Fname,Ver,etime)
order by fname,Ver;

Related

SELECT TOP PERCENT, VaR, Expected Shortfall in MySQL

I would like to achieve SELECT TOP PERCENT in MySQL.
I used Victor Sorokin's idea in Select TOP X (or bottom) percent for numeric values in MySQL, and got the following query:
SELECT x.log AS Login,
AVG(x.PROFIT) AS 'Expected Shortfall',
MAX(x.PROFIT) AS '40%VaR'
FROM
(SELECT t.PROFIT,
#counter := #counter +1 AS counter,
t.LOGIN AS log
FROM (SELECT #counter:=0) initvar, trades AS t
WHERE t.LOGIN IN (100,101)
ORDER BY t.PROFIT) AS x
WHERE x.counter <= (40/100 * #counter)
GROUP BY x.log
Which return the following result:
Login
Expected Shortfall
40%VaR
101
-85
-70
This works when I change WHERE t.LOGIN IN (100,101) to a single value like WHERE t.LOGIN=100. Whereby it will return me values for each login as following:
Login
Expected Shortfall
40%VaR
100
-4.5
-4
Login
Expected Shortfall
40%VaR
101
-95
-90
I'm not really sure what is happening and I was wondering if there is a way to use the query for multiple accounts or there is a better way to solve the issue? Was thinking of a LOOP statement?
I'm currently using MySQL version 5.7.34. Please do not hesitate to let me know if any clarification is needed. Any ideas would be much appreciated!
Edit: To replicate the issue:
CREATE TABLE trades (
TICKET int(11) PRIMARY KEY,
LOGIN int(11),
PROFIT double)
INSERT INTO trades (TICKET,LOGIN,PROFIT)
VALUES
(1,100,-5),
(2,100,-4),
(3,100,-3),
(4,100,-2),
(5,100,-1),
(6,101,-100),
(7,101,-90),
(8,101,-80),
(9,101,-70),
(10,101,-60),
(11,101,-50),
(12,101,500)
The expected output is just like the outputs you would get if you ran the query for 100 and 101 separately:
Expected Output
LOGIN
ES
40%VAR
100
-4.5
-4
101
-95
-90
Expected Output
The reason why the end result was not according to the single value queries was caused by the #row_number assignment. Taking the base query (the subquery) to run alone will return the following results:
PROFIT
counter
log
-100
1
101
-90
2
101
-80
3
101
-70
4
101
-60
5
101
-50
6
101
-5
7
100
-4
8
100
-3
9
100
-2
10
100
-1
11
100
500
12
101
As you can see, the counter value that was generated using #row_number is giving a running number for all of the data in the table regardless of it's log value. The result below shows the differences with query that using a single log value:
PROFIT
counter
log
-5
1
100
-4
2
100
-3
3
100
-2
4
100
-1
5
100
Here you can see that if using log=100, you'll get a counter (#row_number) generated from 1-5 as opposed to it being generated from 7-11 in the combined log IN (100,101). This is why WHERE x.counter <= (40/100*v.ctr) in the final query only take log=101 because it's the only one matches the condition. What you're looking for is a counter value separated by log. On MySQL 8.0+ (or MariaDB 10.2+) that support window function, this can be done by using ROW_NUMBER(). However, since OP is using an older version, I found a way to emulate the functionality of ROW_NUMBER() accordingly.
This is the final query generated:
SELECT x.log AS Login,
AVG(x.PROFIT) AS 'Expected Shortfall',
MAX(x.PROFIT) AS '40%VaR'
FROM
(SELECT t.PROFIT,
#row_number:=CASE
WHEN #id = LOGIN THEN #row_number + 1
ELSE 1 END AS counter,
#id:=LOGIN ID, t.LOGIN AS log
FROM trades t
CROSS JOIN (SELECT #id:=0,#row_number:=0) as n
ORDER BY LOGIN) AS x
JOIN (SELECT Login,COUNT(*) ctr FROM trades GROUP BY login) AS v
ON x.log=v.login
WHERE x.counter <= (40/100*v.ctr)
GROUP BY x.log
ORDER BY x.log;
And here is the demo fiddle (inclusive of ROW_NUMBER()) on MySQL 8.0+ query.

MySQL WHERE RAND() Behaviour

Consider the following table foo:
a b v
0 9 1
10 19 2
20 29 3
30 39 4
40 49 5
50 59 6
60 69 7
70 79 8
80 89 9
90 100 10
a and b are the lower and upper boundaries of a certain value v. Example:
x = 79 => v = 8
This can be done with the following statement:
SELECT `v`
FROM `foo`
WHERE 79 BETWEEN `a` AND `b`
Which MySQL correctly returns:
v
8
For each number between 0 and 100 provided as input, MySQL will correctly return one, and only one number between 1 and 10.
Issue
However, if the input number is substituted with a random number generator, the behaviour is somewhat different:
SELECT `v`
FROM `foo`
WHERE ROUND(RAND()*100) BETWEEN `a` AND `b`
Instead of returning only one number, MySQL might return anything from empty result set up to 3 numbers!
Question 1
Is this the expected behaviour of the RAND() statement? What is the reasoning for this apparently weird behavior?
Question 2
Considering the intended purpose, is the statement correct? What would be the correct one? How to correct this behaviour?
An ansi-sql-friendly solution with a single query would look like
SELECT x.rnd, `v`
FROM yourTable y
INNER JOIN (SELECT RAND()*100 rnd) x
WHERE x.rnd BETWEEN y.`a` AND y.`b`;
it generates a random value just once and then is used in a joined query.
Demo: http://rextester.com/YOIW49684
The query and the base table are kindly borrowed from Tim Biegeleisen
If you want the same random value to be applied to every row of the query, then one option is to use a session variable:
SET #rnd = RAND()*100;
SELECT v
FROM foo
WHERE ROUND(#rnd) BETWEEN a AND b;
Demo

Using DATEDIFF to ignore negative values in MYSQL

I have a requirement where I need to check the sent date of various email threads against date column in another table and determine version number if email sent date occurs after the date specified in the table. I used datediff() for this but I get negative values. I can't use ABS() because it doesn't make sense here.Is there any way I can get the desired result? A sample of the query I am using is
select distinct
x.OPTENTION_DATE,
email.ID,
x.NAME,
email.SEND_DATE,
DATEDIFF(email.SEND_DATE, x.OPTENTION_DATE) AS DT_DIFF
from
classification_version x,
classification_element y,
email
where
x.id_project = y.ID_PROJECT and
x.ID_PROJECT = email.ID_PROJECT_AMENDMENT and
y.ID_PROJECT = 11 and
y.ID_COMPANY=1
order by
email.SEND_DATE ASC
The output for this query is
OPTENTION_DATE ID NAME SEND_DATE DT_DIFF
2014-11-05 3 Version 2 2014-01-13 14:09:34 -296
2015-02-18 3 Version 3 2014-01-13 14:09:34 -401
2014-01-09 3 Version 1 2014-01-13 14:09:34 4
2014-11-05 62 Version 2 2015-01-12 18:46:10 68
2015-02-18 62 Version 3 2015-01-12 18:46:10 -37
2014-01-09 62 Version 1 2015-01-12 18:46:10 368
2014-11-05 61 Version 2 2015-01-19 20:50:09 75
2015-02-18 61 Version 3 2015-01-19 20:50:09 -30
2014-01-09 61 Version 1 2015-01-19 20:50:09 375
My desired output is for email id 3 version 1 should be selected,for email id 62 version 2 should be selected. If I use ABS() and then MIN() version 3 will be selected which is wrong because the send date is before the actual date.Can anyone suggest how to solve this?
You can use email.SEND_DATE > x.OPTENTION_DATE condition in WHERE clause and use MIN(DATEDIFF(email.SEND_DATE, x.OPTENTION_DATE)) and GROUP BY ID. It will find minimum positive value for each id.
Try below query :
select x.OPTENTION_DATE,
email.ID,
x.NAME,
email.SEND_DATE,
MIN(DATEDIFF(email.SEND_DATE, x.OPTENTION_DATE)) AS DT_DIFF
from
classification_version x,
classification_element y,
email
where
x.id_project = y.ID_PROJECT
and x.ID_PROJECT = email.ID_PROJECT_AMENDMENT
and y.ID_PROJECT = 11
and y.ID_COMPANY=1
and email.SEND_DATE > x.OPTENTION_DATE
group by
ID
order by
email.SEND_DATE ASC

sql query to extract rows

table:
id user_id guessed_id result
1219 27 4 Y
1357 20 4 Y
2 3 5 N
9 20 5 N
1392 20 11 Y
1618 27 11 N
2471 20 25 Y
I would like to build a query that fetches only the guessed_ids that have exactly 2 result with the value Y.
In the above rows the query should return:
4
since the rows containing the guessed_id 4 have both Y in the column result.
guessed_id 5 should not be returned since it contains 2 N
11 should not be returned since it has a N
25 should not be returned since it has only one Y
I am using mysql DBMS.
A GROUP BY/HAVING will do what you want, just count the number of Y's and return the guessed_ids where the count is 2;
SELECT guessed_id
FROM myTable
WHERE result='Y'
GROUP BY guessed_id
HAVING COUNT(*)=2
An SQLfiddle to test with.
I'm not sure from your example whether any N results are allowed for a hit, I assumed they're not relevant as long as there are exactly 2 rows with a Y.

MySQL: Matching inexact values using "ON"

I'm way out of my league here...
I have a mapping table (table1) to assign particular values (value) to a whole number (map_nu). My second table (table2), is a collection of averages (avg) for each user (user_id).
(I couldn't figure out how to properly make a markdown table, please feel free to edit!)
table1: table2:
(value)(Map_nu) (user_id)(avg)
---- -----
1 1 1 1.111
1.045 2 2 1.2
1.09 3 3 1.33333
1.135 4 4 1
1.18 5 5 1.389
1.225 6 6 1.42
1.27 7 7 1.07
1.315 8
1.36 9
1.405 10
The value Map_nu is a special number that each user gets assigned according to their average. I need to find a way to match the averages from table2 to the closest value in table1. I only need to match to the 2 digit past the decimal, so I've added the Truncated function
SELECT table2.user_id, map_nu
FROM `table1`
JOIN table2 ON TRUNCATE(table1.value,2)=TRUNCATE(table2.avg,2)
I still miss the values that don't match the averages exactly. Is there a way to pick the nearest truncated value or even to round to the second decimal? Rounding up/down wont matter as long as its applied to all values the same.
I am trying to have the following result (if rounded up):
(user_id)(Map_nu)
----
1 4
2 6
3 6
4 1
5 10
6 11
7 3
Thanks!
i think you might have to do this in 2 separate queries. there is no 'nearest' operator in sql, so you can either calculate it in your software, or you could use
select map_nu from table1 ORDER BY abs(value - $avg) LIMIT 1
inside a loop. however, that cannot be used as a join function as it requires the ORDER and LIMIT which are not valid as joins.
another way of looking at it is it seems that your map_nu and value are deterministic in relation to each other - value = 1 + ((map_nu - 1) * 0.045) - so maybe you could make use of that fact and calculate an integer based on that equation? assuming that relationship holds true for all values of map_nu.
This is an awkward database design. What is the data representing and what are you trying to solve? There might be a better way.
Maybe do something like...
SELECT a.user_id, b.map_nu, abs(a.avg - b.value)
FROM
table2 a
join table1 b
left join table1 c on abs(a.avg - b.value) > abs(a.avg - c.value)
where c.value is null
order by a.user_id
Doesn't actually produce the same output as the one you were expecting for (doesn't do any rounding). Though you should be able to tweak it from there. Above query will produce the output below (w/ data you've provided):
user_id map_nu abs(a.avg - b.value)
------- ------ --------------------
1 3 0.0209999999999999
2 5 0.02
3 8 0.01833
4 1 0
5 10 0.016
6 10 0.0149999999999999
7 3 0.02
Beware though if you're dealing with large tables. Evaluate the explain of the above query if it'll be practical to run it within MySQL or if better to be done outside it.
Note 2: Will produce duplicate rows if there are avg values that are equi-distant to value values within table1 (Ex. if value for map_nu's 11 and 12 are 2 and 3 and someone get's an avg of 2.5). Your question doesn't really specify what to do for that so you might want to take that into account.
Its taking a little extra work, but I figure the easiest way to get my results will be to map all values to the second decimal place in table1:
1 1
1.01 1
1.02 1
1.03 1
1.04 1
1.05 2
1.06 2
1.07 2
1.08 2
1.09 3
1.1 3
1.11 3
1.12 3
1.13 3
1.14 4
...
Thanks for the suggestions! Sorry I couldn't present the question more clear.