Compare sum of two columns to one column - mysql

I am trying to compare the sum of two column values to a third column values, then display a string literal as result set value. Here's my query
Here's my schema with sample data
Player_Games
------------------------
Game1 | Game 2 | Game 3
------------------------
20 | 13 | 45
------------------------
14 | 27 | 25
------------------------
18 | 17 | 36
------------------------
20 | 20 | 29
------------------------
32 | 10 | 33
------------------------
SELECT
CASE
WHEN((
SELECT SUM(Game1 + Game2) as total FROM Player_Games
)
<
(
SELECT Game3 FROM Player_Games
))
THEN "Expected_Performance"
END as Result
FROM Player_Games;
Expected Result
Expected Performance
NULL
Expected Performance
NULL
NULL
However, an error is thrown ERROR 1242 (21000) at line 4: Subquery returns more than 1 row
What am I missing here? Do I need to GROUP BY Game3?

you dont need sum here. just case :
SELECT
CASE
WHEN Game1 + Game2 < Game3
THEN 'Expected_Performance'
END as Result
FROM Player_Games

You can just check using a CASE expression whether the sum of game1 and game2 is less than game3 then add the needed text.
Query
select
case when game1 + game2 < game3
then 'Expected performance'
else null end as result
from player_games;

SELECT CASE WHEN t1.Game3 > t2.total THEN "Expected_Performance" END
FROM Player_Games t1
CROSS JOIN (SELECT SUM(Game1 + Game2) as total FROM Player_Games) t2
On 8+
SELECT CASE WHEN Game3 > SUM(Game1 + Game2) OVER () THEN "Expected_Performance" END
FROM Player_Games
If you need to summarize not over whole table but only in single row then
SELECT CASE WHEN Game3 > Game1 + Game2 THEN "Expected_Performance" END
FROM Player_Games
PS. There is no guarantee that the output rows order matches the source one (in all queries). So you must add some column into the output list which identifies separate rows.

Related

Select less nearest date from table [group by and order by]

I'm trying to find the nearest date for each group Type, Subtype, s_stype, category_id. If there is no date found take a with default value:
Sample data :
Type
subtype
s_stype
category_Id
date
1
1
1
211
20000000
1
1
1
211
30000000
1
1
2
211
20000000
1
1
2
211
20000000
1
1
3
211
null
1
1
2
311
50000000
1
1
2
311
40000000
1
1
2
311
null
Query:
Select *
from Table
where date <= input_date or date is null
group by Type, Subtype, s_stype, category_id
order by date desc
The query should take less nearest date for each type, subtype, s_stype, category.
For example, given input_date = 25000000:
Type
subtype
s_stype
category_Id
date
1
1
1
211
20000000
1
1
2
211
20000000
1
1
3
211
null
1
1
2
311
null
the query should give above result instead it gives incorrect row that takes a first row which satisfy where condition of given group criteria
As i have used mysql 5.7 so i need solution without window functions solution like the above
Getting the best row per group means there does not exist a better row for the group. And "closer" means that the absolute value of the difference of the two dates is smaller. This is how my query below works.
As your dates in your example are integers, I am showing integer math here. If you are working with real dates, you must use DATEDIFF instead of subtraction, because MySQL has a flaw concerning this allowing subtracting one date from another but returning some number that doesn't seem to have a meaning instead of returning an interval or a value of a predefined unit such as dates.
select *
from mytable
where not exists
(
select null
from mytable better
where better.type = mytable.type
and better.subtype = mytable.subtype
and better.s_stype = mytable.s_stype
and better.category_id = mytable.category_id
and
(
abs(better.date - 25000000) < abs(mytable.date - 25000000)
or
(better.date is not null and mytable.date is null)
)
)
order by type, subtype, s_stype, category_id, date;

Is it possible to group a DQL query by a portion of the primary key and return records with max() datetime record (earlier than a reference date)?

