How to group by / group_concat each set of results? - mysql

Image we have a table like this:
table1
+----------+----------+--------+------------+
| position | epoc | name | value |
+----------+----------+--------+------------+
| 1 | 1 | A | v01 |
| 1 | 1 | B | v02 |
| 1 | 1 | C | v03 |
| 1 | 2 | A | v04 |
| 1 | 2 | B | v05 |
| 1 | 2 | C | v06 |
| 1 | 3 | A | v07 |
| 1 | 3 | B | v08 |
| 1 | 3 | C | v09 |
| 1 | 4 | A | v10 |
| 1 | 4 | B | v11 |
| 1 | 4 | C | v12 |
| 2 | 5 | A | v13 |
| 2 | 5 | B | v14 |
| 2 | 5 | C | v15 |
| 2 | 6 | A | v16 |
| 2 | 6 | B | v17 |
| 2 | 6 | C | v18 |
| 2 | 7 | A | v19 |
| 2 | 7 | B | v20 |
| 2 | 7 | C | v21 |
| 2 | 8 | A | v22 |
| 2 | 8 | B | v23 |
| 2 | 8 | C | v24 |
+----------+----------+--------+------------+
I want to be able to get this table:
table2
+----------+--------------------+
| position | value |
+----------+--------------------+
| 1 | v01,v02,v04,v05 |
| 2 | v13,v14,v16,v17 |
+----------+--------------------+
the conditions are:
JUST the "value" of rows with "name" A OR B;
JUST "epocs" that are the first 2 unique results in "position" (epoc 3,4,7,8 are discarded)
GROUP by table1 position (for each position I want the concat of the values that match previous conditions)

