Strange MySQL view issues - mysql

I have a MySQL view defined like this:
SELECT
group_concat(`h`.`name` SEPARATOR ',') AS `hosts`,
`m`.`id` AS `slo_application_id`,
`s`.`application` AS `application`,
`s`.`slo_conformance` AS `slo_conformance`,
`s`.`hourly_conformance` AS `hourly_conformance`,
`s`.`date` AS `date`,
`m`.`slo_profile` AS `slo_profile`
FROM
(
(
`inv_host_slo` `s`
JOIN `slo_host_map` `m` ON (
(
`s`.`application` = `m`.`application_string`
)
)
)
LEFT JOIN `inv_host` `h` ON ((`m`.`host_id` = `h`.`id`))
)
GROUP BY
`s`.`application`
It's very simple, but I'm noticing some strange behavior when I query the view with a WHERE on the date field. If I insert WHERE s.date = '2013-10-22' before the GROUP BY statement in an actual SQL query I get 2425 records, but if I do it to the view directly:
SELECT
*
FROM
v_host_slo_conformance
WHERE
date = '2013-10-22'
I only get 307 records.
This isn't happening on my development database and the only difference is that dev is running 5.5.15, whereas production is running 5.5.14. Is this a bug based on version differences or something I'm overlooking?

Think about the steps.
REAL QUERY
U join
Filter result set by where (date)
You group
this gives you one result set.
IN VIEW
U join
U group by
THIS IS THE VIEW/RESULT SET U WORK ON
U filter the result set.
Two completely different result sets that you work on, obviously will give two different results.

Related

SQL subquery with reference to parent table gives "Unknown column 'test_results.id' in 'where clause'"

First off, take a look at diagram (this is an application for testing students knowledge)
I already have working application, which calculates score (in percents), but to sort by score, it is required to select all the records (of current test). And it drastically slows down app (~ 10 seconds of waiting). So I decided to move that logic into single sql query.
Now, my SQL query looks like this:
select test_results.*,
(
select test_result_total_score * 100 / test_result_total_max_score
from (
select (select sum(question_score)
from (
select question_total_right_answers = question_total_options as question_score
from (
select (
select count(*)
from answers
inner join answer_options on answer_options.id = answers.answer_option_id
where answers.asked_question_id = asked_questions.id
and answers.is_chosen = answer_options.is_right
) as question_total_right_answers,
(
select count(*)
from answers
left join answer_options on answer_options.id = answers.answer_option_id
where answers.asked_question_id = asked_questions.id
) as question_total_options
from asked_questions
where asked_questions.test_result_id = test_results.id
) as rigt_per_question
) as questions_scores) as test_result_total_score,
(select count(*)
from asked_questions
where asked_questions.test_result_id = test_results.id) as test_result_total_max_score
) as right_per_test_result
) as result_in_percents
from test_results
where test_results.id between 1 and 200;
Here is what it should do: for each asked question collect how many answer options there are (question_total_options) and how many answers user selected right (question_total_right_answers) - the very nested subqueries.
Then for each of this results calculate score (this is basically 1 if user selected all right options and 0 if at least one option is selected not right).
After that, we sum scores of all that questions (test_result_total_score - how many questions user answered right). Also, we calculate how many questions there are in test result (test_result_total_max_score).
With that information we can calculate percentage of right answered questions (test_result_total_score * 100 / test_result_total_max_score)
And the error is on lines 23 and 28:
where asked_questions.test_result_id = test_results.id
where asked_questions.test_result_id = test_results.id) as test_result_total_max_score
It says: [42S22][1054] Unknown column 'test_results.id' in 'where clause'
I have tried using variable #test_result_id like this:
select test_results.*,
#test_result_id := test_results.id,
( ... )
where asked_questions.test_result_id = #test_result_id
where asked_questions.test_result_id = #test_result_id) as test_result_total_max_score
And it evaluates, but in wrong way (probably because order of evaluation select values is undefined). BTW, all result_in_percents correspond to very first result.
For those facing similar problem, it seems that there is no simple solution.
First off, you can try rewrite your subqueries with joins as I did (see below). But when you would like to perform group operations on grouped results, you are really unhappy person). A "dirty" solution might be create function to overcome barrier of nesting subqueries.
create function test_result_in_percents(test_result_id bigint unsigned)
returns float
begin
return (
select sum(tmp.question_right) * 100 / count(*)
from (select sum(answers.is_chosen = answer_options.is_right) = count(*) as question_right
, asked_questions.test_result_id as test_result_id
from answers
inner join answer_options on answer_options.id = answers.answer_option_id
inner join asked_questions on asked_questions.id = answers.asked_question_id
where asked_questions.test_result_id = test_result_id
group by answers.asked_question_id
) as tmp
group by test_result_id
);
end;
And then, just use this function:
select (test_result_in_percents(test_results.id)) as `result_percents`
from `test_results`
where `test_results`.`test_id` = 181
and `test_results`.`test_id` is not null
order by `test_results`.`id` desc;

