sum if condition true, subtract if false mysqlite - mysql

I'm using mysqlite in my app and I'm having the following problem:
I have a table with the columns 'name', 'value' and 'condition' and I'd like to query the distinct names and the sum of the values of this name. But there's a condition: I want to sum only the names with 'condition' = 1 and I want to subtract the value if the 'condition' = 0.

Try this:
SELECT name, SUM(((condition * 2) - 1) * value)
FROM tablename
GROUP BY name
Notice how (condition * 2) - 1) evaluates to 1, if condition is 1 and -1 if condition is 0, thereby producing the output you need.

You could construct a case expression to do that:
SELECT name, SUM(CASE condition WHEN 1 THEN value
WHEN 0 THEN value * (-1))
FROM mytable
GROUP BY name

Related

Count two columns from 1 table

I got this table (date, indoor_km, outdoor_km,...)
I have found the SQL count to count indoor_km or outdoor_km
but I'm looking for a SQL count for the 2 columns from 1 table.
You can use the SUM function over a condition in order to count entries that satisfy your condition of IS NOT NULL:
select
sum( case when indoor_km is not null then 1 else 0 end ) As indoorKmCount,
sum( case when outdoor_km is not null then 1 else 0 end ) As outdoorKmCount
from table
You simply add them
SELECT SUM(indoor_km) + SUM(outdoor_km)
FROM...
To select the sum only for a time period simply add a WHERE clause, either using BETWEEN or >=, <=, I used the latter in my example
SELECT SUM(indoor_km) + SUM(outdoor_km)
FROM Fietsen_2018
WHERE datum >= '1/01/2018' AND datum <='31/01/2018'

Calculate percentage of total rows where criteria is true

I want to count the number of rows that meet some criteria and calculate that as a percentage of total number of rows (in this case counting all of the id's).
Something like this:
((select count(error_flag) from MY_TABLE where error_flag == "TRUE" / count(id) from MY_TABLE) * 100) as "% ERROR"
Please try the following:
CREATE TABLE errorTest (`error_flag` ENUM('TRUE','FALSE'));
INSERT INTO errorTest VALUES ('FALSE'), ('TRUE'), ('TRUE');
SELECT SUM(IF(error_flag = "TRUE", 1, 0)) / COUNT(*) `% ERROR` FROM errorTest;
Regards,
James

Subtract values from sum depending on the value of another column

I have a mysql table with columns:
value - integer
type - integer
value only can be positive integer, type can be 0 or 1
I need to sum this value, but the thing is that if type value is 1 i need to subtract this row value from my sum.
Is its possible to make without two queries?
You can try something like this, using if:
sum( if(`type` = 1, -1, 1) * `value` )
Or case:
sum( (case when `type` = 1 then -1 else 1 end) * `value` )

MySql Sub Select

I need to query a table and gather counts with and without a column value.
What is the count of records that contain column value on 'src' and the count without.
Problem
Results contain one day only instead of every day on each row. Each row has same values.
Results Expected
DAY, CONTAINS VALUE, DOESN'T CONTAIN VALUE
Query
SELECT
DATE_FORMAT(edate,'%Y-%m-%d') as day,
(SELECT
COUNT(id) FROM entries WHERE src='a string' and color = 'red') with_value,
(SELECT
COUNT(id) FROM entries WHERE src='' and color = 'red') without_value
FROM entries
GROUP BY day
ORDER BY day DESC
You can do it without subqueryes using this technique:
SELECT
DATE_FORMAT(edate,'%Y-%m-%d') as day,
SUM(src = 'a string') as with_value,
SUM(src = '') as without_value
FROM entries
GROUP BY day
ORDER BY day DESC
What I did there was take advantage of the fact that MySQL does not have a Boolean data type, but rather TRUE is identical to 1, and FALSE to 0, in effect having the SUM act as a COUNT of rows that satisfy the condition.
I would do this using conditional aggregation:
SELECT DATE_FORMAT(edate, '%Y-%m-%d') as day,
SUM(src = 'a string' and color = 'red') as with_value,
SUM(src = '' and color = 'red') as without_value
FROM entries
GROUP BY day
ORDER BY day DESC;
In MySQL boolean expressions are treated as 0 (for false) and (1 for true) in an integer context. This makes them convenient for aggregation.
If you want per-day aggregate results then you need to perform the grouping by day in the query(-ies) where you compute the aggregate(s). You are grouping in a parent query instead.
In any event, you don't need subqueries for this, and it would be better to avoid them:
SELECT
DATE_FORMAT(edate,'%Y-%m-%d') as day,
SUM(CASE WHEN src='a string' THEN 1 ELSE 0 END CASE)
AS with_value
SUM(CASE WHEN src='' THEN 1 ELSE 0 END CASE)
AS without_value
FROM entries
WHERE color = 'red'
GROUP BY day
ORDER BY day DESC

Formatting SQL result as a sum

Here is the query I'm using to gather daily values from three different "addresses", which reference different sensors in our system.
SELECT DISTINCT
LEFT(changes.date_time, 6) AS 'date',
changes.address AS 'address',
(CASE WHEN changes.address IN (18) THEN - MAX(changes.value * types.multiplier) ELSE MAX(changes.value * types.multiplier) END) AS 'value'
FROM sensor.changes
INNER JOIN sensor.types ON changes.type = types.idx
WHERE
changes.address IN (1, 4, 8)
AND changes.date_time >= '12110100000000'
AND changes.date_time <= '13050100000000'
GROUP BY LEFT(changes.date_time, 6),
changes.address
ORDER BY changes.idx;
The first date contains the following values, with subsequent dates carrying the same addresses:
date address value
130203 1 0.0160
130203 4 0.1220
130203 8 -0.0070
I want to combine these three values, address 1+4+8, into a single row for that date. I've attempted a SUM on the "value" column, but that results in an SQL error: #1111 - Invalid use of group function. Taking out the second GROUP BY column results in one value per date, but the value is not a sum of those values. Using a second query to format the result is OK, but I would prefer if the math was done in the first query because the database server is not local.
EDIT: to clarify what I need as the result:
date address value
130203 1 0.131
The integer under the address column in this case doesn't matter.
You should add a grouping clause.
SELECT Resdate,SUM(Res.address),SUM(Res.value)
FROM
(
SELECT DISTINCT
LEFT(changes.date_time, 6) AS 'date',
changes.address AS 'address',
(CASE WHEN changes.address IN (18) THEN - MAX(changes.value * types.multiplier) ELSE MAX(changes.value * types.multiplier) END) AS 'value'
FROM sensor.changes
INNER JOIN sensor.types ON changes.type = types.idx
WHERE
changes.address IN (1, 4, 8)
AND changes.date_time >= '12110100000000'
AND changes.date_time <= '13050100000000'
GROUP BY LEFT(changes.date_time, 6),
changes.address
ORDER BY changes.idx
) AS Res
GROUP BY Res.Date