I have the following tables:
Apps
TYPE_ID | BUILD_ID | CONFIG_ID | VERSION_ID | (All foreign keys to the respective tables)
1 | 1 | 1 | 1 |
1 | 1 | 1 | 2 |
2 | 2 | 3 | 3 |
2 | 2 | 3 | 4 |
Versions
ID | major | minor | patch
1 | 1 |0 |1
2 | 2 |0 |0
3 | 3 |0 |3
4 | 4 |0 |0
I need to select highest version rows from Apps table for each unique combinations of TYPE_ID, BUILD_ID and CONFIG_ID.
The version number should be calculated by MAX(major * 1000000 + minor * 1000 + patch) in the versions table.
So from the given example of the Apps table the result would be:
TYPE_ID | BUILD_ID | CONFIG_ID | VERSION_ID |
1 | 1 | 1 | 2 |
2 | 2 | 3 | 4 |
Have tried something like this:
SELECT p1.* FROM Apps p1
INNER JOIN (
SELECT max(VERSION_ID) MaxVersion, CONFIG_ID
FROM Apps
GROUP BY CONFIG_ID
) p2
ON p1.CONFIG_ID = p2.CONFIG_ID
AND p1.VERSION_ID = p2.MaxVersion
GROUP BY `TYPE_ID`, `BUILD_ID`, `CONFIG_ID`
But MAX is applied on the VERSION_ID and I need MAX to be applied on major, minor and patch combinations.
MySQL Version 15.1 distribution 5.5.56-MariaDB
Any help would be appreciated.
Cheers!
You can compute the maximum version per type_id, build_id, config_id using the formula described in your question, use it again same formula to locate the version:
SELECT sq.type_id, sq.build_id, sq.config_id, versions.id AS version_id_max
FROM (
SELECT type_id, build_id, config_id, MAX(major * 1000000 + minor * 1000 + patch) AS max_version
FROM apps
INNER JOIN versions ON apps.version_id = versions.id
GROUP BY type_id, build_id, config_id
) sq
INNER JOIN versions ON max_version = major * 1000000 + minor * 1000 + patch
+---------+----------+-----------+----------------+
| type_id | build_id | config_id | version_id_max |
+---------+----------+-----------+----------------+
| 1 | 1 | 1 | 2 |
| 2 | 2 | 3 | 4 |
+---------+----------+-----------+----------------+
Try this :
select type_id, build_id, config_id,
max(1000000*v.major+1000*v.minor+v.patch) as version
from apps a left join versions v on a.version_id=v.id
group by type_id, build_id, config_id
Utilizing Nested Derived subqueries, and a bit of hacky way of identifying VERSION_ID corresponding to MAX VERSION_NO.
We basically first get a derived table determining the VERSION_NO for each row in the Apps table.
Now using that derived table as a source for SELECT, we group by on the TYPE_ID, BUILD_ID and CONFIG_ID, and using a GROUP_CONCAT and string manipulation based trick, we determine the VERSION_ID corresponding to maximum VERSION_NO, for a group.
Try the following:
SELECT nest.TYPE_ID,
nest.BUILD_ID,
nest.CONFIG_ID,
SUBSTRING_INDEX(GROUP_CONCAT(DISTINCT nest.VERSION_ID
ORDER BY nest.VERSION_NO DESC
SEPARATOR ','), ',', 1) AS VERSION_ID
FROM (
SELECT A.TYPE_ID,
A.BUILD_ID,
A.CONFIG_ID,
A.VERSION_ID,
(V.major*1000000 + V.minor*1000 + V.patch) AS VERSION_NO
FROM Apps AS A
INNER JOIN Versions AS V ON V.ID = A.VERSION_ID
) AS nest
GROUP BY nest.TYPE_ID, nest.BUILD_ID, nest.CONFIG_ID
SQL FIDDLE
Try this:
SELECT a1.type_id, a1.build_id, a1.config_id, a1.version_id
FROM apps a1
WHERE NOT EXISTS(
(SELECT 'NEXT'
FROM apps a2
WHERE a2.type_id = a1.type_id
AND a2.build_id = a1.build_id
AND a2.config_id = a1.config_id
AND a2.version_id > a1.version_id))
Try this query, what I do here, I imitate well-known function ROW_NUMBER() OVER (PARTITION BY Type_id, Build_id, Config_id ORDER BY major desc, minor desc, patch desc).
select #type_id_lag := 0, #build_id_lag :=0, #config_id_lag := 0, #rn := 0;
select type_id, build_id, config_id, major, minor, patch from (
select case when #type_id_lag = type_id and
#build_id_lag = build_id and
#config_id_lag = config_id then #rn := #rn + 1 else #rn := 1 rn,
#type_id_lag := type_id type_id,
#build_id_lag := build_id build_id,
#config_id_lag := config_id config_id,
v.major, v.minor, v.patch
from Apps a
left join Versions v on a.version_id = v.id
order by a.type_id, a.build_id, a.config_id,
v.major desc, v.minor desc, v.patch desc
) a where rn = 1;
Related
I am trying the below code, which analyses and scores customers based on recency, frequency and monetary value of transactions.
select customer_id, rfm_recency, rfm_frequency, rfm_monetary
from
(
select customer_id,
ntile(4) over (order by last_order_date) as rfm_recency,
ntile(4) over (order by count_order) as rfm_frequency,
ntile(4) over (order by sum_amount) as rfm_monetary
from
(
select customer_id,
max(local_date) as last_order_date,
count(*) as count_order,
sum(amount) as sum_amount
from transaction
group by customer_id) as T
) as P
However ntile is not available in my MySQL version (v5) as apparently it's a "window function" which works on v8+ only.
I can't find a working alternative to this function. I am very new to SQL so I'm having a hard time figuring it out myself.
Is there an ntile alternative that I can use? The code works fine if i remove the ntile segment.
You should really upgrade to MySQL 8.0 if you need features in MySQL 8.0. They are bound to be easier and more optimized.
I found a way to simulate the ntile query shown in the documentation:
SELECT
val,
ROW_NUMBER() OVER w AS 'row_number',
NTILE(2) OVER w AS 'ntile2',
NTILE(4) OVER w AS 'ntile4'
FROM numbers
WINDOW w AS (ORDER BY val);
Here's a solution:
SELECT val, #r:=#r+1 AS rownum,
FLOOR((#r-1)*2/9)+1 AS ntile2,
FLOOR((#r-1)*4/9)+1 AS ntile4
FROM (SELECT #r:=0,#n:=0) AS _init, numbers
The 2 and 4 factors are for the ntile(2) and ntile(4) respectively. The 9 value is because there are 9 rows in this example table. You must know the count of the table before you can run this query. The solution also requires user defined variables, which are always kind of tricky.
Result:
+------+--------+--------+--------+
| val | rownum | ntile2 | ntile4 |
+------+--------+--------+--------+
| 1 | 1 | 1 | 1 |
| 1 | 2 | 1 | 1 |
| 2 | 3 | 1 | 1 |
| 3 | 4 | 1 | 2 |
| 3 | 5 | 1 | 2 |
| 3 | 6 | 2 | 3 |
| 4 | 7 | 2 | 3 |
| 4 | 8 | 2 | 4 |
| 5 | 9 | 2 | 4 |
+------+--------+--------+--------+
I'll leave it as an exercise for you to adapt this technique to your query and your table, or to decide that it's time to upgrade to MySQL 8.0.
You can enumerate rows and use arithmetic. Unfortunately, you'll need to do this three times:
select floor(seqnum * 4 / #rn) as ntile_recency, t.*
from (select (#rn := #rn + 1) as seqnum, t.*
from (select customer_id, max(local_date) as last_order_date, count(*) as count_order,
sum(amount) as sum_amount
from transaction
group by customer_id
order by last_order_date
) t cross join
(select #rn := 0) params
) t;
I have a sports database where I want to sort the data by a custom field ('Rating') and update the field ('Ranking') with the row number.
I have tried the following code to sort the data by my custom field 'Rating'. It works when I sort it by a normal field, but not with a custom/calculated field. When the sorting has been done, I want it to update the field 'Ranking' with the row number.
Ie the fighter with the highest 'Rating' should have the value '1' as 'Ranking.
SELECT id,lastname, wins, Round(((avg(indrating)*13) + (avg(Fightrating)*5) * 20) / 2,2) as Rating,
ROW_NUMBER() OVER (ORDER BY 'Rating' DESC) AS num
from fighters
JOIN fights ON fights.fighter1 = fighters.id
GROUP BY id
The code above isn't sorting the Rating accurately. It sorts by row number, but the highest Rating isn't rated as #1. It seems a bit random.
SQL Fiddle: http://sqlfiddle.com/#!9/aa1fca/1 (This example is correctly sorted, but I want it to update the "Ranking" column by row number - meaning the highest rated fighter (by 'Rating') gets '1' in the Ranking column, the second highest reated fighter gets '2' in the Ranking column etc).
Also I would like to be able to add WHERE clause in the fighters table (where fighters.organization = 'UFC') for example.
First, let's fix your query so it runs on MySQL < 8.0. This requires doing the computing and sorting in a subquery, then using a variable to compute the rank:
select
id,
rating,
#rnk := #rnk + 1 ranking
from
(select #rnk := 0) r
cross join (
select
fighter1 id,
round(((avg(indrating)*13) + (avg(fightrating)*5) * 20) / 2,2) as rating
from fights
group by fighter1
order by rating desc
) x
Now we use the update ... join ... set ... syntax to update the fighters table:
update fighters f
inner join (
select
id,
rating,
#rnk := #rnk + 1 ranking
from
(select #rnk := 0) r
cross join (
select
fighter1 id,
round(((avg(indrating)*13) + (avg(fightrating)*5) * 20) / 2,2) as rating
from fights
group by fighter1
order by rating desc
) x
) y on y.id = f.id
set f.ranking = y.ranking;
Demo in a MySQL 5.6 fiddle based on the fiddle you provided in the comments.
The select query returns:
| id | rating | ranking |
| --- | ------ | ------- |
| 3 | 219.5 | 1 |
| 4 | 213 | 2 |
| 1 | 169.5 | 3 |
| 2 | 156.5 | 4 |
And here is the content of the fighters table after the update:
| id | lastname | ranking |
| --- | ---------- | ------- |
| 1 | Gustafsson | 3 |
| 2 | Cyborg | 4 |
| 3 | Jones | 1 |
| 4 | Sonnen | 2 |
I have a table with 2 columns, the first column is called ID and the second is called TRACKING. The ID column has duplicates, I want to to take all of those duplicates and consolidate them into one row where each value from TRACKING from the duplicate row is placed into a new column within the same row and I no longer have duplicates.
I have tried a few suggested things where all of the values would be concatenated into one column but I want these TRACKING values for the duplicate IDs to be in separate columns. The code below did not do what I intended it to.
SELECT ID, TRACKING =
STUFF((SELECT DISTINCT ', ' + TRACKING
FROM #t b
WHERE b.ID = a.ID
FOR XML PATH('')), 1, 2, '')
FROM #t a
GROUP BY ID
I am looking to take this:
| ID | TRACKING |
-----------------
| 5 | 13t3in3i |
| 5 | g13g13gg |
| 3 | egqegqgq |
| 2 | 14y2y24y |
| 2 | 42yy44yy |
| 5 | 8i535i35 |
And turn it into this:
| ID | TRACKING | TRACKING1 | TRACKING2 |
-----------------
| 5 | 13t3in3i | g13g13gg | 8i535i35 |
| 3 | egqegqgq | | |
| 2 | 14y2y24y | 42yy44yy | |
On (relatively) painful way to do this in MySQL is to use correlated subqueries:
select i.id,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 0
) as tracking_1,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 1
) as tracking_2,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 2
) as tracking_3
from (select distinct id from t
) i;
As bad as this looks, it will probably have surprisingly decent performance with an index on (id, tracking).
By the way, your original code with stuff() would put everything into one column:
select id, group_concat(tracking)
from t
group by id;
with test_tbl as
(
select 5 id, 'goog' tracking,'goog' tracking1
union all
select 5 id, 'goog1','goo'
union all
select 2 , 'yahoo','yah'
union all
select 2, 'yahoo1','ya'
union all
select 3,'azure','azu'
), modified_tbl as
(
select id,array_agg(concat(tracking)) Tracking,array_agg(concat(tracking1)) Tracking1 from test_tbl group by 1
)
select id, tracking[safe_offset(0)] Tracking_1,tracking1[safe_offset(0)] Tracking_2, tracking[safe_offset(1)] Tracking_3,tracking1[safe_offset(1)] Tracking_4 from modified_tbl where array_length(Tracking) > 1
I have a MySQL table with timestamp column t. I need to create another integer column (groupId) which will have the same value for records with timestamp with
less then 3 sec difference. My version of MySQL has no window function support. This is the expected output in 2nd column:
+---------------------+--------+
| t | groupId|
+---------------------+--------+
| 2017-06-17 18:15:13 | 1 |
| 2017-06-17 18:15:14 | 1 |
| 2017-06-17 20:30:06 | 2 |
| 2017-06-17 20:30:07 | 2 |
| 2017-06-17 22:44:58 | 3 |
| 2017-06-17 22:44:59 | 3 |
| 2017-06-17 23:59:50 | 4 |
| 2017-06-17 23:59:51 | 4 |
I tried to use self-join and TIMESTAMPDIFF(SECOND,t1,t2) <3
but I do not know how to generate the unique groupId.
P.S.
It is guaranteed by the nature of data what there is no continues range which spans > 3 sec
You can do this using variables.
select tm
,#diff:=timestampdiff(second,#prev,tm)
,#prev:=tm
,#grp:=case when #diff<3 or #diff is null then #grp else #grp+1 end as groupID
from t
cross join (select #prev:='',#diff:=0,#grp:=1) r
order by tm
For this, I believe that you need to create a stored procedure that first sort your table by the column t (timestamp) and then goes through it grouping and assigning the groupId accordingly.... in this case you can use your own counter as groupID.
What it is important here, is how you split the time into frames of 2 seconds, you could end with different results depending of your point of reference...
This query puts every record in the same group when the previous record is just 3 seconds before:
UPDATE t
JOIN (
SELECT
t.*
, #gid := IF(TIMESTAMPDIFF(SECOND, #prev, t) > 3, #gid + 1, #gid) AS gid
, #prev := t
FROM t
, (SELECT #prev := NULL, #gid := 1) v
ORDER BY t
) sq ON t.t = sq.t
SET t.groupId = sq.gid;
see it working live in an sqlfiddle
learn more about user-defined variables here
This query will work in Oracle sql:
select *
from (
select e.*,
rank() over (partition by trunc(hiredate,'mi') order by trunc(hiredate,'mi') desc) MINu
from emp e
)
I have a feeling this is a very simple question but maybe i'm having brain fart right now and just can't seem to figure out how to go about it.
I have a MySQL table structure like below
+---------------------------------------------------+
| id | date | score | speed | user_id |
+---------------------------------------------------+
| 1 | 2016-11-17 | 2 | 133291 | 17 |
| 2 | 2016-11-17 | 6 | 82247 | 17 |
| 3 | 2016-11-17 | 6 | 21852 | 17 |
| 4 | 2016-11-17 | 1 | 109338 | 17 |
| 5 | 2016-11-17 | 7 | 64762 | 61 |
| 6 | 2016-11-17 | 8 | 49434 | 61 |
Now i can get a particular user's best performance by doing this
SELECT *
FROM performance
WHERE user_id = 17 AND date = '2016-11-17'
ORDER BY score desc,speed asc LIMIT 1
This should return the row with ID = 3. Now what I want is a single query to run to be able to return that 1 such row for each unique user_id in the table. So the resulting result would be something like this
+---------------------------------------------------+
| id | date | score | speed | user_id |
+---------------------------------------------------+
| 3 | 2016-11-17 | 6 | 21852 | 17 |
| 6 | 2016-11-17 | 8 | 49434 | 61 |
Also further more, can I have another question within this same query that would further sort this eventual resulting table by the same criteria of sort (score desc, speed asc). Thanks
A simple method uses a correlated subquery:
select p.*
from performance p
where p.date = '2016-11-17' and
p.id = (select p2.id
from performance p2
where p2.user_id = p.user_id and p2.date = p.date
order by score desc, speed asc
limit 1
);
This should be able to take advantage of an index on performance(date, user_id, score, speed).
Is easy using variable to emulate row_number() over (partition by Order by)
Explanation:
First create two variables in the subquery.
Order by user_id so when user change the #rn reset to 1
Order by score desc, speed asc so each row will have a row_number, and the one you want always will have rn = 1
#rn := you change #rn for each row
if you have a new user_id then #rn is set to 1
otherwise #rn is set to #rn+1
SQL Fiddle Demo
SELECT `id`, `date`, `score`, `speed`, `user_id`
FROM (
SELECT *,
#rn := if(#user_id = `user_id`,
#rn + 1 ,
if(#user_id := `user_id`,1,1)
) as rn
FROM Table1
CROSS JOIN (SELECT #user_id := 0, #rn := 0) as param
WHERE date = '2016-11-17'
ORDER BY `user_id`, `score` desc, `speed` asc
) T
where T.rn =1
OUTPUT
For mysql
You can try with a double in subselect and group by
select * from performance
where (user_id, score,speed ) in (
SELECT user_id, max_score, max(speed)
FROM performance
WHERE (user_id, score) in (select user_id, max(score) max_score
from performance
group by user_id)
group by user_id, max_score
);