Find Max number from unique values in SQL Stored Procedure - mysql

Hi I'm trying to get a unique list of ID values based on the latest (max) version number
My table looks like:
id Version Cost Name Status
---|-----|------------|--------|---------
35 | 1.0 | 200000 | john | Open
36 | 1.0 | 400000 | juliet | Open
35 | 2.0 | 350000 | borat | Closed
36 | 1.5 | 30000 | john | Waiting Update
I want it to be able to return
id Version Cost Name Status
---|-----|------------|--------|---------
35 | 2.0 | 350000 | borat | Closed
36 | 1.5 | 30000 | john | Waiting Update
I've tried using this but can't get it to work and I'm not sure where I'm going wrong
select FB.* from CSLL.Feedback FB
inner join (
select ID
,max(Version)
,Status as MaxID
from CSLL.Feedback
group by ID
) groupedID
ON FB.ID = groupedID.ID
AND FB.Version = groupedID.MaxID
and FB.Status = groupedID.Status
Which was based on a response to another question found here
LINKY
Can anyone help at all?
Many thanks in advance
Tom

Try this.
select fb1.* from Feedback fb1
LEFT JOIN Feedback fb2 on fb2.id=fb1.id and fb2.version>fb1.version
where fb2.id IS NULL;

Related

How to output a list by the results of two columns

I’ve a database where I store each product submitted by the curators, and there I register if it was approved. I need to generate a list where I show their score, ordered by the one who has more submitted (subm) and approved (appr). For that I need to get the approval rate (with the division of appr/subm) and we call it ar (Approval rate), and then I need a second operation to get the cs (Curator Score), which is the result of appr*(ar*ar).
The final output should be as the following:
| Curator | subm | appr| ar | cs |
------------------------------------------------
| 1 | 21 | 20 | 95.24% | 18.14058957 |
| 4 | 13 | 12 | 92.31% | 10.22485207 |
| 2 | 10 | 7 | 70.00% | 3.43 |
| 3 | 2 | 2 |100.00% | 2 |
To get the values from the table I use
SELECT curator, SUM(prop) subm, SUM(date) appr
FROM control
GROUP BY curator
ORDER BY cs
But I need to add somewhere:
SUM(appr/subm) ar, SUM(appr*(ar*ar)) cs
But I don’t know how to do this.
It's probably simplest to use your existing query as a subquery:
SELECT *, appr/subm AS ar, appr*(appr/subm*appr/subm)) AS cs
FROM (SELECT curator, SUM(prop) subm, SUM(date) appr
FROM control
GROUP BY curator) c
ORDER BY cs

"GROUP BY" on MariaDB behaves differently from MySQL

