MySQL: having rounded sum not equal to integer - mysql

I have a query in which I compare a rounded sum to an integer, and I'm receiving unexpected results.
SELECT assignments.*
FROM assignments
INNER JOIN time_entries
ON time_entries.assignment_id = assignments.id
WHERE assignments.organization_id = 2
AND assignments.allocation_mode = 'fixed'
AND (fixed_hours is not null)
HAVING round(sum(time_entries.scheduled_hours)) != round(assignments.fixed_hours);
It returns an assignment with fixed_hours of 20. The column is a float.
When I select the sum of the time_entries for that match that record, I get 20.000000298023224. When I call round on that, I get 20:
SELECT
round(sum(scheduled_hours))
FROM time_entries
WHERE assignment_id=112869;
And SELECT round(fixed_hours) from assignments where id=112869 also gives 20.
And of course select round(20.000000298023224) = round(20); returns 1.
So what's wrong with my query that that record is being returned?

Nice try at query summarization :), but you need a group by...
SELECT
assignments.*
FROM assignments
INNER JOIN (
SELECT
assignment_id
, ROUND(SUM(time_entries.scheduled_hours)) sum_hrs
FROM time_entries
GROUP BY
assignment_id
) te ON assignments.id = te.assignment_id
WHERE assignments.organization_id = 2
AND assignments.allocation_mode = 'fixed'
AND (fixed_hours IS NOT NULL)
AND te.sum_hrs <> ROUND(assignments.fixed_hours)
;

Related

Passing data from parent to sub-query

Im am trying to create a query that joins on some data for each row of the main query, but I do not know how to get around the issue "Unknown column 'cm.index' in 'where clause'". How can I pass a column value of the parent query row to the sub query?
Each row in the parent query has an index integer. I want to count the number of channel_members that have a consumption index greater than the index of each message.
Here is a sample db
SELECT read_count.*, cm.*
FROM chat_messages as cm
JOIN (SELECT b.chat_channel_id, Count(b.chat_channel_id) AS members_read
FROM channel_members b
WHERE b.consumption_index >= cm.index
GROUP BY b.chat_channel_id) read_count
ON cm.channel_id = read_count.chat_channel_id
WHERE cm.channel_id=5;
A subquery that you're joining with is not a correlated subquery, so you can't pass columns from the main query into it, they have to be related in the ON condition.
You should change the subquery to include cm.index in the grouping. Then you can join on that.
SELECT cm.*, SUM(rc.members_read) AS members_read
FROM chat_messages AS cm
JOIN (SELECT chat_channel_id, consumption_index, COUNT(*) AS members_read
FROM channel_members
GROUP BY chat_channel_id, consumption_index) AS rc
ON cm.channel_id = rc.chat_channel_id AND rc.consumption_index >= cm.index
WHERE cm.channel_id = 5
Or you could do it as a real correlated subquery, which goes in the SELECT list.
SELECT cm.*,
(SELECT COUNT(*)
FROM channel_members AS b
WHERE b.chat_channel_id = cm.channel_id AND b.consumption_index = cm.index) AS members_read
FROM chat_messages AS cm
WHERE cm.channel_id = 5

SUM case returns value without GROUP BY