This might be what you are looking for:
select position,
group_concat(value order by overall_row_num) value
from
(
select position,
name,
value,
epoc,
#num := if(#position = `position`, #num + 1, 1) as group_row_number,
#position := `position` as dummy,
overall_row_num
from
(
select position, name,
epoc,
value,
#rn:=#rn+1 overall_row_num
from t1, (SELECT #rn:=0) r
where name in ('A', 'B')
order by position, epoc
) x
order by overall_row_num
) x1
where group_row_number <= 4
group by position
See SQL Fiddle with demo

Related

Calculate percentages by group using sql

I have a table that cotains the id of the students, the course name and the course level.
+----+--------+-------+
| Id | Course | Level |
+----+--------+-------+
| 1 | A | 1 |
| 2 | A | 1 |
| 1 | B | 1 |
| 3 | B | 1 |
| 4 | C | 2 |
+----+--------+-------+
From this, I want to know the percentage each course covers by level.
Like in the below table:
+-------+--------+----------------+
| Level | Course | Count_by_level |
+-------+--------+----------------+
| 1 | A | 50% |
| 1 | A | 50% |
| 1 | B | 50% |
| 1 | B | 50% |
| 2 | C | 100% |
+-------+--------+----------------+
How can I do this using SQL?
SQL DEMO
SELECT S.[Id] , S.[Course], S.[Level], T.ctotal,
100.0 / T.ctotal
FROM students S
JOIN ( SELECT [Course], COUNT(*) as ctotal
FROM students
GROUP BY [Course]
) T
ON S.[Course] = T.[Course]
OUTPUT
| Id | Course | Level | ctotal | |
|----|--------|-------|--------|-----|
| 1 | A | 1 | 2 | 50 |
| 2 | A | 1 | 2 | 50 |
| 1 | B | 1 | 2 | 50 |
| 3 | B | 1 | 2 | 50 |
| 4 | C | 2 | 1 | 100 |
Just another option using the window functions (assuming 2012+)
Example
Select [Level]
,[Course]
,Pct = 100.0 / sum(1) over (partition by [Level],[Course])
From YourTable
Returns
Level Course Pct
1 A 50.000000
1 A 50.000000
1 B 50.000000
1 B 50.000000
2 C 100.000000

How to get count of combinations from database?

How to get count of combinations from database?
I have to database tables and want to get the count of combinations. Does anybody know how to put this in a database query, therefore I haven't a db request for each trip?
Trips
| ID | Driver | Date |
|----|--------|------------|
| 1 | A | 2015-12-15 |
| 2 | A | 2015-12-16 |
| 3 | B | 2015-12-17 |
| 4 | A | 2015-12-18 |
| 5 | A | 2015-12-19 |
Passengers
| ID | PassengerID | TripID |
|----|-------------|--------|
| 1 | B | 1 |
| 2 | C | 1 |
| 3 | D | 1 |
| 4 | B | 2 |
| 5 | D | 2 |
| 6 | A | 3 |
| 7 | B | 4 |
| 8 | D | 4 |
| 9 | B | 5 |
| 10 | C | 5 |
Expected result
| Driver | B-C-D | B-D | A | B-C |
|--------|-------|-----|---|-----|
| A | 1 | 2 | - | 1 |
| B | - | - | 1 | - |
Alternative
| Driver | Passengers | Count |
|--------|------------|-------|
| A | B-C-D | 1 |
| A | B-D | 2 |
| A | B-C | 1 |
| B | A | 1 |
Has anybody an idea?
Thanks a lot!
Try this:
SELECT Driver, Passengers, COUNT(*) AS `Count`
FROM (
SELECT t.ID, t.Driver,
GROUP_CONCAT(p.PassengerID
ORDER BY p.PassengerID
SEPARATOR '-') AS Passengers
FROM Trips AS t
INNER JOIN Passengers AS p ON t.ID = p.TripID
GROUP BY t.ID, t.Driver) AS t
GROUP BY Driver, Passengers
The above query will produce the alternative result set. The other result set can only be achieved using dynamic sql.
Demo here

MySQL : Select three last item INNER JOIN [duplicate]

This question already has answers here:
Using LIMIT within GROUP BY to get N results per group?
(14 answers)
Closed 7 years ago.
Just have a tricky blockING with MySQL.
I've 3 tables :
TV
| TV_ID | TV_name |
----------------------
| 1 | HBO |
| 2 | BBC |
| 3 | Fox news |
----------------------
----------------------
----------------------
Emission
| E_ID | E_TV_ID | E_NAME |
-------------------------------
| 1 | 1 | Weather |
| 2 | 1 | News |
| 3 | 1 | FAKE1 |
| 4 | 1 | FAKE2 |
| 5 | 1 | FAKE3 |
| 6 | 1 | FAKE4 |
| 7 | 2 | FAKE5 |
| 8 | 2 | FAKE6 |
| 9 | 2 | FAKE7 |
| 10 | 2 | FAKE8 |
| 11 | 2 | FAKE9 |
| 12 | 2 | FAKE10 |
| 13 | 2 | FAKE11 |
| 14 | 3 | FAKE12 |
| 15 | 3 | FAKE13 |
| 16 | 3 | FAKE14 |
| 17 | 3 | FAKE15 |
| 18 | 3 | FAKE16 |
| 19 | 3 | FAKE17 |
| 20 | 3 | FAKE18 |
-------------------------------
-------------------------------
-------------------------------
Replay
| R_ID | R_E_ID | R_DATE | R_URL_REPLAY |
-------------------------------------------
| 1 | 1 | 20150431 | URL1 |
| 2 | 20 | 20150431 | URL2 |
| 3 | 19 | 20150431 | URL3 |
| 4 | 2 | 20150431 | URL4 |
| 5 | 7 | 20150431 | URL5 |
| 6 | 16 | 20150430 | URL6 |
| 7 | 10 | 20150430 | URL7 |
| 8 | 1 | 20150430 | URL8 |
| 9 | 4 | 20150430 | URL9 |
| 10 | 9 | 20150430 | URL10 |
| 11 | 19 | 20150429 | URL11 |
| 12 | 2 | 20150429 | URL12 |
| 13 | 1 | 20150429 | URL13 |
| 14 | 12 | 20150429 | URL14 |
-------------------------------------------
-------------------------------------------
-------------------------------------------
And I want to create ONLY ONE query to get 3rd last emission of each TV, order by date and TV (if possible).
So for this exemple, I've 3 TV. 3*3 = 9 emissions, like :
| TV_ID | E_NAME | R_URL_REPLAY |
-------------------------------------
| 1 | Weather | URL1 |
| 1 | FAKE2 | URL4 |
| 1 | FAKE6 | URL8 |
| 2 | FAKE3 | URL5 |
| 2 | FAKE8 | URL7 |
| 2 | FAKE7 | URL10 |
| 3 | FAKE18 | URL2 |
| 3 | FAKE17 | URL3 |
| 3 | FAKE14 | URL6 |
I've try many solution (INNER JOIN -- SELECT .. FROM ( SELECT ...) -- Use var #:= -- Sub-sub-sub-sub query ) but not works..
Only works if I use UNION, but I've more than 20 TV, and write 20 UNION is really urgly..
If you have suggestion,
Thanks in advance,
It's not straightforward, but in a nutshell, sort your replays by tv and date, then rank them, then select those that match your rank criteria.
select *
from (
select if(#prev = e_tv_id, #rank := #rank +1, #rank := 1 and #prev := e_tv_id) as rank, q.*
from (
select e.e_tv_id, r_date, r_url_replay
from emission e
join (select #prev := 0, #rank := 1) q
inner join replay r
on r.r_e_id = e.e_id
order by e.e_tv_id asc, r.r_date desc
) q
) qq
where rank <=3 ;
demo here

Concatenate string with the number of ocurrence

I'm migrating a database from one application to another. In the first one I've two tables: proyectos and presupuestos. A row in 'proyectos' can have one or more rows in 'presupuestos'.
The new application has a field in presupuestos that is made concatenating the code of the proyect with the number of 'presupuesto' of this proyect. That's what I don't know how to do it.
My tables are like:
Proyectos:
+--------------+------------------+
| proyectos_id | proyectos_codigo |
+--------------+------------------+
| 1 | E+-00001 |
| 2 | E+-00002 |
| 3 | E+-00003 |
| 4 | E+-00004 |
| 5 | E+-00005 |
+--------------+------------------+
Presupuestos:
+-----------------+--------------+
| presupuestos_id | proyectos_id |
+-----------------+--------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 3 |
| 6 | 3 |
| 7 | 3 |
| 8 | 4 |
| 9 | 4 |
| 10 | 5 |
+-----------------+--------------+
I've tried with this query:
select presupuestos_id, p.proyectos_id, concat(pr.proyectos_codigo,'_1') from presupuestos p join proyectos pr on p.proyectos_id = pr.proyectos_id
Which result is:
+-----------------+--------------+----------------------------------+
| presupuestos_id | proyectos_id | concat(pr.proyectos_codigo,'_1') |
+-----------------+--------------+----------------------------------+
| 1 | 1 | E+-00001_1 |
| 2 | 1 | E+-00001_1 |
| 3 | 1 | E+-00001_1 |
| 4 | 2 | E+-00002_1 |
| 5 | 3 | E+-00003_1 |
| 6 | 3 | E+-00003_1 |
| 7 | 3 | E+-00003_1 |
| 8 | 4 | E+-00004_1 |
| 9 | 4 | E+-00004_1 |
| 10 | 5 | E+-00005_1 |
+-----------------+--------------+----------------------------------+
But obviusly, It doesn't what I want. My desired result is:
+-----------------+--------------+----------------------------------+
| presupuestos_id | proyectos_id | some code |
+-----------------+--------------+----------------------------------+
| 1 | 1 | E+-00001_1 |
| 2 | 1 | E+-00001_2 |
| 3 | 1 | E+-00001_3 |
| 4 | 2 | E+-00002_1 |
| 5 | 3 | E+-00003_1 |
| 6 | 3 | E+-00003_2 |
| 7 | 3 | E+-00003_3 |
| 8 | 4 | E+-00004_1 |
| 9 | 4 | E+-00004_2 |
| 10 | 5 | E+-00005_1 |
+-----------------+--------------+----------------------------------+
Try this :
SELECT presupuestos_id, p.proyectos_id,
CONCAT(pr.proyectos_codigo,'_',
(CASE p.proyectos_id
WHEN #p_id
THEN #rownumber := #rownumber + 1
ELSE #rownumber := 1 AND #p_id := p.proyectos_id END)
)AS result
FROM presupuestos p
JOIN proyectos pr ON p.proyectos_id = pr.proyectos_id
JOIN (SELECT #rownumber:=0, #p_id:='') AS t
This should do what you want, although the answer by RubahMalam looks better... :
SELECT a.presupuestos_id, a.proyectos_id, concat(p.proyectos_codigo,'_', count(*)) as "Some code"
FROM (
SELECT pr.presupuestos_id, pr.proyectos_id
FROM Presupuestos pr JOIN Proyectos p ON pr.proyectos_id = p.proyectos_id
) a
JOIN (
SELECT pr.presupuestos_id, pr.proyectos_id
FROM Presupuestos pr JOIN Proyectos p ON pr.proyectos_id = p.proyectos_id
) b
ON a.proyectos_id = b.proyectos_id AND a.presupuestos_id >= b.presupuestos_id
JOIN Proyectos p ON a.proyectos_id = p.proyectos_id
GROUP BY a.proyectos_id, a.presupuestos_id, p.proyectos_codigo
Sample SQL Fiddle

Is there a way in SQL (MySQL) to do a "round robin" ORDER BY on a particular field?

Is there a way in SQL (MySQL) to do a "round robin" ORDER BY on a particular field?
As an example, I would like to take a table such as this one:
+-------+------+
| group | name |
+-------+------+
| 1 | A |
| 1 | B |
| 1 | C |
| 2 | D |
| 2 | E |
| 2 | F |
| 3 | G |
| 3 | H |
| 3 | I |
+-------+------+
And run a query that produces results in this order:
+-------+------+
| group | name |
+-------+------+
| 1 | A |
| 2 | D |
| 3 | G |
| 1 | B |
| 2 | E |
| 3 | H |
| 1 | C |
| 2 | F |
| 3 | I |
+-------+------+
Note that the table may have many rows, so I can't do the ordering in the application. (I'd obviously have a LIMIT clause as well in the query).
I'd try something like:
SET #counter = 0;
SELECT (#counter:=#counter+1)%3 as rr, grp, name FROM table ORDER by rr, grp
What you can do is create a temporary column in which you create sets to give you something like this:
+-------+------+-----+
| group | name | tmp |
+-------+------+-----+
| 1 | A | 1 |
| 1 | B | 2 |
| 1 | C | 3 |
| 2 | D | 1 |
| 2 | E | 2 |
| 2 | F | 3 |
| 3 | G | 1 |
| 3 | H | 2 |
| 3 | I | 3 |
+-------+------+-----+
To learn how to create the sets, have a look at this question/answer.
Then its a simple
ORDER BY tmp, group, name
You can use MySQL variables to do this.
SELECT grp, name, #row:=#row+1 from table, (SELECT #row:=0) r ORDER BY (#row % 3);
+------+------+--------------+
| grp | name | #row:=#row+1 |
+------+------+--------------+
| 1 | A | 1 |
| 2 | D | 4 |
| 3 | G | 7 |
| 1 | B | 2 |
| 2 | E | 5 |
| 3 | H | 8 |
| 1 | C | 3 |
| 2 | F | 6 |
| 3 | I | 9 |
+------+------+--------------+