How to update columns in another table from a select query?

i want to update some columns in another table from a query result. I keep getting error. Please help.
Update customer_info
set customer_info.reader_ID = aisle_info.reader_ID,
customer_info.tag_no = tag_logs.tag_no,
customer_info.area = aisle_info.area,
customer_info.max_timestamp = TIMESTAMPDIFF(SECOND,MIN(tag_logs.timestamp),MAX(tag_logs.timestamp))
FROM tag_logs
INNER join aisle_info ON tag_logs.reader_ID = aisle_info.reader_ID
WHERE T.tag_no = 515988190124;
1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM tag_logs
INNER join aisle_info ON tag_logs.reader_ID = aisle_info.reader_I' at line 5
You were close. Different databases have slightly different syntax on updates from select/join. Think of MySQL as a select statement, and use the alias of the primary table you are trying to update and then the SET clauses are after that.
So I first will start by writing the SELECT query by itself.
select
CI.Tag_No,
AI.Reader_ID,
AI.Area,
MIN( TL.TimeStamp ) MinTime,
MAX( TL.TimeStamp ) MaxTime
from
customer_info CI
join tag_logs TL
CI.tag_no = TL.tag_no
join aisle_info AI
on TL.Reader_ID = Reader_ID
WHERE
CI.tag_no = 515988190124
group by
CI.Tag_No,
AI.Reader_ID,
AI.Area
So this gives us the final results of what you want, and you can confirm it as needed.
Then apply your update such as
update Customer_Info CIUpd
JOIN
( select
CI.Tag_No,
AI.Reader_ID,
AI.Area,
MIN( TL.TimeStamp ) MinTime,
MAX( TL.TimeStamp ) MaxTime
from
customer_info CI
join tag_logs TL
CI.tag_no = TL.tag_no
join aisle_info AI
on TL.Reader_ID = Reader_ID
WHERE
-- notice your filter is HERE for the one tag_no you want to update
-- and will result with only this on TAG_NO set of values returned
CI.tag_no = 515988190124
group by
CI.Tag_No,
AI.Reader_ID,
AI.Area ) FirstQuery
-- the JOIN will ensure updating only on that one tag_no
ON CIUpd.Tag_No = FirstQuery.Tag_No
set
CIUpd.Reader_ID = FirstQuery.Reader_ID,
CIUpd.Area = FirstQuery.Area,
CIUpd.Max_TimeStamp = TimeStampDiff( second, FirstQuery.MinTime, FirstQuery.MaxTime )
Now this is not a perfect answer as your original query was not a proper query using the MIN() / MAX() context. When doing an aggregate query, you need to apply a group by on all NON aggregate columns. In this case you did not clarify any such group consideration on the Reader_ID and Area which COULD result in multiple rows from your tag_logs and aisle_info tables.
If the aisle info will be the same all the time for a given Tag_No, then that's simple, just skip the grouping on that and change those column retrieve values as MAX() each. if they never change, MAX() or even MIN() will always return the same value and not have an issue with the aggregate query without non-aggregate columns.
If you can provide additional clarification of data, purpose, etc, please edit your original post vs just leaving a comment. Then leave a comment for me to please review with updated info.

Correct result for MySQL query using LEFT JOIN and History table

Hi i would like to create query which returns status for meters in apartment.
I have tables BR_Apartment, BR_Meter and BR_Parameter_Value.
Default value for meter status is active (1) all history data was stored in BR_Parameter_Value table when status has changed.
I need only last status for meters in apartment or default value.
Current query returns all records from BR_Parameter_Value.
Example of my query on SQL Fiddle
Can someone correct me if I am building my query incorrectly or educate me with a few tips on how to properly accomplish what I want to do?
Edit:
I've updated example based on answer from Gordon which works if date is NOW() but I would like to use same query for history values as well to get meter status based on specific date.
You can use this to get the most recent parameter value from the parameter table (just the addition of the exists statement in the where clause):
SELECT m.*, IFNULL( pva.ParameterValue, 1 ) AS MeterIsActive,
IF( pva.ParameterValue <1, 'meterNotActive', '' ) AS MeterTypeClass
FROM BR_Meter m INNER JOIN
BR_Apartment a
ON ( m.ApartmentID = a.ApartmentID ) LEFT JOIN
BR_Parameter_Value pva
ON ( pva.ForeignID = m.MeterID AND
pva.ParameterDate <= NOW( ) AND
pva.ParameterID =12
)
WHERE m.ApartmentID = 2452 and
(not exists (select 1
from BR_Parameter_value pv2
where pv2.ForeignID = pva.ForeignID and
pv2.parameterid = pva.parameterid and
pv2.ParameterDate > pva.ParameterDate
)
)
I don't know what you mean by default value.