When I add SUM around my case select, it returns the summed value without the GROUP BY.
The query I am using, without the SUM, is the following
SELECT CASE WHEN subscription_types.type = 'Succes lidmaatschap' THEN 7 ELSE 8 END FROM subscription_used
INNER JOIN training_sessions ON training_sessions.id = subscription_used.training_session_id
INNER JOIN training_series AS tserie ON tserie.id = training_sessions.training_serie_id
INNER JOIN user_training_session ON user_training_session.training_session_id = training_sessions.id
INNER JOIN subscriptions ON subscriptions.id = subscription_used.subscription_id
INNER JOIN subscription_types ON subscription_types.id = subscriptions.subscription_type_id
WHERE subscription_used.training_session_id = (SELECT training_sessions.id FROM training_sessions WHERE DATE(event_start_date) = #week_2_ago_date AND training_serie_id = 17) AND present=1
GROUP BY subscriptions.id
This query returns the values: 8,7. However, when I put a SUM around the case, it gives me the number 75. 75 is the SUM of the values that are getting returned without the GROUP BY.
Any ideas on how to fix this problem so that the query gives me the correct value (8+7 = 15, 1 row)? Thanks in advance
Group by is implying distinct values based on subscription.id so probably if you take the group by you you will get something like 8,8,8,8,8,7,7,7,7,7 due to the joins and such.
With the group by you only get the distinct values of 8 and 7. When you do sum with the group it will sum all of them though not the 2 distinct.
Most Simple fix that will give you 15:
SELECT SUM(SUBSCRIPTION_USED) FROM (
SELECT CASE WHEN subscription_types.type = 'Succes lidmaatschap' THEN 7 ELSE 8 END FROM subscription_used
INNER JOIN training_sessions ON training_sessions.id = subscription_used.training_session_id
INNER JOIN training_series AS tserie ON tserie.id = training_sessions.training_serie_id
INNER JOIN user_training_session ON user_training_session.training_session_id = training_sessions.id
INNER JOIN subscriptions ON subscriptions.id = subscription_used.subscription_id
INNER JOIN subscription_types ON subscription_types.id = subscriptions.subscription_type_id
WHERE subscription_used.training_session_id = (SELECT training_sessions.id FROM training_sessions WHERE DATE(event_start_date) = #week_2_ago_date AND training_serie_id = 17) AND present=1
GROUP BY subscriptions.id);
Probably a better way to write it in general though.
EDIT: You could also do SUM(DISTINCT CASE......) if you only want to sum distinct values.

MySQL group by twice and COUNT

Some sql query gives me the following result:
As you can see, it already has GROUP BY.
So what I need? I need to group it again (by treatment_name) and count rows for each group. See more details on screenshot.
Here is full query:
SELECT
treatment_summaries.*
FROM `treatment_summaries`
INNER JOIN
`treatments`
ON
`treatments`.`treatment_summary_id` = `treatment_summaries`.`id`
AND
(treatment <> '' and treatment is not null)
INNER JOIN
`treatment_reviews`
ON
`treatment_reviews`.`treatment_id` = `treatments`.`id`
INNER JOIN
`conditions_treatment_reviews`
ON
`conditions_treatment_reviews`.`treatment_review_id` = `treatment_reviews`.`id`
INNER JOIN
`conditions` ON `conditions`.`id` = `conditions_treatment_reviews`.`condition_id`
INNER JOIN `conditions_treatment_summaries` `conditions_treatment_summaries_join`
ON
`conditions_treatment_summaries_join`.`treatment_summary_id` = `treatment_summaries`.`id`
INNER JOIN `conditions` `conditions_treatment_summaries`
ON `conditions_treatment_summaries`.`id` = `conditions_treatment_summaries_join`.`condition_id`
WHERE
`conditions`.`id` = 9
AND `conditions`.`id` IN (9)
AND (latest_review_id is not null)
GROUP BY
treatment_reviews.id
ORDER BY
treatment_summaries.reviews_count desc
LIMIT 20 OFFSET 0
Maybe there is another issue, cause GROUP BY should not leave same lines (for given column), but anyway you can wrap it like this:
SELECT * FROM ( YOUR_SQL_SELECT_WITH_EVERYTHING ) GROUP BY id
So the result you get will behave as another table and you can do all operations like GROUP BY again.

MySQL Inner Join with where clause sorting and limit, subquery?

Everything in the following query results in one line for each invBlueprintTypes row with the correct information. But I'm trying to add something to it. See below the codeblock.
Select
blueprintType.typeID,
blueprintType.typeName Blueprint,
productType.typeID,
productType.typeName Item,
productType.portionSize,
blueprintType.basePrice * 0.9 As bpoPrice,
productGroup.groupName ItemGroup,
productCategory.categoryName ItemCategory,
blueprints.productionTime,
blueprints.techLevel,
blueprints.researchProductivityTime,
blueprints.researchMaterialTime,
blueprints.researchCopyTime,
blueprints.researchTechTime,
blueprints.productivityModifier,
blueprints.materialModifier,
blueprints.wasteFactor,
blueprints.maxProductionLimit,
blueprints.blueprintTypeID
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
So what I need to get in here is the following table with the columns below it so I can use the values timestamp and sort the entire result by profitHour
tablename: invBlueprintTypesPrices
columns: blueprintTypeID, timestamp, profitHour
I need this information with the following select in mind. Using a select to show my intention of the JOIN/in-query select or whatever that can do this.
SELECT * FROM invBlueprintTypesPrices
WHERE blueprintTypeID = blueprintType.typeID
ORDER BY timestamp DESC LIMIT 1
And I need the main row from table invBlueprintTypes to still show even if there is no result from the invBlueprintTypesPrices. The LIMIT 1 is because I want the newest row possible, but deleting the older data is not a option since history is needed.
If I've understood correctly I think I need a subquery select, but how to do that? I've tired adding the exact query that is above with a AS blueprintPrices after the query's closing ), but did not work with a error with the
WHERE blueprintTypeID = blueprintType.typeID
part being the focus of the error. I have no idea why. Anyone who can solve this?
You'll need to use a LEFT JOIN to check for NULL values in invBlueprintTypesPrices. To mimic the LIMIT 1 per TypeId, you can use the MAX() or to truly make sure you only return a single record, use a row number -- this depends on whether you can have multiple max time stamps for each type id. Assuming not, then this should be close:
Select
...
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Left Join (
SELECT MAX(TimeStamp) MaxTime, TypeId
FROM invBlueprintTypesPrices
GROUP BY TypeId
) blueprintTypePrice On blueprints.blueprintTypeID = blueprintTypePrice.typeID
Left Join invBlueprintTypesPrices blueprintTypePrices On
blueprintTypePrice.TypeId = blueprintTypePrices.TypeId AND
blueprintTypePrice.MaxTime = blueprintTypePrices.TimeStamp
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
Order By
blueprintTypePrices.profitHour
Assuming you might have the same max time stamp with 2 different records, replace the 2 left joins above with something similar to this getting the row number:
Left Join (
SELECT #rn:=IF(#prevTypeId=TypeId,#rn+1,1) rn,
TimeStamp,
TypeId,
profitHour,
#prevTypeId:=TypeId
FROM (SELECT *
FROM invBlueprintTypesPrices
ORDER BY TypeId, TimeStamp DESC) t
JOIN (SELECT #rn:=0) t2
) blueprintTypePrices On blueprints.blueprintTypeID = blueprintTypePrices.typeID AND blueprintTypePrices.rn=1
You don't say where you are putting the subquery. If in the select clause, then you have a problem because you are returning more than one value.
You can't put this into the from clause directly, because you have a correlated subquery (not allowed).
Instead, you can put it in like this:
from . . .
(select *
from invBLueprintTypesPrices ibptp
where ibtp.timestamp = (select ibptp2.timestamp
from invBLueprintTypesPrices ibptp2
where ibptp.blueprintTypeId = ibptp2.blueprintTypeId
order by timestamp desc
limit 1
)
) ibptp
on ibptp.blueprintTypeId = blueprintType.TypeID
This identifies the most recent records for all the blueprintTypeids in the subquery. It then joins in the one that matches.

MySQL Query Help, including a count?

Any ideas why this isn't working in MySQL?
SELECT blogentry.*,
person.personName,
(SELECT *
FROM BlogEntryComment
Where BlogEntryID = '8') as CommentCount
FROM blogentry
INNER JOIN person ON blogentry.personID = person.personID
WHERE blogentry.deleted = 'N'
ORDER BY blogentry.dateAdded DESC
The subquery needs to return only one value: the field count. * returns all rows, whereas count(*) will return how many there are.
(SELECT count(*) FROM BlogEntryComment Where BlogEntryID = '8')
You have to use the aggregate function COUNT in order to get the value - SELECT * in a subSELECT will frail, because it is attempting to return all the column values for a row into a single column.
That said, what you have will return the same CommentCount value for every BLOGENTRY record returned. The following is a better approach:
SELECT be.*,
p.personname,
COALESCE(x.num, 0) AS CommentCount
FROM BLOGENTRY be
JOIN PERSON p ON p.personid = be.personid
LEFT JOIN (SELECT bec.blogentryid,
COUNT(*) AS num
FROM BLOGENTRYCOMMENT bec
GROUP BY bec.blogentryid) x ON x.blogentryid = be.blogentryid