How to group and concatenate several rows into groups of 20s - mysql

I have a table with only numeric IDs
ID
1
2
3
4
5
6
7
8
9
10
And I want to break and concatenate (group) this ids into groups of 5s or 20s, ej.
GROUPS
1,2,3,4,5
6,7,8,9,10
11,12,13,14,15
How can I do this with SQL?
UPDATE:
SELECT with sorted ids
SELECT GROUP_CONCAT(id ORDER BY id) AS GROUPS
FROM `test`
GROUP BY (id - 1) DIV 5
Result:
GROUPS
1,2,3,4,5
6,7,8,9,10
11,12,13,14,15
16,17,18,19,20
21,22,23,24,25
26,27,28,29,30
31,32,33,34,35
SELECT with second unsorted table
SELECT GROUP_CONCAT(id ORDER BY id) AS GROUPS
FROM `test2`
GROUP BY (id - 1) DIV 5
Result:
GROUPS
3,5
10
12
16
23,24,25
32,35
43,44
47
55
61
68,70
77
84
89
91,92,95
97,100

For MySQL:
SELECT GROUP_CONCAT(id ORDER BY id) AS GROUPS
FROM yourtable
GROUP BY (id - 1) DIV 5
See it working online: sqlfiddle

For unsorted IDs:
SET #rank=0;
SELECT
id,
#rank:=#rank+1 AS rank,
GROUP_CONCAT(id ORDER BY id) AS GROUPS
FROM `test2`
GROUP BY (#rank ) DIV 5
And the result:
+----+------+----------------+
| id | rank | GROUPS |
+----+------+----------------+
| 1 | 1 | 1,3,5,7,9 |
| 13 | 7 | 11,13,15,17,19 |
| 29 | 15 | 21,23,25,27,29 |
| 31 | 16 | 31,33,35,37,39 |
| 45 | 23 | 41,43,45,47,49 |
| 51 | 26 | 51,53,55,57,59 |
| 61 | 31 | 61,63,65,67,69 |
| 77 | 39 | 71,73,75,77,79 |
| 81 | 41 | 81,83,85,87,89 |
| 93 | 47 | 91,93,95,97,99 |
+----+------+----------------+
Thanks a lot for your help, I could't know without your help!

Related

Multi-event tournament standings (with arbitrary number of entries)

Suppose you have a a multi-event competition where competitors can attempt any event an arbitrary number of times. (weird, I know.)
How do pull out a desired player's best time for each event,
and assign it a placing? (1st 2nd 3rd...)
Data example: Desired output:
Name | Event | Score Name | Event | Score | Rank
-------------------- ----------------------------
Bob 1 50 Given input: "Bob"
Bob 1 100 Bob 1 100 1
Bob 2 75 Bob 2 75 3
Bob 3 80 Bob 3 80 2
Bob 3 65
Given input: "Jill"
Jill 2 75 Jill 2 90 1
Jill 2 90 Jill 3 60 3
Jill 3 60
Given input: "Chris"
Chris 1 70 Chris 1 70 2
Chris 2 50 Chris 2 85 2
Chris 2 85 Chris 3 100 1
Chris 3 100
This is a build up of my previous question:
Multi-event tournament standings
I feel understand that problem much better (Thanks!), but I cannot bridge the gap to this version of the problem.
I have SQL 5.x so I cant use stuff like Rank(). This will also be crunching many thousands of scores.
Desired output can be acheaved with this query:
select
IF(event is NULL, CONCAT('Given input: "', name,'"'), name) as name,
IF(event is NULL, '', event) as event,
IF(event is NULL, '', max(score)) as score,
IF(event is NULL, '', (
select count(s2.name) + 1
from (
select name, max(score) as score
from scores es
where es.event = s.event
group by es.name
order by score desc
) s2
where s2.score > max(s.score)
)) as `rank`
from scores s
group by name, event with rollup
having name is not NULL
order by name, event;
And output (if run query in mysql cli):
+----------------------+-------+-------+------+
| name | event | score | rank |
+----------------------+-------+-------+------+
| Given input: "Bob" | | | |
| Bob | 1 | 100 | 1 |
| Bob | 2 | 75 | 3 |
| Bob | 3 | 80 | 2 |
| Given input: "Chris" | | | |
| Chris | 1 | 70 | 2 |
| Chris | 2 | 85 | 2 |
| Chris | 3 | 100 | 1 |
| Given input: "Jill" | | | |
| Jill | 2 | 90 | 1 |
| Jill | 3 | 60 | 3 |
+----------------------+-------+-------+------+
11 rows in set, 3 warnings (0.00 sec)
Should work on any Mysql 5.
You can get the highest score per event by an aggregation by event taking the max(). To simulate a dense_rank() you can use a subquery counting the scores higher than or equal to the current score per event.
For a particular contestant (here Bob) that makes:
SELECT d1.name,
d1.event,
max(d1.score) score,
(SELECT count(*)
FROM (SELECT d2.event,
max(d2.score) score
FROM data d2
GROUP BY d2.event,
d2.name) x1
WHERE x1.score >= max(d1.score)
AND x1.event = d1.event) rank
FROM data d1
WHERE d1.name = 'Bob'
GROUP BY d1.event
ORDER BY d1.event;
And for all of them at once:
SELECT d1.name,
d1.event,
max(d1.score) score,
(SELECT count(*)
FROM (SELECT d2.event,
max(d2.score) score
FROM data d2
GROUP BY d2.event,
d2.name) x1
WHERE x1.score >= max(d1.score)
AND x1.event = d1.event) rank
FROM data d1
GROUP BY d1.name,
d1.event
ORDER BY d1.name,
d1.event;
db<>fiddle
E.g.:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,name VARCHAR(12) NOT NULL
,event INT NOT NULL
,score INT NOT NULL
);
INSERT INTO my_table (name,event,score) VALUES
('Bob' ,1, 50),
('Bob' ,1,100),
('Bob' ,2, 75),
('Bob' ,3, 80),
('Bob' ,3, 65),
('Jill' ,2, 75),
('Jill' ,2, 90),
('Jill' ,3, 60),
('Chris',1, 70),
('Chris',2, 50),
('Chris',2, 85),
('Chris',3,100);
SELECT a.*
, FIND_IN_SET(a.score,b.scores) my_rank
FROM my_table a -- it's possible that this really needs to be a repeat of the subquery below, so
-- ( SELECT m.* FROM my_table m JOIN (SELECT name,event,MAX(score) score FROM my_table
-- GROUP BY name, event) n ON n.name = m.name AND n.event = m.event AND n.score = m.score) AS a
JOIN
(
SELECT x.event
, GROUP_CONCAT(DISTINCT x.score ORDER BY x.score DESC) scores
FROM my_table x
JOIN
( SELECT name
, event
, MAX(score) score
FROM my_table
GROUP
BY name
, event
) y
ON y.name = x.name
AND y.event = x.event
AND y.score = x.score
GROUP
BY x.event
) b
ON b.event = a.event
WHERE FIND_IN_SET(a.score,b.scores) >0;
+----+-------+-------+-------+------+
| id | name | event | score | rank |
+----+-------+-------+-------+------+
| 2 | Bob | 1 | 100 | 1 |
| 3 | Bob | 2 | 75 | 3 |
| 4 | Bob | 3 | 80 | 2 |
| 6 | Jill | 2 | 75 | 3 |
| 7 | Jill | 2 | 90 | 1 |
| 8 | Jill | 3 | 60 | 3 |
| 9 | Chris | 1 | 70 | 2 |
| 11 | Chris | 2 | 85 | 2 |
| 12 | Chris | 3 | 100 | 1 |
+----+-------+-------+-------+------+

