SUM,COUNT with UNION in MySql - mysql

I have a table that has the following schema:
DATA | CAUSE_1 | TIME_1 | CAUSE_2 | TIME_2 | CAUSE_3 | TIME_3
The CAUSE.* field (VarChar) can not contain any string, and if so, the field TIME.* is 0.
I'm trying to create a query, but unfortunately without success, where I would have the result display in this form:
CAUSE | TOT_TIME | N_RIPET_CAUSE,
where:
In CAUSE I have a list of what is contained in CAUSE_1 ... CAUSE_3,
In TOT_TIME the sum of the values in TIME_1 ... TIME_3,
In N_RIPET_CAUSE the number of repetitions of each CAUSE.
I hope I explained.

try this
SELECT DATA ,CAUSE , TOT_TIME , N_RIPET_CAUSE
FROM ( select DATA, CONCAT(`CAUSE_1`,' ',`CAUSE_2`, ' ', `CAUSE_3`) as CAUSE ,
sum(`TIME_1` + `TIME_2` +`TIME_3`) as TOT_TIME ,
(count(`CAUSE_1`)+count(`CAUSE_2`)+count(`CAUSE_3`)) as N_RIPET_CAUSE
FROM your_table
group by DATA
) t
SEE SQLFIDDLE DEMO
EDIT.
try this
( select DATA , `CAUSE_1` as CAUSE ,
sum(`TIME_1` + `TIME_2` +`TIME_3`) as TOT_TIME ,
(count(`CAUSE_1`)+count(`CAUSE_2`)+count(`CAUSE_3`)) as N_RIPET_CAUSE
FROM Table1
group by DATA)
union all
(select DATA , `CAUSE_2` as CAUSE ,
sum(`TIME_1` + `TIME_2` +`TIME_3`) as TOT_TIME ,
(count(`CAUSE_1`)+count(`CAUSE_2`)+count(`CAUSE_3`)) as N_RIPET_CAUSE
FROM Table1
group by DATA )
union all
(select DATA , `CAUSE_3` as CAUSE ,
sum(`TIME_1` + `TIME_2` +`TIME_3`) as TOT_TIME ,
(count(`CAUSE_1`)+count(`CAUSE_2`)+count(`CAUSE_3`)) as N_RIPET_CAUSE
FROM Table1
group by DATA )
SQL DEMO HERE
EDIT:
try this due to your need
select cause, sum(time) Tot_time, count(cause) N_Ripet_Cause
from(
select cause_1 as cause, time_1 as time
from Table1
union all
select cause_2 as cause, time_2 as time
from Table1
union all
select cause_3 as cause, time_3 as time
from Table1
) t
group by cause
DEMO SQL FIDDLE

If you cannot change the table structure, then in order to get this result, you are going to need to unpivot the columns into rows.
MySQL does not have an unpivot function but this can be done using a UNION ALL query. You can then apply the aggregate to those values to get the final result:
select cause, sum(time) Tot_time, count(cause) N_Ripet_Cause
from
(
select data, cause_1 as cause, time_1 as time
from yourtable
union all
select data, cause_2 as cause, time_2 as time
from yourtable
union all
select data, cause_3 as cause, time_3 as time
from yourtable
) src
group by cause

You could make a select from union select like that:
select * from
(
select cause_1 as cause, time_1 as time from tableName
union
select cause_2 as cause, time_2 as time from tableName
union
select cause_3 as cause, time_3 as time from tableName
) as joinedValues
Then you could perform any actions from that select.
Like number of each clause:
select cause, count(cause) from
(
...
) as joinedValues
group by cause

Jack is on the mark - you've got too many possibly redundant cells in your table structure. Use relations to eliminate such occurrences.
DataTable
dID | Data
instancesTable
ID | dID | CAUSE | TIME
Then use NATURAL JOIN upon the two tables to extract the information;
SELECT * FROM DataTable NATURAL JOIN instancesTable WHERE dID=? LIMIT 3
This query will return a list of causes and times of whatever occurred on the ID of the 'Data' in the first table.
Edit: *N_RIPET_CAUSE* can be found using a SUM(CAUSE) on the dID.

Related

MySQL select all table data, gouped by one column data

I have a table that looks like this:
serial|vehicule|alert_emails
12411|AAA|yes
12411|BBB|yes
13411|CCC|yes
13411|DDD|yes
14411|EEE|yes
I want to do a mysql query to select all data and organize it by serial field to get a array result like this:
12411
AAA|yes
BBB|yes
13411
CCC|yes
DDD|yes
14411
EEE|yes
I tried group by the field serial but I'm not getting the desired result:
SELECT * FROM mytable GROUP BY serial;
Any help please?
Thanks.
Use UNION ALL to get the distinct serials of the table and all the rows of the table:
SELECT CASE WHEN t.col IS NULL THEN t.serial END serial, t.col
FROM (
SELECT DISTINCT serial, null AS col
FROM mytable
UNION ALL
SELECT serial, CONCAT(vehicule, '|', alert_emails)
FROM mytable
) t
ORDER BY t.serial, t.col IS NULL DESC
See the demo.