In a doctrine query, how can I group by only some of the variables in a composite primary key? In the case below, Category and Product (but not Iterator)?
Example
The below table, should keep a history of price changes over time (Transactions). I am trying to write a doctrine query to return the latest dated record (that precedes a reference date):
Category (PK) | Product (PK) | Iterator (PK) | CreateDT | Amount
1 1 1 2016-01-01 100
1 1 2 2016-04-01 150
1 2 1 2016-09-01 50
2 1 1 2016-01-01 75
2 1 2 2016-01-31 80
2 1 3 2016-09-01 90
Using the above as reference, the result I am trying to get is one record per Category/Product as below:
a). Considering Category=1 and comparing against a reference date 2016-02-01:
Category (PK) | Product (PK) | Iterator (PK) | CreateDT | Amount
1 1 1 2016-01-01 100
1 2 1 2016-09-01 50
b). Only comparing against a reference date 2016-02-01:
Category (PK) | Product (PK) | Iterator (PK) | CreateDT | Amount
1 1 1 2016-01-01 100
1 2 1 2016-09-01 50
2 1 2 2016-01-31 80
Code
My attempt is with the below but it does not work (in an ideal world, I would like `$category to be optional):
public function findActiveIterationByDate($category, $refdate)
{
return $this->getEntityManager()
->createQuery(
'SELECT b3
FROM AppBundle:Transaction t1
INNER JOIN
(SELECT t2
FROM AppBundle:Transaction t2, max(createDT) as createDT
WHERE t2.Category=$category, t2.createDT <= $refdate
GROUP BY t2.Category, t2.Product) as t3
ON t1.Category=t3.Category AND t1.Product=t3.Product'
)
->getResult();
}
Error
However, attempting this gives me the below error - I'm not sure if the problem is my INNER JOIN or DQL is not able to group by a partial primary key?
[Doctrine\ORM\Query\QueryException]
[Semantical Error] line 0, col 102 near '(SELECT t2
': Error: Class '(' is not defined.
The error is because you can't use a subquery in JOIN statement. You can see it in DQL syntax declartation. More specifically Join and JoinAssociationDeclaration definitions.
I think you could move the INNER JOIN statement into a SELECT subquery and make the WHERE conditions more restrictive if possible even though this would probably decrease performance.
Maybe even better would be writing a Native SQL and map the result to Doctrine entities which is relatively simple and well documented. See http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html#the-resultsetmapping.

Get largest values from multiple columns from latest timestamps in MySql

I'm trying to get a list of the*usedpc values across multiple similar columns, and order desc to get worst offenders. Also, I need to only select the values from the most recent timestamp for each sys_id.
Example data:
Sys_id | timestamp | disk0_usedpc | disk1_usedpc | disk2_usedpc
---
1 | 2016-05-06 15:24:10 | 75 | 45 | 35
1 | 2016-04-06 15:24:10 | 70 | 40 | 30
2 | 2016-05-06 15:24:10 | 23 | 28 | 32
3 | 2016-05-06 15:24:10 | 50 | 51 | 55
Desired result (assuming limit 2 for example):
1 | 2016-05-06 15:24:10 | disk0_usedpc | 75
3 | 2016-05-06 15:24:10 | disk2_usedpc | 55
I know I can get the max from each column using greatest, max and group timestamp to get only the latest values, but I can't figure out how to get the whole ordered list (not just max/greatest from each column, but the "5 highest values across all 3 disk columns").
EDIT: I set up a SQLFiddle page:
http://sqlfiddle.com/#!9/82202/1/0
EDIT2: I'm very sorry about the delay. I was able to get all three solutions to work, thank you. If #PetSerAl can put his solution in an answer, I'll mark it as accepted, as this solution allowed me to very smoothly customise further.
You can join vm_disk table with three row table to create separate row for each of yours disks. Then, as you have row per disk now, you can easily filter or sort them.
select
`sys_id`,
`timestamp`,
concat('disk', `disk`, '_usedpc') as `name`,
case `disk`
when 0 then `disk0_usedpc`
when 1 then `disk1_usedpc`
when 2 then `disk2_usedpc`
end as `usedpc`
from
`vm_disk` join
(
select 0 as `disk`
union all
select 1
union all
select 2
) as `t`
where
(`sys_id`, `timestamp`) in (
select
`sys_id`,
max(`timestamp`)
from `vm_disk`
group by `sys_id`
)
order by `usedpc` desc
limit 5
Maybe something like this would work... I know it may look pretty redundant but it could save overhead caused by doing multiple joins to the same table:
SELECT md.Sys_id,
md.timestamp,
CASE
WHEN
md.disk0_usedpc > md.disk1_usedpc
AND
md.disk0_usedpc > md.disk2_usedpc
THEN 'disk0_usedpc'
WHEN
md.disk1_usedpc > md.disk0_usedpc
AND
md.disk1_usedpc > md.disk2_usedpc
THEN 'disk1_usedpc'
ELSE 'disk2_usedpc'
END AS pcname,
CASE
WHEN
md.disk0_usedpc > md.disk1_usedpc
AND
md.disk0_usedpc > md.disk2_usedpc
THEN md.disk0_usedpc
WHEN
md.disk1_usedpc > md.disk0_usedpc
AND
md.disk1_usedpc > md.disk2_usedpc
THEN md.disk1_usedpc
ELSE md.disk2_usedpc
END AS pcusage
FROM mydatabase md
GROUP BY md.Sys_id HAVING MAX(md.timestamp)
ORDER BY pcusage DESC
Try this:
select
t1.sys_id, t1.`timestamp`,
case locate(greatest(disk0_usedpc ,disk1_usedpc ,disk2_usedpc), concat_ws(',' ,disk0_usedpc ,disk1_usedpc ,disk2_usedpc))
when 1 then 'disk0_usedpc'
when 1 + length(concat(disk0_usedpc, ',')) then 'disk1_usedpc'
when 1 + length(concat(disk0_usedpc, ',', disk1_usedpc, ',')) then 'disk2_usedpc'
end as usedpc,
greatest(disk0_usedpc ,disk1_usedpc ,disk2_usedpc) as amount
from yourtable t1
join (
select max(`timestamp`) as `timestamp`, sys_id
from yourtable
group by sys_id
) t2 on t1.sys_id = t2.sys_id and t1.`timestamp` = t2.`timestamp`
order by t1.`timestamp` desc
-- limit 2
SQLFiddle Demo
How it works, the sub query here is try to get the latest row for each group sys_id, as one kind of way in many solutions. Then you should get the greatest column in disk0_usedpc ,disk1_usedpc ,disk2_usedpc, as you wrote in your question, the function greatest is the plan. So greatest(disk0_usedpc ,disk1_usedpc ,disk2_usedpc) as amount can help you get the amount.
But also you want that column's name, here I used locate and concat, concat_ws(which avoids writing so many separators, here is comma ,).
Let's take row 1 | 2016-05-06 15:24:10 | 75 | 45 | 35 as an example:
concat_ws(',' ,disk0_usedpc ,disk1_usedpc ,disk2_usedpc) will give us "75,45,35", here 75's index in this string is 1, 45 is 4, 35 is 7.
As you see, locate(greatest(disk0_usedpc ,disk1_usedpc ,disk2_usedpc), concat_ws(',' ,disk0_usedpc ,disk1_usedpc ,disk2_usedpc)) will return 1, so the greatest row is disk0_usedpc, here it makes.

SQL SUM + Distinct

I want to know the request with which I displayed the sum of the amounts of the various clients that do not repeat with the SUM function and DISTINCT.
I used :
SELECT DISTINCT id_721z, SUM(montant) AS somme_montant
FROM `roue_ventes_cb`
WHERE `date_transaction` between '2015/01/01' and '2015/01/21';
But the result is not displayed correctly. I have this data:
id_721z | montant
1 | 15
1 | 15
2 | 22
2 | 22
2 | 22
I would like to show total_montant = 37 but not
id_721z | montant
1 | 30
2 | 66
SELECT SUM(montant) AS somme_montant
FROM (
SELECT DISTINCT id_721z, montant
FROM `roue_ventes_cb`
WHERE `date_transaction` between '2015/01/01' and '2015/01/21'
) AS t
This will sum all different montants. But if two ids have the same montant it will only count it once.
SELECT id_721z, SUM(DISTINCT montant) AS somme_montant
FROM `roue_ventes_cb`
WHERE `date_transaction` between '2015/01/01' and '2015/01/21';
So I will prefer emiros answer in any case. It is safer and distint will have a performance penalty anyway.

SQL query - how to construct multiple SUMs (based on different parameters) in one query

Please review my tables below... Is it possible to build a single query capable of
1) calculating the SUM of total_time for all vehicles that have class_id 1 (regardless of feature_id)(result would be 6:35)
2) calculating the SUM of total_time for all vehicles that have class_id 1 AND have feature_id 2(result would be 5:35 based on vehicle_id 22 and 24)
I'm able to get the results in two seperate queries, but I was hoping to retrieve them in one single query.... something like:
SELECT
SUM((CASE WHEN (VEHICLE_TABLE.class_id = 1) then LOG_TABLE.total_time else 0 end)) **AS TOTAL_ALL**,
...here goes statement for 2)... AS TOTAL_DIESEL...
FROM LOG_TABLE, VEHICLE_TABLE .....
WHERE VEHICLE_TABLE.vehicle_id = LOG_TABLE.vehicle_id ......
TABLE 1: LOG_TABLE (vehicle_id is NOT unique)
vehicle_id | total_time
--------------|--------------
22 2:00
22 0:30
23 1:00
24 2:20
24 0:45
TABLE 2: VEHICLE_TABLE (vehicle_id is unique)
vehicle_id | class_id
--------------|--------------
22 1
23 3
24 1
TABLE 3: VEHICLE_FEATURES_TABLE (vehicle_id is NOT unique but feature_id is unique per vehicle_id)
vehicle_id | feature_id
--------------|--------------
22 1
22 2
23 1
23 2
23 6
24 2
24 6
SELECT SUM(lt.total_time) AS TOTAL_ALL,
SUM(CASE WHEN (vft.feature_id IS NOT NULL) then LOG_TABLE.total_time else 0 end) AS FEATURE_TOTAL
FROM VEHICLE_TABLE vt
JOIN LOG_TABLE lt
ON vt.vehicle_id = lt.vehicle_id
LEFT JOIN VEHICLE_FEATURES_TABLE vft
ON vt.vehicle_id = vft.vehicle_id AND vft.feature_id = 2
WHERE vt.class_id = 1
It seems that there is not much point in putting both of them in one query unless you want the results together.
If so, just add a UNION between the 2 queries.
If you want to have both values in the same row try something like this:
SELECT (SELECT Sum(X)
FROM TBL
WHERE CLASS_ID = 1) AS CLS_id1,
(SELECT Sum(X)
FROM TBL
WHERE CLASS_ID = 1
AND FEATURE_ID = 2) AS CLS_id1_FTR_ID2