ORDER BY Causes MySQL query to become Extremely Slow

I have the following query:
SELECT *
FROM products
INNER JOIN product_meta
ON products.id = product_meta.product_id
JOIN sales_rights
ON product_meta.product_id = sales_rights.product_id
WHERE ( products.categories REGEXP '[[:<:]]5[[:>:]]' )
AND ( active = '1' )
AND ( products.show_browse = 1 )
AND ( product_meta.software_platform_mac IS NOT NULL )
AND ( sales_rights.country_id = '240'
OR sales_rights.country_id = '223' )
GROUP BY products.id
ORDER BY products.avg_rating DESC
LIMIT 0, 18;
Running the query with the omission of the ORDER BY section and the query runs in ~90ms, with the ORDER BY section and the query takes ~8s.
I've browsed around SO and have found the reason for this could be that the sort is being executed before all the data is returned, and instead we should be running ORDER BY on the result set instead? (See this post: Slow query when using ORDER BY)
But I can't quite figure out the definitive way on how I do this?
I've browsed around SO and have found the reason for this could be
that the sort is being executed before all the data is returned, and
instead we should be running ORDER BY on the result set instead?
I find that hard to believe, but if that's indeed the issue, I think you'll need to do something like this. (Note where I put the parens.)
select * from
(
SELECT products.id, products.avg_rating
FROM products
INNER JOIN product_meta
ON products.id = product_meta.product_id
JOIN sales_rights
ON product_meta.product_id = sales_rights.product_id
WHERE ( products.categories REGEXP '[[:<:]]5[[:>:]]' )
AND ( active = '1' )
AND ( products.show_browse = 1 )
AND ( product_meta.software_platform_mac IS NOT NULL )
AND ( sales_rights.country_id = '240'
OR sales_rights.country_id = '223' )
GROUP BY products.id
) as X
ORDER BY avg_rating DESC
LIMIT 0, 18;
Also, edit your question and include a link to that advice. I think many of us would benefit from reading it.
Additional, possibly unrelated issues
Every column used in a WHERE clause should probably be indexed somehow. Multi-column indexes might perform better for this particular query.
The column products.categories seems to be storing multiple values that you filter with regular expressions. Storing multiple values in a single column is usually a bad idea.
MySQL's GROUP BY is indeterminate. A standard SQL statement using a GROUP BY might return fewer rows, and it might return them faster.
If you can, you may want to index your ID columns so that the query will run quicker. This is a DBA-level solution, rather than a SQL solution - tuning the database will help overall performance.
The issue in the instance of this query, was that by using GROUP BY and ORDER BY in a query, MySQL is unable to use the index if the GROUP BY and ORDER BY expressions are different.
Related Reading:
http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
http://mysqldba.blogspot.co.uk/2008/06/how-to-pick-indexes-for-order-by-and.html

Mysql: Can't use a subquery field as a condition?

This code runs fine. Pay special attention to the 'AS commercial' subquery field. It works.
SELECT `Contacts`.`id`,
(
SELECT `team_members`.`id`
FROM team_members
INNER JOIN team_categories_team_members AS memcat
ON `team_members`.`id` = `memcat`.`team_member_id`
WHERE `memcat`.`team_category_id` =3
) AS commercial
FROM `oys001`.`team_members` AS `Contacts`
JOIN `oys001`.`brands_team_members` AS `BrandsTeamMember` ON (
`BrandsTeamMember`.`brand_id` =2
AND `BrandsTeamMember`.`team_member_id` = `Contacts`.`id` )
However, now I want to perform a condition on it, so I just add this:
WHERE commercial > 0
And it tells me the field does not exist... What's going wrong here?
Wrap your inital query in brackets
SELECT *
FROM (yourqueryhere) AS `v`
WHERE commercial > 0