I have a table called battery_data,
+-------+-------+-------+-------+-------+-------+-------+
|Module |Machine| Num1 | Num3 | Field | Value | Value2 |
+-------+-------+-------+-------+-------+-------+-------+
| T-01 | A&B | 23.9| 123 | Cell| POS | 328.34|
| T-01 | A&B | 27.0| 456 |Battery| POS | 23.8|
| T-01 | C&D | 409.01| 789 | EV | NEG | NULL |
| T-01 | C&D | 411.02| 124 | Cell | VSB | NULL |
| T-02 | A&B | 509.01| 989 | EV | NEG | NULL |
| T-02 | A&B | 611.02| 824 |Battery| VSB | NULL |
+-------+-------+-------+-------+-------+-------+-------+
I want an output which returns the occurrences of all POS/NEG/VSB in Value field aggregated per unique module and unique machine. (There are only 2 types of machine: A&B, C&D)
If a certain Value(POS/NEG/VSB) isn't found for a module, 0 should be returned for that column.
I want to generate an output table as,
+-------+-------+-------+-------+-------+
|Module |Machine| POS | NEG | VSB |
+-------+-------+-------+-------+-------+
| T-01 | A&B | 2 | 0 | 0 |
| T-01 | C&D | 0 | 1 | 1 |
| T-02 | A&B | 0 | 1 | 1 |
+-------+-------+-------+-------+-------+
What should be the SQL query to achieve this?
Well, as you say, you want to find "the occurrences of all POS/NEG/VSB in Value field aggregated per unique module and unique machine". So first, you have to group by module and machine, and then for each row you have to count if it's value is POS or NEG or VSB. One way to do that is to have a different counter for every possible value, and then increase the counter that corresponds to the value of the current row. One way of doing that is the following:
SELECT Module, Machine,
SUM(CASE WHEN Value = 'POS' THEN 1 ELSE 0 END) AS POS,
SUM(CASE WHEN Value = 'NEG' THEN 1 ELSE 0 END) AS NEG,
SUM(CASE WHEN Value = 'VSB' THEN 1 ELSE 0 END) AS VSB
FROM battery_data
GROUP BY Module, Machine
Related
Consider the following table:
____________________________________
| my_table |
| ID | val_1 | val_2 | val_3 | val_4 |
| 1 | 1 | 1 | 2 | 0 |
| 2 | 1 | 2 | 8 | 1 |
| 3 | 1 | 2 | 9 | 1 |
| 4 | 1 | 2 | 10 | 1 |
| 5 | 1 | 3 | 6 | 1 |
| 6 | 1 | 4 | 8 | 1 |
| 7 | 2 | 1 | 14 | 1 |
| 8 | 2 | 2 | 1 | 0 |
| 8 | 2 | 2 | 8 | 1 |
| 8 | 2 | 3 | 2 | 0 |
------------------------------------
I need sum(val_3) for every combination of val_1,val_2 where val_4=1, or 0 if there is no val_4=1 for the given val_1,val_2
This query gets the proper sum grouping, but does not include a 0 value for empty combinations:
select val_1,val_2,sum(val_3) from my_table where val_4 = 1 group by val_1,val_2
I can get the proper result with a combination of LEFT JOIN on the same table and IFNULL(<condition>,0)
The first query averages .22 seconds on my dataset. The LEFT JOIN / IFNULL query averages 0.98 seconds. Is there a query structure that will include the 0 values and perform closer to the 0.22s time of the first query? My script may run this query a few thousand times per call.
This is actually a subquery to an outer query that calculates the stdev_samp() of val_3, so I do need to include 0 values.
We can do conditional aggregation without using any Derived Tables (subquery), or Left Join.
Query
SELECT
val_1,
val_2,
SUM(CASE WHEN val_4 = 1 THEN val_3
ELSE 0
END) AS sum
FROM my_table
GROUP BY val_1, val_2;
Result
| val_1 | val_2 | sum |
| ----- | ----- | --- |
| 1 | 1 | 0 |
| 1 | 2 | 27 |
| 1 | 3 | 6 |
| 1 | 4 | 8 |
| 2 | 1 | 14 |
| 2 | 2 | 8 |
| 2 | 3 | 0 |
View on DB Fiddle
Without your obvious data to test and compare to, I offer the following. The inner query goes through the list ONCE and does a sum as both the val_4 = 1 AND when = 0 as respective individual column results with the grouping. Once this is done, all combinations are already resolved with the group by. So now the outer query gets the Val 1 & 2, but a case-when for the final. If there is a value in the SumWhen1 > 0, we know there WAS a value with a 1. If no value, then it returns the sumWhen0 results.
SELECT
pq.Val_1,
pq.Val_2,
CASE when PQ.SumWhen1 > 0 then SumWhen1 else SumWhen0 end FinalSum
from
( select
val_1,
val_2,
sum( CASE when val_4 = 1 then val_2 else 0 end ) SumWhen1,
sum( CASE when val_4 = 0 then val_2 else 0 end ) SumWhen0,
from
my_table
where
val_4 = 1
group by
val_1,
val_2 ) PQ
Now, if your data can contain negatives, I would just add to the inner query the following and use this column as the basis to confirm a val_4 had any records.
sum( CASE when val_4 = 1 then 1 else 0 end ) Val1Records,
I'm having a problem.
I have this table called usersbycourse which shows this information:
+------------+-----------------+--------+-----------+-------+-----------------+-----------------+
| instanceid | shortname | userid | firstname | logid | lastaccessdelta | modulesfinished |
+------------+-----------------+--------+-----------+-------+-----------------+-----------------+
| 2 | PJU | 74 | Robin | 766 | 1662246 | 0 |
| 3 | Fundgest-GRHN1A | 75 | Batman | 867 | 1576725 | 0 |
| 3 | Fundgest-GRHN1A | 77 | Abigobeu | 1004 | 610480 | 0 |
+------------+-----------------+--------+-----------+-------+-----------------+-----------------+
and this SQL:
SELECT
mdl_course.id,
mdl_course.shortname,
COUNT(CASE WHEN usersbycourse.modulesfinished = 1 THEN NULL ELSE 1 END) AS studentcount
FROM mdl_course LEFT JOIN usersbycourse ON mdl_course.id = usersbycourse.instanceid
GROUP BY mdl_course.id;
The results from the SQL are:
+----+-----------------+--------------+
| id | shortname | studentcount |
+----+-----------------+--------------+
| 1 | Unity I | 1 |
| 2 | PJU | 1 |
| 3 | Fundgest-GRHN1A | 2 |
| 4 | asdzxc2 | 1 |
+----+-----------------+--------------+
But why? In inside SQL has no Unity I, and no asdzxc2. How do I produce a result like this:
+----+-----------------+--------------+
| id | shortname | studentcount |
+----+-----------------+--------------+
| 1 | Unity I | 0 |
| 2 | PJU | 1 |
| 3 | Fundgest-GRHN1A | 2 |
| 4 | asdzxc2 | 0 |
+----+-----------------+--------------+
?
EDIT:
I want to count only rows having modulesfinished = 0
What you're looking for is SUM rather than COUNT, that is,
SELECT
mdl_course.id,
mdl_course.shortname,
SUM(CASE WHEN usersbycourse.modulesfinished = 0 THEN 1 ELSE 0 END) AS studentcount
FROM mdl_course LEFT JOIN usersbycourse ON mdl_course.id = usersbycourse.instanceid
GROUP BY mdl_course.id;
The problem is because you are using LEFT JOIN some of the values for usersbycourse.modulesfinished are NULL
Something you need to learn is
NULL == something
Is always unknown, not true, not false, just unknown.
So when you try to compare with = 1 your nulls get the ELSE but not because they aren't 1, is because is all the rest.
So if instead you change the condition to
COUNT(CASE WHEN usersbycourse.modulesfinished = 0 THEN 1 ELSE NULL)
Only the TRUE match will get 1, the FALSE and the UNKNOW part ill get NULL and COUNT doesnt count nulls. And that is what you want.
Imagine the result of a query is something like the following:
+----+---------+--------+
| id | count | type |
+----+---------+--------+
| 1 | 20 | a |
| 1 | 30 | b |
| 1 | 10 | c |
| 2 | 05 | a |
| 2 | 20 | b |
| 2 | 40 | c |
+----+---------+--------+
and the expected result:
+----+---------+--------+------+
| id | a | b | c |
+----+---------+--------+------+
| 1 | 20 | 30 | 10 |
| 2 | 05 | 20 | 40 |
+----+---------+--------+------+
I know some solutions which are complex using Cursor, Variables, Join and etc. I would like to find the most efficient one, otherwise I will handle it from the application layer.
One method uses conditional aggregation:
select id,
sum(case when type = 'a' then count else 0 end) as a,
sum(case when type = 'b' then count else 0 end) as b,
sum(case when type = 'c' then count else 0 end) as c
from t
group by id;
+----+--------+------+------------+-----------+----------+------+
| id | deb_id | name | date | nominal | duration | type |
+----+--------+------+------------+-----------+----------+------+
| 1 | K7PJ8 | John | 2016-09-04 | 100000.00 | 2 | DB |
| 2 | K7PJ7 | Rey | 2016-08-30 | 125000.00 | 1 | DB |
| 3 | K7PJ8 | John | 2016-09-05 | 50000.00 | 2 | CR |
| 4 | K7PJ7 | Rey | 2016-08-05 | 25000.00 | 1 | CR |
+----+--------+------+------------+-----------+----------+------+
From the MySQL table above, it is possible if I want to display like the table below using PHP, and what syntax can I use for this ?
Result :
+--------+------+------------+------------+
| deb_id | name | nominal | residual |
+--------+------+------------+------------+
| K7PJ8 | John | 100000.00 | 50000.00 |
| K7PJ7 | Rey | 125000.00 | 100000.00 |
+--------+------+------------+------------+
Grouped by deb_id, and then sum the nominal WHERE type =(CR-DB)
SELECT deb_id
, name
, SUM(CASE WHEN type = 'DB' THEN nominal ELSE 0 END) AS nominal
, SUM(CASE WHEN type = 'CR' THEN nominal ELSE 0 END) AS credit
, SUM(CASE WHEN type = 'DB' THEN nominal ELSE 0 END) - SUM(CASE WHEN type = 'CR' THEN nominal ELSE 0 END) AS residual
FROM myTable
GROUP
BY deb_id
, name
You can you a case statement to separate those values into two separate columns.
Consider following table:
+------------+-----------+-------------+---------+------------+--------+-------------+
| client_id | TradeDate | servicetype | SEGMENT | OrdChannel | orders | OrderAmount |
+------------+-----------+-------------+---------+------------+--------+-------------+
| 1 | 20140611 | Type_1 | CASH | TT | 1 | 39275 |
| 2 | 20150119 | Type_1 | CASH | DNT | 2 | 11856.9 |
| 3 | 20150922 | Type_1 | FNO | OTHER | 1 | 854750 |
| 4 | 20151223 | Type_1 | CASH | TT | 5 | 71075 |
| 5 | 20140529 | Type_1 | Offline | FNO | 1 | 0 |
| 6 | 20160310 | Type_2 | CASH | WEB | 2 | 8009.6 |
| 7 | 20150318 | Type_1 | Offline | FNO | 2 | 432900 |
| 8 | 20150914 | Type_2 | CASH | WEB | 2 | 15612 |
| 9 | 20160317 | Type_2 | FNO | MINI | 1 | 9000 |
| 10 | 20140421 | Type_1 | CASH | TT | 8 | 17112.5 |
+------------+-----------+-------------+---------+------------+--------+-------------+
I am trying to group this data by client_id and TradeDate. Thus final dataset would contain each row for every <client-id, TradeDate> pair.
I want to compute following features from this data:
For each type of SEGMENT in every pair, I want to compute sum of orders and OrderAmount
Similarly, for each type of OrdChannel in every pair, I want to compute sum of orders and OrderAmount
Finally, a count of each servicetype i.e. Type_1 and Type_2.
Thus final dataset would contain columns similar to shown below:
+------------+-----------+-------------+---------+------------+--------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
| client_id | TradeDate | SEGMENT_CASH_orders_sum | SEGMENT_CASH_OrderAmount_sum.... | OrdChannel_TT_orders_sum | OrdChannel_TT_OrderAmount_sum.... | servicetype_Type_1_count | servicetype_Type_2_count |
+------------+-----------+-------------+---------+------------+--------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
So far, I have tried:
select clientsrno, TradeDate, SEGMENT, COUNT(orders) from orders group by clientsrno, TradeDate, SEGMENT;
but it's not giving separate columns for SEGMENT_CASH-orders_sum, etc.
Instead the output I am getting is:
+------------+-----------+---------+---------------+
| clientsrno | TradeDate | SEGMENT | COUNT(orders) |
+------------+-----------+---------+---------------+
| 44 | 20141209 | CASH | 23 |
| 44 | 20141211 | FNO | 10 |
+------------+-----------+---------+---------------+
You can do with a lot of sum( CASE .... END)
this is a suggestionfor the firts column
select client_di, TradeDate
sum (case SEGMENT
when 'CASH' then orders ELSE 0 END) as SEGMENT_CASH_orders_sum,
sum (case SEGMENT
when 'CASH' then OrderAmount ELSE 0 END) as SEGMENT_CASH_ordersAmount_sum,
sum (case SEGMENT
when 'FNO' then orders ELSE 0 END) as SEGMENT_FNO_orders,
sum (case SEGMENT
when 'FNO' then OrderAmount ELSE 0 END) as SEGMENT_FNO_ordersAmount_sum
from my_table
group by client_id, TradeDate
You can do for example:
SELECT clientsrno, TradeDate, SEGMENT, COUNT(orders), sumOrders.total FROM tabletest
INNER JOIN (
SELECT tabletest.tradeDate, SUM(orders) as total FROM tabletest
GROUP BY clientsrno, TradeDate, SEGMENT
) sumOrders ON tableTest.tradeDate = sumOrders.tradeDate
GROUP BY clientsrno, TradeDate, SEGMENT
And repeat the process for every sum you need.