Query to sum some values of a column with a condition - mysql

Having a table with this values:
name | executing | failed |
-------------------------------
task1 0 1
task2 1 0
task3 1 0
task4 0 0
With a query i want to get:
The total amount of executing task (2 in the example, task2 and task3)
The total amount of failed task (1 in the example, task1)
The total amount of pending task (those that are executing=0 and failed=0, 1 in the example, task4)
I can get the first two by uysing the following query:
SELECT IFNULL(SUM(executing), 0) Executing, IFNULL(SUM(failed), 0) Failed FROM mytable;
How can I expand my query so I can get another column with the sum of pending tasks?
Thanks in advance
Expected output:
executing | failed | pending
----------------------------
2 1 1

You don't specify how you want the results. I would do this as:
select (case when executing = 1 and failed = 0 then 'Executing'
when failed = 1 then 'Failed'
when executing = 0 and failed = 0 then 'Pending'
else 'Unknown'
end) as status, count(*) as cnt
from t
group by status;
You can also easily pivot the data using conditional aggregation:
select sum(executing = 1 and failed = 0) as Executing,
sum(failed = 1) as Failed,
sum(executing = 0 and failed = 0) as Pending
from t;
This uses a MySQL shorthand that treats boolean expressions as numbers -- with "1" for true and "0" for false.

SELECT SUM(executing) AS Executing
, SUM(failed) as Failed
, SUM(CASE
WHEN executing = 0 AND failed = 0 THEN 1
ELSE 0 END
) AS Pending
FROM mytable;

Related

SQL IN query behavior

Executing the following SQL statement;
select '2312' in ('2312,254,2111') as result1, 2312 in ('2312,254,2111') as result2
I am getting the following result
+---------+---------+
| result1 | result2 |
+---------+---------+
| 0 | 1 |
+---------+---------+
I would expect the opposite result. Having result1 to be true and result2 to be false. Could someone explain why?
Using IN() with a CSV-string as parameter is not supported.
It should be IN ('2312','254','2111') instead of IN ('2312,254,2111')
The reason for the observed behaviour is an implicit type conversion happening. Look:
SELECT 2312 IN ('2312,254,2111') -- result: 1
SELECT 2312 IN ('254,2312,2111') -- result: 0 -- interesting
SELECT 2312 = '2312,254,2111' -- result: 1 << see ??
SELECT 2312 = '254,2312,2111' -- result: 0
Only the first number in the string is relevant. The rest is ignored due to the implicit type conversion.
Also,
SELECT '2312' in ('2312,254,2111') -- result: 0
is false, because no type conversion happens here. '2312' does not equal the only string '2312,254,2111' in the value list and hence the IN() operator return false.
If you use a list of values for IN() instead of a CSV-string, everything works als expected:
SELECT
2312 IN ('2312','254','2111') -- result: 1
, '2312' IN ('2312','254','2111') -- result: 1
, 254 IN ('2312','254','2111') -- result: 1
, '254' IN ('2312','254','2111') -- result: 1
, 2312 IN (2312,254,2111) -- result: 1
, '2312' IN (2312,254,2111) -- result: 1
, 254 IN (2312,254,2111) -- result: 1
, '254' IN (2312,254,2111) -- result: 1
From the manual:
Implicit type conversion may produce nonintuitive results:
mysql> SELECT 'a' IN (0), 0 IN ('b');
-> 1, 1
In both cases, the comparison values are converted to floating-point values, yielding 0.0 in each case, and a comparison result of 1 (true).

MySql Query for calculating Service uptime