I have been told many times that same queries MariaDB will work just the same like how it is on MySQL... until I meet this problem.
Recently, I am trying to clone an application from MySQL(InnoDB) to MariaDB(XtraDB).
Although MariaDB runs MySQL queries without the need of changing anything, I was surprised to discover that the same queries actually behave quite differently on both platforms particularly in ORDER BY and GROUP BY.
For an example:
MyTable
=======
+----+----------+---------------------+-----------+
| id | parentId | creationDate | name |
+----+----------+---------------------+-----------+
| 1 | 2357 | 2017-01-01 06:03:40 | Anna |
+----+----------+---------------------+-----------+
| 2 | 5480 | 2017-01-02 07:13:20 | Becky |
+----+----------+---------------------+-----------+
| 3 | 2357 | 2017-01-03 08:20:12 | Christina |
+----+----------+---------------------+-----------+
| 4 | 2357 | 2017-01-03 08:20:15 | Dorothy |
+----+----------+---------------------+-----------+
| 5 | 5480 | 2017-01-04 09:25:45 | Emma |
+----+----------+---------------------+-----------+
| 6 | 1168 | 2017-01-05 10:30:10 | Fiona |
+----+----------+---------------------+-----------+
| 7 | 5480 | 2017-01-05 10:33:23 | Gigi |
+----+----------+---------------------+-----------+
| 8 | 1168 | 2017-01-06 12:46:34 | Heidi |
+----+----------+---------------------+-----------+
| 9 | 1168 | 2017-01-06 12:46:34 | Irene |
+----+----------+---------------------+-----------+
| 10 | 2357 | 2017-01-07 14:58:37 | Jane |
+----+----------+---------------------+-----------+
| 11 | 2357 | 2017-01-07 14:58:37 | Katy |
+----+----------+---------------------+-----------+
Basically what I want to get from a query is the latest records from each GROUPing (i.e. parentId). By latest, I mean MAX(creationDate) and MAX(id)
So, for the above example, since there are only three different parentId values, I am hoping to get:
+----+----------+---------------------+-----------+
| id | parentId | creationDate | name |
+----+----------+---------------------+-----------+
| 11 | 2357 | 2017-01-07 14:58:37 | Katy |
+----+----------+---------------------+-----------+
| 9 | 1168 | 2017-01-06 12:46:34 | Irene |
+----+----------+---------------------+-----------+
| 7 | 5480 | 2017-01-05 10:33:23 | Gigi |
+----+----------+---------------------+-----------+
Originally the application has queries similar to this fashion:
SELECT * FROM
( SELECT * FROM `MyTable` WHERE `parentId` IN (...)
ORDER BY `creationDate` DESC, `id` DESC ) AS `t`
GROUP BY `parentId`;
On MySQL, this works, since the inner query will order and then the outer query gets the first of each GROUP from the result of the inner query. The outer query basically obeys ordering of the inner query.
But on MariaDB, the outer query will ignore the ordering of the inner query result. I get this on MariaDB instead:
+----+----------+---------------------+-----------+
| id | parentId | creationDate | name |
+----+----------+---------------------+-----------+
| 1 | 2357 | 2017-01-01 06:03:40 | Anna |
+----+----------+---------------------+-----------+
| 2 | 5480 | 2017-01-02 07:13:20 | Becky |
+----+----------+---------------------+-----------+
| 6 | 1168 | 2017-01-05 10:30:10 | Fiona |
+----+----------+---------------------+-----------+
To achieve the same behaviour on MariaDB, I have come up with something like this. (Not sure if this is accurate though.)
SELECT `t1`.* FROM `MyTable` `t1` LEFT JOIN `MyTable` `t2` ON (
`t1`.`parentId` = `t2`.`parentId`
AND `t2`.`parentId` IN (...)
AND `t1`.`creationDate` <= `t2`.`creationDate`
AND `t1`.`id` < `t2`.`id`)
) WHERE `t2`.`id` IS NULL;
Now the problem is... If I am going to rewrite the queries, I have to rewrite hundreds of them... and they are some how a little bit different from each other.
I wonder if anyone here have any ideas that would allow me to make the least changes possible.
Thank you all in advance.
Yeah, this is a link-only answer. But the links are to the MariaDB site.
Here is another discussion of the 'incompatibility': https://mariadb.com/kb/en/mariadb/group-by-trick-has-been-optimized-away/
Technically, speaking, MySQL implemented an extension to the the Ansi standard. Much later, it decided to remove it, so I think you will find that MySQL has migrated toward MariaDB.
Here is list of "fast" ways to do group-wise max, which is probably what you are trying to do: https://mariadb.com/kb/en/mariadb/groupwise-max-in-mariadb/
Your first query would probably work in MySQL but its behavior is not documented: you are grouping by groupid but you are selecting non-aggregated columns with * and the value of any of those non-aggregated columns is undefined - if the value you get is the first value encountered it's just a "matter of luck".
It is true that, even if it cannot be considered correct, on MySQL I have never seen this "trick" fail (and here on stackoverflow there are plenty of upvoted answers suggesting you to use this trick), but MariaDB uses a different optimization engine and you cannot rely on MySQL undocumented behavior.
Your second query needs a little adjustment:
and (
`t1`.`creationDate` < `t2`.`creationDate`
or (
`t1`.`creationDate` = `t2`.`creationDate`
and `t1`.`id` < `t2`.`id`
)
)
because first you are ordering by creation date, then if more than one record share the same creation date you are getting the one with the highest id.
There are other ways to write the same query, e.g.
select * from mytable
where id in (
select max(m.id)
from mytable m inner join (
select parentID, max(creationDate) as max_cd
from mytable
group by ParentID
) t on m.parentID = t.parentID and m.creationDate = t.max_cd
group by m.parentID, m.creationDate
)
but every query needs to be rewritten separately.
Edit
Your example is a little more complicated because you are ordering by both creationDate and id. Let me explain better. First thing to do, for every parentID you have to get the last creationDate:
select parentID, max(creationDate) as max_cd
from MyTable
group by parentID
then for every max creationDate you have to get the highest id:
select t.parentID, t.max_cd, max(t.id) as max_id
from
MyTable t inner join (
select parentID, max(creationDate) as max_cd
from MyTable
group by parentID
) t1 on t.parentID = t1.parentID and t.creationDate = t1.max_cd
group t.parentID, t.max_cd
then you have to get all records where the id are returned by this query. In this particular context a LEFT JOIN with the table itself should be easier to write and more performant.

MySQL - How to subtract two datetime from the same table