Is there any other option to get total count from table and distinct count of a column in same query?

I have table with 4 records with similar event name and 2 different device ids and i want total no. of records with total unique device ids.
Mysql gives perfect result but redshift is giving incorrect data.
CREATE TABLE test (
event_name varchar(50) NOT NULL,
deviceid int NOT NULL
);
INSERT INTO test (event_name, deviceid) VALUES
('install', 1),
('install', 1),
('install', 2),
('install', 1);
select count(event_name), count(distinct(deviceid)) from test;
Mysql result
You should use Distinct without ( )
SELECT count(event_name), COUNT(Distinct deviceid)
FROM Test;
Or
SELECT count(event_name), (SELECT count(deviceid) FROM (SELECT DISTINCT deviceid FROM test)) DisCount
FROM test;
As far as I know, Redshift should process this query correctly:
select count(event_name), count(distinct deviceid)
from test;
That said, in my experience with RedShift, count(distinct) was quite slow, particularly over an entire table. (This may be been fixed.)
If this is still the case, then a simple workaround is:
select sum(cnt) as row_count, count(*) as distinct_count
from (select deviceid, count(*) as cnt
from test
group by deviceid
) t
This might be significantly faster.

mysql - Get two greatest values from multiple columns

We can use GREATEST to get greatest value from multiple columns like below
SELECT GREATEST(mark1,mark2,mark3,mark4,mark5) AS best_mark FROM marks
But now I want to get two best marks from all(5) marks.
Can I do this on mysql query?
Table structure (I know it is wrong - created by someone):
student_id | Name | mark1 | mark2 | mark3 | mark4 | mark5
This is not the most elegant solution but if you cannot alter the table structure then you can unpivot the data and then apply a user defined variable to get a row number for each student_id. The code will be similar to the following:
select student_id, name, col, data
from
(
SELECT student_id, name, col,
data,
#rn:=case when student_id = #prev then #rn else 0 end +1 rn,
#prev:=student_id
FROM
(
SELECT student_id, name, col,
#rn,
#prev,
CASE s.col
WHEN 'mark1' THEN mark1
WHEN 'mark2' THEN mark2
WHEN 'mark3' THEN mark3
WHEN 'mark4' THEN mark4
WHEN 'mark5' THEN mark5
END AS DATA
FROM marks
CROSS JOIN
(
SELECT 'mark1' AS col UNION ALL
SELECT 'mark2' UNION ALL
SELECT 'mark3' UNION ALL
SELECT 'mark4' UNION ALL
SELECT 'mark5'
) s
cross join (select #rn := 0, #prev:=0) c
) s
order by student_id, data desc
) d
where rn <= 2
order by student_id, data desc;
See SQL Fiddle with Demo. This will return the top 2 marks per student_id. The inner subquery is performing a similar function as using a UNION ALL to unpivot but you are not querying against the table multiple times to get the result.
I think you should change your database structure, because having that many marks horizontally (i.e. as fields/columns) already means you're doing something wrong.
Instead put all your marks in a separate table where you create a many to many relationship and then perform the necessary SELECT together with LIMIT.
Suggestions:
Create a table that you call mark_types. Columns: id, mark_type. I
see that you currently have 5 type of marks; it would be very simple
to add additional types.
Change your marks table to hold 3 columns: id,
mark/grade/value, mark_type (this column foreign constraints to
mark_types).
Write your SELECT query with the help of joins, and GROUP BY mark_type.
you can create a temporary table and then
Create a temporary table in a SELECT statement without a separate CREATE TABLE
query that table as follows
SELECT TOP 2 * FROM temp
ORDER BY mark DESC
then
drop temp table
Okay here's a new answer that's should work with the current table structure:
SELECT `student_id`, `Name`, `mark` FROM (SELECT `student_id`, `Name`, `mark1` AS `mark` FROM `marks`
UNION ALL
SELECT `student_id`, `Name`, `mark2` AS `mark` FROM `marks`
UNION ALL
SELECT `student_id`, `Name`, `mark3` AS `mark` FROM `marks`
UNION ALL
SELECT `student_id`, `Name`, `mark4` AS `mark` FROM `marks`
UNION ALL
SELECT `student_id`, `Name`, `mark5` AS `mark` FROM `marks`) AS `marks`
ORDER BY `mark` DESC
LIMIT 2

select 2nd row of every ID in mysql

I have a table :
ID | time
1 | 300
1 | 100
1 | 200
2 | 200
2 | 500
I want to get 2nd row for every ID
I know that I can get 1st row as
select ID,time from T group by ID;
But I don't know about how to get 2nd row for every ID.
I know about limit and offset clause in mysql, but can't figure out how to use them here.
How can I do it ?
EDIT : Actually, time is not ordered. I forgot to specify that. I have made an edit in the table.
i have just an idee how to make it but i couldnt fix it , maybe you can fix it. any suggest is appreciated to correct my query
first this to select the first row of each id.
SELECT min(id) id
FROM TableName t2
group by id
then select the min(id) which are not in the first query to select to min(id) (which is second row)
like that
SELECT min(id) id ,time
FROM TableName
WHERE id NOT IN (
SELECT min(id) id
FROM TableName
GROUP BY id
)
GROUP BY id
** as i said its just suggest . it returns me 0 values.if u fix it let me edit my post to be helpful
here a demo
SELECT ID, MAX(time) time
FROM
(
select ID, Time
from TableName a
where
(
select count(*)
from TableName as f
where f.ID = a.ID and f.time <= a.time
) <= 2
) s
GROUP BY ID
SQLFiddle Demo
SELECT x.*
FROM test x
JOIN test y
ON y.id = x.id
AND y.time >= x.time
GROUP
BY id,time
HAVING COUNT(*) = n;
Note that any entries with less than n results will be omitted
You cannot do this with the tables that you have. You could make a valiant attempt with:
select id, time
from (select id, time
from t
group by t
) t
where not exists (select 1 from t t2 where t2.id = t.id and t2.time = t.time)
group by id
That is, attempt to filter out the first row.
The reason this is not possible is because tables are inherently unordered, so there is not real definition of "second" in your tables. This gives the SQL engine the opportunity to rearrange the rows as it sees fit during processing -- which can result in great performance gains.
Even the construct that you are using:
select id, time
from t
group by id
is not guaranteed to return time from the first row. This is a (mis)feature of MySQL called Hidden Columns. It is really only intended for the case where all the values are the same. I will admit that in practice it seems to get the value from the first row, but you cannot guarantee that.
Probably your best solution is to select the data into a new table that has an auto-incrementing column:
create table newtable (
autoid int auto_increment,
id int,
time int
);
insert into newtable(id, time)
select id, time from t;
In practice, this will probably keep the same order as the original table, and you can then use the autoid to get the second row. I want to emphasize, though, the "in practice". There is no guarantee that the values are in the correct order, but they probably will be.

Counting rows matching each of the multiple conditions in MySQL

Please help me figure a single query that will transform the data below...
|id |status_a |status_b |
|+++++++++++++++++++++++|
| 1|active |inactive |
...into this one.
|status_group |count|
|++++++++++++++++++++++++|
|status_a.active | 1|
|status_b.inactive | 1|
edit: If a single pass query is possible then that will be better. Also, does a query with unions does a single pass?
If status can be either only active or inactive, I'd suggest a different approach:
SELECT
sum(if(status_a='active',1,0)) AS status_a_active,
sum(if(status_a='inactive',1,0)) AS status_a_inactive,
sum(if(status_b='active',1,0)) AS status_b_active,
sum(if(status_b='inactive',1,0)) AS status_b_inactive
FROM table
Otherwise you need to use the UNION approach, but I'd do it a little differently. First, you can use UNION ALL, because you don't need to remove duplicates in the result. I'd also use GROUP BY only once like this:
SELECT status_group, count(id)
FROM (
SELECT CONCAT('status_a.', status_a) AS status_group, id FROM table
UNION ALL
SELECT CONCAT('status_b.', status_b) AS status_group, id FROM table
) a
GROUP BY status_group
I have a solution that uses UNIONs. Shown here:
SELECT 'status_a.active' AS status_group, COUNT(*) AS count FROM `test` WHERE status_a = 'active'
UNION
SELECT 'status_a.inactive' AS status_group, COUNT(*) AS count FROM `test` WHERE status_a = 'inactive'
UNION
SELECT 'status_b.active' AS status_group, COUNT(*) AS count FROM `test` WHERE status_b = 'active'
UNION
SELECT 'status_b.inactive' AS status_group, COUNT(*) AS count FROM `test` WHERE status_b = 'inactive'
Basically, it queries each condition for status_a or status_b being active or not. We get four such queries and we apply UNION to all of them.
I suppose, I've to move my comment a while ago which is also a shorter solution here than hw's.
SELECT CONCAT('status_a.', status_a) AS stat, COUNT(id) FROM base GROUP BY stat
UNION
SELECT CONCAT('status_b.', status_b) AS stat, COUNT(id) FROM base GROUP BY stat