I have two tables. In one table I maintain start and End time of the service. The 2nd table is transnational which contains Start and Stop time of service (0 means Stop and 1 means Start).
Need help on MySQL query on availability of service for a day or for specific range of date as below in hours:
Date |ServiceId|Available|Not available
06-08-2020|189 | 10 |2
06-08-2020|187 | 8 |4
My Tables as below:
TblStatus
ServiceId|status|Updated_Date
189 |1 |04-08-2020 09:42
189 |0 |04-08-2020 12:29
189 |1 |04-08-2020 12:47
189 |0 |04-08-2020 13:37
189 |1 |04-08-2020 14:16
TblMaster
ServiceId|StartTime|EndTime
189 |09:00:00 |23:59:59
SELECT Date(TS.Updated_Date) as Updated_Date, TM.ServiceId,
count(case when TS.Status=1 then 1 END) as Available,
count(case when TS.Status=0 then 1 END) as NotAvailable
from TblMaster TM
INNER JOIN TblStatus TS ON (TM.ServiceId = TS.ServiceId)
where TM.StartTime <= Time(TS.Updated_Date) and TM.EndTime >= Time(TS.Updated_Date)
group by Date(TS.Updated_Date),ServiceId;
Sort your dataset by service, updated_at asc
Create variables where you'll save the previous row you processed
You might need to track:
last_row_service_id
last_row_status
last_row_updated_date
Process dataset
#pseudocode
#SET last_row_service_id := -1;
#SET last_row_status := -10; #set to -10 so that we wont have -1 or +1 value in first row
#SET last_row_updated_date :='2020-08-06 00:00:00';
select DATE(updated_date) as 'Date', serviceId as ServiceId,
SUM(IF(#last_row_service_id = serviceId AND #last_row_status - status = 1,
#check if youre still on the same service and if last_row_status is 1 and current row status is 0
TIME_TO_SEC(updated_date) - TIME_TO_SEC(#last_row_updated_date), 0)) as 'Available',
#Get elapsed time in seconds, else return 0 seconds
# Do the same to 'Not available'
SUM(IF(#last_row_service_id = serviceId AND #last_row_status - status = -1,
TIME_TO_SEC(updated_date) - TIME_TO_SEC(#last_row_updated_date), 0)) as 'Not available',
# change value of pointers
#last_row_service_id := serviceId,
#last_row_status := status,
#last_row_updated_date := updated_date
from TblStatus
#Add your date filter
group by DATE(updated_date),serviceId
You still need to handle service uptime that runs past 23:59:59
What you can do is to add START (status 1) event on every start of day and STOP (status 0) event on every end of day before processing.

SQL statement equivalent to ternary operator

I would like to create a statement that is equivalent to (x - y == 0) ? return 0 : return 100 in MySQL. Something that might look like this:
SELECT id, [(integer_val - 10 == 0) ? 0 : 100] AS new_val FROM my_table
I want to compare an attribute in each row to a certain number, and if the difference between that number and the number in the row is 0, I want it to give me 0, otherwise, I want it to give me 100.
Example:
Applying this query on my_table (with 10 being the 'compared to' number):
id | integer_val
===================
1 10
2 10
3 3
4 9
Would return this:
id | new_val
===================
1 100
2 100
3 0
4 0
How can I do this?
Try this:
SELECT id, IF(integer_val = 10, 100, 0) AS new_val
FROM my_table;
OR
SELECT id, (CASE WHEN integer_val = 10 THEN 100 ELSE 0 END) AS new_val
FROM my_table;
Use case when statement:
select *, (case when integer_val = 10 then 100 else 0 end) as New_Val
from yourtable
Try using the IF function:
SELECT id, IF(integer_val - 10 = 0, 0, 100) AS new_val FROM my_table
(I stuck with your condition expression, but it can be simplified a bit since integer_value - 10 = 0 has exactly the same truth value as integer_value = 10.)
Note that the IF function is different from MySQL's IF statement used for stored programs.

MySQL Update CASE updates field with "0"

I have a problem by updating my SQL-Table with the CASE statement.
SELECT number,amount,minimuminventory FROM artikel WHERE number=17;
+--------+--------+------------------+
| number | amount | minimuminventory |
+--------+--------+------------------+
| 17 | 10 | 0 |
+--------+--------+------------------+
I have an amount of 10 but when I update my table:
UPDATE artikel
SET amount = CASE WHEN amount - minimuminventory - 2 < 0
THEN amount=amount-2
ELSE amount=99
END
WHERE artnr=17;
Query OK, 1 rows affected (0,01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql updates my table an sets amount to 0
SELECT number,amount,minimuminventory FROM artikel WHERE number=17;
+--------+--------+------------------+
| number | amount | minimuminventory |
+--------+--------+------------------+
| 17 | 0 | 0 |
+--------+--------+------------------+
Do you you see the problem?
amount = CASE WHEN amount - minimuminventory - 2 < 0 THEN amount=amount-2 ELSE amount=99 END
The value from the CASE statement is the intended value for the amount column, but you're doing amount=x again inside the THEN and ELSE parts of it. Change it to this:
amount = CASE WHEN amount - minimuminventory - 2 < 0 THEN amount-2 ELSE 99 END
Do you you see the problem?
In your statement:
UPDATE artikel
SET amount = CASE WHEN amount - minimuminventory - 2 < 0
THEN amount=amount-2
ELSE amount=99
END
WHERE artnr=17;
The comparison is interpreted as boolean but not as assignment.
SET amount = (amount=amount-2) -- comparison as if amount is equal to (17=17-2)
SET amount = (amount=99) -- comparison as if amount is equal to (17=99)
SET amount = false -- i.e. 0, because 17 != 15 or 17 != 99
That resulted a value of 0 in amount column.
Change your update statement as below:
UPDATE artikel
SET amount = CASE WHEN amount - minimuminventory - 2 < 0
THEN amount-2
ELSE 99
END
WHERE artnr=17;

Weighted rotator table design

I am having a hard time with this very simple project. Maybe it's because it's Monday, I'm not sure.
I have a table that looks like this:
id | weight | hits
------------------------------------------------
1 | 4 | 0
2 | 1 | 0
3 | 2 | 0
Obviously, the hits column will increment by 1 each time that particular record is selected (We will run this update from within our PHP script).
How can I best retrieve these records by their weight? What I mean is that if I ran my query 15 times, I would want the following IDs returned:
1
1
1
1
3
3
2
1
1
1
1
3
3
2
1
We have a simple formula in place that we got online that retrieves a random weighted result, but we aren't running the formula enough to statistically balance out the results, so instead we need to do a simple rotation as described above.
I know that this is a simple problem to solve, but I'm having a hard time coming up with the best way to do it today.
I hope I've been clear enough in my description of the problem.
Unless I'm missing something (it is Monday here too after all), can't you just sort by hits / weight and LIMIT 1?
Run:
SELECT id FROM tbl WHERE weight - hits > 0 ORDER BY weight DESC LIMIT 0,1
With the last result, run:
UPDATE tbl SET hits = hits + 1, total_hits = total_hits + 1 WHERE id = (THE RESULT);
Then run
SELECT (Sum(weight-hits)) FROM tbl
If result = 0
UPDATE tbl SET hits = 0
EDIT:
It's possible to make it with two queries:
$query="SELECT id FROM tbl, weight - hits AS diff WHERE weight - hits > 0 ORDER BY weight DESC";
$result=mysql_query($query);
$num=mysql_numrows($result);
$i=0;
$id=mysql_result($result,$i,"id");
$diff =0;
while ($i < $num) {
$diff= $diff + mysql_result($result,$i,"diff");
++$i;
}
if( $diff = 1 and $i = 1);
$query2="UPDATE tbl SET hits = 0, total_hits = total_hits + IF(id='$id',1,0)";
else{
$query2="UPDATE tbl SET hits = hits + 1, total_hits = total_hits + 1 WHERE id = '$id'";
}
mysql_query($query2);