I need help on a small problem with a subtraction in the same table and column
Well, iam creating a view, but the aplication generated the results of used time in tha same table and column.
My table have the following columns: id,field_id,object_id and value_date.
| ID | FIELD_ID | OBJECT_ID | VALUE_DATE |
| 55 | 4 | 33 | 2016-12-18 19:02:00 |
| 56 | 5 | 33 | 2016-12-18 19:12:00 |
| 57 | 4 | 35 | 2016-12-18 19:30:00 |
| 58 | 5 | 35 | 2016-12-18 20:00:00 |
I do not have much knowledge in sql, but i have tried some functions like timestampdiff, period_siff and others examples in stackoverflow.com.
Someone help me to subtract ID 56 with field_id 5 by line with ID 55 and field_id 4 in object_id 33 in SQL to bring the result in minutes. Ex: 10 or 00:10:00
An article about this problem would already help me. Thank you very much!
Lets assume that you want result to be in day format then query will be :
SELECT DATEDIFF(day,startDate,endDate) AS 'Day'
FROM table1;
Find complete example here
The soluction is below:
select TIMESTAMPDIFF(MINUTE,F1.value_date,F2.value_date) as minutes, F1.value_date,F2.value_date,F1.object_id,F2.object_id,F1.field_id,F2.field_id
from otrs_tst.dynamic_field_value F1
join otrs_tst.dynamic_field_value F2 on F1.object_id = F2.object_id
where F1.field_id in ('4','5')
and F2.field_id in ('4','5')
and F2.field_id <> F1.field_id
and F1.field_id < F2.field_id
group by F1.object_id,F2.field_id

Grouping query by similar items in a row

How can I group all items next to each other returned from a query.
It's difficult to explain so best if I just provide an example.
I have a database called UserActions with two columns and the following data:
ID | User | Action
1 | Mark | Jump
2 | Mark | Jump
3 | Mark | Jump
4 | Mark | Run
5 | Mark | Run
6 | John | Run
7 | John | Run
8 | Mark | Run
9 | Mark | Run
10 | Mark | Jump
11 | Mark | Jump
12 | John | Jump
13 | John | Jump
The output I want is this:
Last ID | User | Action | Count
12 | John | Jump | 2
10 | Mark | Jump | 2
8 | Mark | Run | 2
6 | John | Run | 2
4 | Mark | Run | 2
1 | Mark | Jump | 3
Basically it groups all items by the user and action and outputs the total count before the next row is either a different action or user. If I do regular group by using "annotate" it will just group all items.
Is there a way to do this using a Django Query or raw SQL?
Thanks,
Mark
SELECT Max(ID),Count([Action]) AS [Count], [User], [Action]
FROM #Table1
GROUP BY [User],[Action]
The above query will yield the desired output.
The Output generated is:
LastID User Action Count
13 John Jump 2
11 Mark Jump 5
7 John Run 2
9 Mark Run 4
Hope it helps
In django ORM:
(Model.objects.values('user', 'action')
.order_by()
.annotate(max_id=models.Max('id'),
count=models.Count('action')))
Please note the empty .order_by(). It's needed in order to override one declared in Meta. Django includes default ordering field in GROUP BY fields.
SELECT x.*
FROM my_table x
LEFT
JOIN my_table y
ON y.user = x.user
AND y.action = x.action
AND y.id = x.id - 1
WHERE y.id IS NULL;
This assumes contiguous incremental ids, as per the example, but it's trivial to rewrite it if that's not the case.

Self join and recursive selection in a table

Assuming a table as below
| ID | NAME | ROLE | MGRID |
---------------------------
| 1 | ONE | 5 | 5 |
| 2 | TWO | 5 | 5 |
| 3 | THREE | 5 | 6 |
| 4 | FOUR | 5 | 6 |
| 5 | FIVE | 15 | 7 |
| 6 | SIX | 25 | 8 |
| 7 | SEVEN | 25 | 7 |
| 8 | EIGHT | 5 | 8 |
How do I get a list of all employees reporting to an employee, including the ones who are in subsequent reporting levels below?
I mean, given emp id 5, I should get [1, 2] and given 7, I should get [1, 2, 5, 7]. How do I get this done?
Will self joins be of help here? Need to brush up my knowledge on joins now.
SELECT id
FROM emp
START WITH id = 7
CONNECT BY NOCYCLE mgrid = PRIOR id
SQLFIDDLE LINK
Here is a SQL statement using Oracle.
select id, name, role, mgrID
from employees
start with id = 7
connect by NoCycle prior id = mgrid;
Please note that the manager for employee 7 is the employee 7 - they are their own manager. This will cause an error - "Connect By loop in user data'. By using the NoCycle keyword you can tell Oracle to detect this and avoid the error.
Does this solve your issue?
I know this isn't exactly what you were asking, but if you are willing to choose a finite number of level's to recurse it isn't too bad to write.
SELECT table_2.id
FROM table LEFT JOIN
(table AS table_1 LEFT JOIN table AS table_2 ON table_1.id = table_2.MgrID)
ON table.id = table_1.MgrID
WHERE (((table.id)=7));
ETC.