MySQL - Student grade list, replace small grade by bonus grade

SELECT TestID, Grade FROM tests_points;
Returns:
+--------+-------+
| TestID | Grade |
+--------+-------+
| 10 | 125 |
| 11 | 110 |
| 12 | 100 |
| 13 | 75 |
| 14 | 50 |
| 15 | 65 |
| 16 | 70 |
| 17 | 100 |
| 18 | 100 |
+--------+-------+
But, tests ID 17 and 18 are "bonus tests", so I need replace the two lower grades by these two ones, and return the SUM of all grades.
So, how I can "replace"the two lower grades (From TestID 14 and 15) by testID 17 and 18 grades.
The "correct grade list" would be:
+--------+-------+
| TestID | Grade |
+--------+-------+
| 10 | 125 |
| 11 | 110 |
| 12 | 100 |
| 13 | 75 |
| 14 | 100|
| 15 | 100|
| 16 | 70 |
+--------+-------+
In the end I just need the SUM of all grades, fixing the lower grades.
SELECT SUM(Grade) FROM tests_points;
How can I do that?
This is what you need!
select testID, GREATEST(
IF(
grade = (select min(grade) from test_points),(select grade from test_points where testID = 17),
grade)
,
IF(grade = (select min(grade) from test_points WHERE grade > ( SELECT min(grade) FROM test_points))
,(select grade from test_points where testID = 18),
grade)) as Score
from test_points
where testID not in(17, 18)
Demo on SQLfiddle.com
the following query will get probably get what you want..
SELECT ttal-garba FROM
(SELECT sum(c.grade) as garba FROM (SELECT * from tests_points where testid < 17 order by grade asc limit 2) as c) as a,
(SELECT sum(grade) as ttal FROM tests_points) as b
(I know.. hard coding everything is bad ..)
If your just trying to add up all the values except the bottom two you can do something lik,e this.
First add a new field lets say test_type:
UPDATE test_points
SET test_type = 'test';
Now we can just select using group by the sum. For example
SELECT test_type, (SUM(Grade) OVER (ORDER BY Grade DESC))
FROM test_Points
LIMIT 7;
This will return the sum of all values, but it will show 7 columns. If you want only the total, Throw a group by into the mix, and it will narrow it down to one field, or just select the 7th row number.

