Return multiple rows from multi SELECT subquery - mysql

query
SELECT
(SELECT NAME FROM product_component) AS pcNAME,
(SELECT PROJECT_NAME FROM jira_project) AS jpNAME,
(SELECT FILTER_NAME FROM jira_filter) AS jfNAME
Each SELECT will return an indeterminate number of rows. I get the error Subquery returns more than 1 row. My desired output will be something like this (quick sketch):
=======================================
| pcNAME | jpNAME | jfNAME |
=======================================
| data | data | data |
+------------+------------+-----------+
| data | data | data |
+------------+------------+-----------+
| data | data | data |
+------------+------------+-----------+
| | data | data |
+------------+------------+-----------+
| | data | data |
+------------+------------+-----------+
| | data | |
+------------+------------+-----------+
Each column may produce a different number of rows than the others. So I will want to produce the amount of rows from the max and then blank out the others that don't fill the max number of rows.
NOTE: None of these tables have a shared column so cannot achieve as INNER JOIN
Any ideas on how this can be achieved?

One way to handle this in MySQL to use to variables, union all and aggregation:
SELECT MAX(NAME) as NAME, MAX(PROJECT_NAME) as PROJECT_NAME,
MAX(FILTER_NAME) as FILTER_NAME
FROM ((SELECT (#rnpc := #rnpc + 1) as rn, NAME, NULL as PROJECT_NAME, NULL as FILTER_NAME
FROM product_component CROSS JOIN
(SELECT #rnpc := 0) params
) UNION ALL
(SELECT (#rnpn := #rnpn + 1) as rn, NULL, PROJECT_NAME, NULL as FILTER_NAME
FROM jira_project CROSS JOIN
(SELECT #rnpn := 0) params
) UNION ALL
(SELECT (#rnf := #rnf + 1) as rn, NAME, NULL as PROJECT_NAME, NULL as FILTER_NAME
FROM jira_filter CROSS JOIN
(SELECT #rnf := 0) params
)
) t
GROUP BY rn
ORDER BY rn;

Related

Create new columns for duplicate row values based on column ID duplicate in sql

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

MySQL: Find max consecutive rows in a table based on value

Using MySQL, I am trying to find the highest number of consecutive rows in a table based on a value. For the sake of simplicity, my table looks like this:
+----+-------+
| ID | VALUE |
+----+-------+
| 1 | A |
| 2 | B |
| 3 | A |
| 4 | A |
| 5 | B |
| 6 | B |
| 7 | A |
| 8 | A |
| 9 | A |
| 10 | B |
+----+-------+
In this example, if I wanted the highest number of consecutive rows for 'A', I would get 3. For 'B', I would get 2. Even returning a result set of the counts of consecutive rows for 'A' would be preferable. I am newer to SQL so hints would be appreciated too. Any suggestions?
You can do it using variables:
SELECT VALUE, MAX(cnt) AS maxCount
FROM (
SELECT VALUE, COUNT(grp) AS cnt
FROM (
SELECT ID, VALUE, rn - rnByVal AS grp
FROM (
SELECT ID, VALUE,
#rn := #rn + 1 AS rn,
#rnByVal := IF (#val = VALUE,
IF (#val := VALUE, #rnByVal + 1, #rnByVal + 1),
IF (#val := VALUE, 1, 1)) AS rnByVal
FROM mytable
CROSS JOIN (SELECT #rn := 0, #rnByVal := 0, #val := '') AS vars
ORDER BY ID) AS t
) AS s
GROUP BY VALUE, grp ) AS u
GROUP BY VALUE
Variables #rn and #rnByVal are used in order to simulate ROW_NUMBER window function, currently not available in MySQL. The second variable (#rnByVal) performs a count over VALUE partitions.
Using #rn - #rnByVal in an outer query we can calculate grp field, which identifies islands of consecutive rows having the same VALUE. Performing a GROUP BY on VALUE, grp we can calculate the population of these islands and, finally, in the outermost query, get the max population per VALUE.
Demo here

MySQL - Enumerate all rows within a minute and keep the first one

I have a MySQL table with the following columns :
+----------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | varchar(11) | NO | | NULL | |
| time | varchar(12) | NO | | NULL | |
| gmt_offset | varchar(2) | YES | | NULL | |
| type | varchar(10) | YES | | NULL | |
| yield_b | varchar(10) | YES | | NULL | |
| yield_d | varchar(10) | YES | | NULL | |
+----------------+-------------+------+-----+---------+----------------+
And I am trying to keep one row per minute (the first one of every minute) using the following query:
create table temp_table1 as
select t.*
from (select t1.*,
(#rn := if(#prevd <> date or minute(time) <> #prevm, 1,
if(#prevd := date, if(#prevm := minute(time), 1, 1), 1)
)
) as seqnum
from table1 t1 cross join
(select #rn := 0, #prevd := 0, #prevm := 0) vars
order by date, time
) t
where seqnum = 1;
truncate table table1;
insert into table1(col1, . . ., coln)
select col1, . . . , coln
from temp_table1;
I try with this query to enumerate all rows within a minute, and then keep the first row for every minute.. But this doesn't work, it simply puts 1 in seqnum for every row, and does not get rid of any rows at all.
Can anybody help me make this query work and keep the first row of every minute?
Thanks in advance!
I don't know why the logic was inverted in the first if(). I think I was confused. Apologies for that.
create table temp_table1 as
select t.*
from (select t1.*,
(#rn := if(#prevd = date and minute(time) = #prevm, #rn + 1,
if(#prevd := date, if(#prevm := minute(time), 1, 1), 1)
)
) as seqnum
from table1 t1 cross join
(select #rn := 0, #prevd := 0, #prevm := 0) vars
order by date, time
) t
where seqnum = 1;
The logic for the variable assignment in this case is:
First clause does the comparisons and increments the #rn value.
The next clauses assign the new values of the variables using nested if() calls, returning 1 for all possibilities.
The purpose of putting all the assignments in a single expression is not to create arbitrarily complicated SQL. MySQL does not guarantee the order of evaluation of expressions in a select. If you have multiple expressions with variable assignments, then they may evaluated in the wrong order.

MySQL group chunks by column value, sorted by other column

I have a table like:
time | status
1390836600 | 1
1390836605 | 1
1390836610 | 0
1390836615 | 0
1390836620 | 1
1390836625 | 1
1390836630 | 1
I need to output the data "grouped" by the status, and sorted by time. The trick is that I need the groupings in chunks for each time the status changes, with the fields: MIN(time), status
So for the example data above I'd need an output like
MIN(time) | status
1390836600 | 1
1390836610 | 0
1390836620 | 1
This is not the behaviour of GROUP BY, which would just group ALL rows with the same status and only output 2 rows. But is something like this possible?
This (grouping of continuous ranges) is called gaps-and-islands problem and can be effectively solved by using analytic functions (specifically ROW_NUMBER()) which MySQL still has no support for.
But you can emulate ROW_NUMBER() with session variables in the following way
SELECT MIN(time) time, status
FROM
(
SELECT time, status,
#n := #n + 1 rnum,
#g := IF(status = #s, #g + 1, 1) rnum2,
#s := status
FROM table1 CROSS JOIN (SELECT #n := 0, #g := 0, #s := NULL) i
ORDER BY time
) q
GROUP BY rnum - rnum2
Output:
| TIME | STATUS |
|------------|--------|
| 1390836600 | 1 |
| 1390836610 | 0 |
| 1390836620 | 1 |
Here is a SQLFiddle demo

MySQL return first n rows and group the rest

I wnat to draw a pie chart with MySQL data. I need to retrieve the first n rows and group the rest.
The problem is that the first query is already grouped.
SELECT name AS especie, SUM(superficie) AS superficie
FROM ciclos
JOIN cultivos ON id_cultivo = idcultivo
JOIN tbl_especies ON id_especie = idespecie
WHERE fecha_cierre IS NULL
GROUP BY id_especie
ORDER BY superficie DESC
This is what I get:
+------------+------------+
| Especie | Superficie |
+------------+------------+
| Avena | 50.0000 |
| Centeno | 32.4000 |
| Trigo | 18.0000 |
| Almendros | 5.1100 |
| Olivos | 4.7000 |
| Vid | 1.8300 |
| Nogal | 0.3500 |
| Cerezo | 0.2500 |
+------------+------------+
And this is what I need:
+------------+------------+
| Especie | Superficie |
+------------+------------+
| Avena | 50.0000 |
| Centeno | 32.4000 |
| Trigo | 18.0000 |
| Almendros | 5.1100 |
| Rest | 7.1300 |
+------------+------------+
In this case, I need to retrieve the first 4 rows and group the rest.
Is there any way to solve this with one query?
SOLVED:
I took the #Gordon Linoff concept and mixed it with this.
The problem with the #Gordon Linoff solution, was that the row number were added during the order.
SELECT #rn := #rn + 1 AS rn, SUM(superficie) AS superficie, (CASE WHEN #rn <= 4 THEN name ELSE "Other" END) AS especie
FROM (
SELECT name, SUM(superficie) AS superficie
FROM ciclos
JOIN cultivos ON id_cultivo = idcultivo
JOIN tbl_especies ON id_especie = idespecie
WHERE fecha_cierre IS NULL
GROUP BY id_especie
ORDER BY superficie DESC
) AS temp
CROSS JOIN (SELECT #rn := 0) AS const
GROUP BY (CASE WHEN #rn <= 4 THEN name ELSE "Other" END)
ORDER BY superficie DESC
Hope this helps someone. Thanks for the help.
You can do this with one query, but it requires a subquery (in the end, somehow, you have to group already grouped data). Here is one, MySQL-specific way. It adds a sequence number on the rows using a variable, and then uses that for the grouping:
select (case when rn <= 4 then especie else 'otros' end) as grouping,
sum(superficie) as superficie
from (SELECT name AS especie, SUM(superficie) AS superficie, #rn := #rn + 1 as rn
FROM ciclos
JOIN cultivos ON id_cultivo = idcultivo
JOIN tbl_especies ON id_especie = idespecie
cross join (select #rn := 0) const
WHERE fecha_cierre IS NULL
GROUP BY id_especie
ORDER BY superficie DESC
) t
group by (case when rn <= 4 then especie else 'otros' end)