Deleting duplicate entries with search criteria

I have table like
table_id item_id vendor_id category_id
1 1 33 4
2 1 33 4
3 1 33 2
4 2 33 4
5 2 33 2
6 3 33 4
7 3 33 4
8 1 34 4
9 1 34 4
10 3 35 4
Here table_id is primary key and table having total 98000 entries including 61 duplicate entries which I found by executing query
SELECT * FROM my_table
WHERE vendor_id = 33
AND category_id = 4
GROUP BY item_id having count(item_id)>1
In above table table_id 1,2 and 6,7 duplicate. I need to delete 2 and 7 from my table( Total 61 Duplicate Entries). How can I delete duplicate entries from my table using query with where clause vendor_id = 33 AND category_id = 4 ? I don't want delete other duplicate entries such as table_id 8,9
I cannot index the table, since I need to kept some duplicate entries which required. I need to delete duplicate with certain criteria
Please always take backup before running any deletion query.
Try using LEFT JOIN like this:
DELETE my_table
FROM my_table
LEFT JOIN
(SELECT MIN(table_id) AS IDs FROM my_table
GROUP BY `item_id`, `vendor_id`, `category_id`
)A
ON my_table.table_id = A.IDs
WHERE A.ids IS NULL;
Result after deletion:
| TABLE_ID | ITEM_ID | VENDOR_ID | CATEGORY_ID |
------------------------------------------------
| 1 | 1 | 33 | 4 |
| 3 | 1 | 33 | 2 |
| 4 | 2 | 33 | 4 |
| 5 | 2 | 33 | 2 |
| 6 | 3 | 33 | 4 |
See this SQLFiddle
Edit: (after OP's edit)
If you want to add more conditions, you can add it in outer WHERE condition like this:
DELETE my_table
FROM my_table
LEFT JOIN
(SELECT MIN(table_id) AS IDs FROM my_table
GROUP BY `item_id`, `vendor_id`, `category_id`
)A
ON my_table.table_id = A.IDs
WHERE A.ids IS NULL
AND vendor_id = 33 --< Additional conditions here
AND category_id = 4 --< Additional conditions here
See this SQLFiddle
What about this:
DELETE FROM my_table
WHERE table_id NOT IN
(SELECT MIN(table_id)
FROM my_table
GROUP BY item_id, vendor_id, category_id)
try below code...
DELETE FROM myTable
WHERE table_ID NOT IN (SELECT MAX (table_ID)
FROM myTable
GROUP BY table_ID
HAVING COUNT (*) > 1)
Try
DELETE m
FROM my_table m JOIN
(
SELECT MAX(table_id) table_id
FROM my_table
WHERE vendor_id = 33
AND category_id = 4
GROUP BY item_id, vendor_id, category_id
HAVING COUNT(*) > 1
) q ON m.table_id = q.table_id
After delete you'll have
| TABLE_ID | ITEM_ID | VENDOR_ID | CATEGORY_ID |
------------------------------------------------
| 1 | 1 | 33 | 4 |
| 3 | 1 | 33 | 2 |
| 4 | 2 | 33 | 4 |
| 5 | 2 | 33 | 2 |
| 6 | 3 | 33 | 4 |
| 8 | 1 | 34 | 4 |
| 9 | 1 | 34 | 4 |
| 10 | 3 | 35 | 4 |
Here is SQLFiddle demo
From your Question, I guess you need to remove the duplicate rows which has same values for the item_id,vendor_id and category_id like the rows having tabled_id 1 and 2. So it can be done by making the mentioned three columns unique together. So try the following,
alter ignore table table_name add unique index(item_id, vendor_id, category_id);
Note: I didnt test this yet, Will give sqlfiddle in sometime

MySql selecting greatest difference between 2 rows within the past day

I have a table that sort of looks like this
id | name | c1 | c2 | c3 | c4 | time
-------------------------------------------------
1 | miley | 23 | 11 | 21 | 18 | 2013-01-13 20:26:25
2 | john | 31 | 29 | 23 | 27 | 2013-01-14 20:26:25
3 | steve | 44 | 31 | 33 | 35 | 2013-01-14 20:26:25
4 | miley | 34 | 44 | 47 | 48 | 2013-01-15 08:26:25
5 | john | 27 | 53 | 49 | 52 | 2013-01-15 08:26:25
6 | steve | 27 | 62 | 50 | 64 | 2013-01-16 08:26:25
7 | miley | 44 | 54 | 57 | 87 | 2013-01-16 20:26:25
8 | john | 37 | 93 | 59 | 62 | 2013-01-17 20:26:25
9 | steve | 85 | 71 | 87 | 74 | 2013-01-17 20:26:25
...etc
*note: this is a random table I made up to just give you an idea of what my table looks like
I need to grab the name for who had the greatest change in a specific column over the course of a specific date range. I've tried a bunch of different queries by can't get one to work. I think my closest solution is something like...
SELECT table1.name, MAX(table1.c1-h.c1) as maxDiff
FROM table_a as table1
LEFT JOIN table_a as table2
ON table2.name=table1.name AND table1.c1>table2.c1
WHERE table2.c1 IS NOT NULL
What am I doing wrong? To be clear, I want to be able to select a range of dates then determine who has the biggest difference for that date range in a determined column. Also note that the data only increments over time, so the first capture of any day will always be <= the last capture of the day for that person.
It sounds like you will be needing a nested query. First, a query of each person on their own measurements within the date range, then order it by the biggest and take the top 1... something like this may work for you...
select
PreGroupByName.`Name`,
PreGroupByName.MaxC1 - PreGroupByName.MinC1 as MaxSpread
from
( select
t1.`Name`,
min( t1.c1 ) as MinC1,
max( t1.c1 ) as MaxC1
from
table_a t1
where
t1.`time` between '2013-01-01' and '2013-01-17' -- or whatever date/time range
group by
t1.`Name` ) as PreGroupByName
order by
MaxSpread DESC
limit 1
SELECT
`id`,`name`
,MAX(`c1`)-MIN(`c1`) AS `diff_c1`
-- ,MAX(`c2`)-MIN(`c2`) AS `diff_c2`
-- ,MAX(`c3`)-MIN(`c3`) AS `diff_c3`
-- ,MAX(`c4`)-MIN(`c4`) AS `diff_c4`
FROM `the_table`
WHERE `time` BETWEEN '2013-01-13 20:26:25' AND '2013-01-17 20:26:25'
GROUP BY `name`
ORDER BY `diff_c1` DESC -- whichever you want to evaluate
LIMIT 1

SQL query for getting count and distinct

I have the following table called user_pin_tags with the following data that I need to query and show distinct tags and a count of each:
| user_id | pin_id | tag_id |
+---------+--------+--------+
1 | 34 | 7
2 | 34 | 7
3 | 34 | 15
4 | 34 | 16
5 | 34 | 16
6 | 34 | 16
Right now I am using the following query to get the distinct tag_ids
SELECT DISTINCT tag_id
FROM user_pin_tags
WHERE pin_id = 34
which gives the result of:
| tag_id |
+--------+
7
15
16
I need to modify this query to give the distinct tag_ids as well as the count within that pin_id, so I want to generate this result set:
| tag_id | count |
+--------+-------+
7 | 2
15 | 1
16 | 3
How can I modify this query to achieve this?
Use COUNT(*) and GROUP BY:
SELECT tag_id, COUNT(*) AS total
FROM user_pin_tags
WHERE pin_id = 34
GROUP BY tag_id
SELECT
DISTINCT(tag_id)
,count(pin_id)
FROM user_pin_tags
WHERE pin_id = 34
GROUP BY tag_id
Group By should do it ... i think.
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
You can group by the tag_id:
SELECT tag_id, COUNT(pin_id)
FROM user_pin_tags
WHERE pin_id = 34
GROUP BY tag_id