Issue with a subquery MySQL 5.5 vs 5.6 - mysql

I have been running a website on MySQL 5.5 (percona) and using this query, which works:
SELECT
bds.FromStationPositionID
FROM
BusDestinationSegments AS bds
LEFT JOIN BusDestinationSegmentPrices AS bdsp ON bds.SegmentID = bdsp.BusDestinationSegmentID
AND EXISTS (
SELECT
BusDestinationScheduleID
FROM
BusDestinationSchedule
WHERE
BusDestinationScheduleDate + INTERVAL bdssf.DayOfset DAY = '2016-01-26'
),
BusDestinationStations AS bdssf
I needed to upgrade to 5.6 (again Percona) so I just did (on a development server).
However, this query now does not work. It tells me:
[Err] 1054 - Unknown column 'bdssf.DayOfset' in 'where clause'
I should add that I've checked and the column is there.
I'm guessing there is some kind of change from 5.5 to 5.6 which does not allow me to use columns in sub-queries, but I can't find anything on the topic.
Has anyone had any experience with this or can anyone suggest a way that what I'm trying to accomplish will work ?

You appear to be using a mix of old and new JOIN syntax. And expecting BusDestinationStations (which is bdssf from which the DayOfset column comes) to CROSS JOIN.
This used to work but one of the changes in MySQL 5.6 is to the precedence of the joins:-
http://dev.mysql.com/doc/refman/5.6/en/join.html :-
Previously, the comma operator (,) and JOIN both had the same
precedence, so the join expression t1, t2 JOIN t3 was interpreted as
((t1, t2) JOIN t3). Now JOIN has higher precedence, so the expression
is interpreted as (t1, (t2 JOIN t3)). This change affects statements
that use an ON clause, because that clause can refer only to columns
in the operands of the join, and the change in precedence changes
interpretation of what those operands are.
So I think what is happening is that MySQL is trying to perform the processing of the sub query before the implicit join to BusDestinationStations, and as such it doesn't know anything about bdssf.DayOfset when the sub query is executed.
Think the following would be the equivalent of what you are trying to do:-
SELECT bds.FromStationPositionID
FROM BusDestinationSegments AS bds
CROSS JOIN BusDestinationStations AS bdssf
LEFT JOIN BusDestinationSegmentPrices AS bdsp ON bds.SegmentID = bdsp.BusDestinationSegmentID
AND EXISTS
(
SELECT BusDestinationScheduleID
FROM BusDestinationSchedule
WHERE BusDestinationScheduleDate + INTERVAL bdssf.DayOfset DAY = '2016-01-26'
)
(although I am a bit dubious about doing a cross join like that just to get values for the sub query - looks very inefficient).

Related

Unknown column 'r' in field list

I've been working on a SQL query for a project, and I face an error message when I want to use it.
Here is the query itself :
SELECT COUNT(r) AS auditMade,
SUM(g.nbrMilkingCows) AS cowsAudited,
AVG(r.gainPerCowPerYearTransition) AS averageGainTransition,
AVG(r.gainPerCowPerYearLactation) AS averageGainLactation,
AVG(r.totalGain) AS averageTotalGain,
AVG(r.supplementalCostPerCow) AS averageSuppCost
FROM `smart_calculator_infos` i
INNER JOIN `smart_calculator_result` r ON r.idSmartCalculatorResult = i.idSmartCalculatorResult
INNER JOIN `calculator_general_informations` g ON g.idSmartCalculatorInfo = i.idSmartCalculatorInfo
WHERE i.idUser = 14
MySQL answers me "Unknown column 'r' in field list".
But I dont really understand why I get an error here as I define r in my INNER JOIN.
I'm kinda new at using SQL so maybe there is something pretty obvious I forgot, but I can't seem to understand what.
You can't count an alias itself, so the very first line of your query is what is causing the error:
SELECT COUNT(r)
To remedy this, you could use COUNT(*):
SELECT COUNT(*)
Or, you could count an actual column in the smart_calculator_result table, e.g.
SELECT COUNT(r.idSmartCalculatorResult)

How do I write an AREL UpdateManager query for MySQL that uses a subquery

I am tying to run an update query with a subquery against a MySQL database using ruby. I am using ruby 1.9.3 and rails 4.1.
The query I am trying to create is as below:
UPDATE `items`
SET
`items`.`status_id` = 12
WHERE
`items`.`id` IN (SELECT DISTINCT
`items`.`id`
FROM
`items`
LEFT OUTER JOIN
`statuses` ON `items`.`status_id` = `statuses`.`id`
LEFT OUTER JOIN
`resources` ON `items`.`resource_id` = `resources`.`id`
WHERE
`statuses`.`title` LIKE 'On Loan'
AND `items`.`duedate` < '2015-04-24'
AND `items`.`return_date` IS NULL
ORDER BY `items`.`duedate`)
I can produce this query in ruby using AREL with the code shown below:
# Declare Arel objects
i = Item.arel_table
s = Status.arel_table
r = Resource.arel_table
# This is the AREL query that returns the data
overdues = i.project(i[:id]).
join(s, Arel::Nodes::OuterJoin).on(i[:status_id].eq(s[:id])).
join(r, Arel::Nodes::OuterJoin).on(i[:resource_id].eq(r[:id])).
where(s[:title].matches("On Loan").
and(i[:duedate].lt(DateTime.now.to_date)).
and(i[:return_date].eq(nil))
).
order(i[:duedate])
# Note: You can't chain distinct, otherwise "overdues" becomes a string with the value "DISTINCT".
overdues.distinct
# This creates the update...
u = Arel::UpdateManager.new i.engine
u.table(i)
u.set([[i[:status_id], 10]]).where(i[:id].in(overdues))
This does not work and returns an error message:
ActiveRecord::StatementInvalid: Mysql2::Error: You can't specify target table 'items' for update in FROM clause:
I tried using AR "update_all" but it produces the same SQL and hence the same error.
Item.where(i[:id].in(overdues)).update_all(:status_id => (Status.find_by(:title => "Overdue").id))
Having done some research I have found that you cannot run a update with a subquery that references the table you want to update in MySQL. I have seen a number of posts on this site and the wider internet that detail work arounds.
One suggestion says that the update should use a join instead of a sub query. Having looked at the code behind the update manager it has no "join" so I can't do that.
Another says run this in two parts but I can't see how to because AREL and AciveRecord both chain actions.
The only way I can see of doing this is by aliasing the table and adding an additional select (see below). This isn't great but it would be useful to see if it is possible to do.
UPDATE `items`
SET `status_id` = 10
WHERE `items`.`id` IN (
SELECT x.id
FROM
(SELECT DISTINCT `items`.`id`
FROM `items`
LEFT OUTER JOIN `statuses` ON `items`.`status_id` = `statuses`.`id`
LEFT OUTER JOIN `resources` ON `items`.`resource_id` = `resources`.`id`
WHERE `statuses`.`title` LIKE 'On Loan'
AND `items`.`duedate` < '2015-04-24'
AND `items`.`return_date` IS NULL
ORDER BY `items`.`duedate`) x
);
If I can't get this to work I could adopt two other approaches:
1) I could just hard-code the SQL but I want to use ActiveRecord and reference the models to keep it database agnostic.
2) The other way is to return an instance of all the records and loop through them doing individual updates. This will have a performance issue but I can accept this because its a background job that won't be updating more than a handful of records each day.
Update
I have the AREL query below that produces the subquery in the format I need.
x = Arel::Table.new('x')
overdues = Item.select(x[:id]).from(
Item.select(Item.arel_table[:id]).where(
Status.arel_table[:title].matches("On Loan").and(
Item.arel_table[:duedate].lt(DateTime.now.to_date).and(
Item.arel_table[:return_date].eq(nil))
)
).joins(
Item.arel_table.join(Status.arel_table, Arel::Nodes::OuterJoin).on(
Item.arel_table[:status_id].eq(Status.arel_table[:id])
).join_sources
).joins(
Item.arel_table.join(Resource.arel_table, Arel::Nodes::OuterJoin).on(
Item.arel_table[:resource_id].eq(Resource.arel_table[:id])
).join_sources
).order(Item.arel_table[:duedate]).uniq.as('x')
)
Sadly it returns an error when I use it in my update statement.
TypeError: Cannot visit Item::ActiveRecord_Relation
Having revisited this question I am at the conclusion that it's not possible to do this because of a limitation with MySQL:
ActiveRecord::StatementInvalid: Mysql2::Error: You can't specify target table 'items' for update in FROM clause:
It should be possible to do with other databases (although I haven't tested that).
I could create a temporary table, which is the copy of the original table, reference that and then drop the temporary table like this post suggests:
http://richtextblog.blogspot.co.uk/2007/09/mysql-temporary-tables-and-rails.html. That seems a lot of overhead to do a simple subquery.
What I am going to do is find all the ID's and loop through them and update the records that way (using a simple find and update). This has an overhead but it should only be updating a handful of records each run (no more than 100). The update will be running as a scheduled job outside user working hours so it won't impact performance.
I still find it bizarre that in all other flavours of SQL I have never encountered this problem before. Still you live and learn.
UPDATE:
Since updating my version of MySQL the select statement now works. I had to take out the order by for it to work.
ORDER BY `items`.`duedate`
I am now using version: 5.7.19.

MySQL Error Code: 1349. View's SELECT contains a subquery in the FROM clause

I am trying to create a view but get the following error:
View's SELECT contains a subquery in the FROM clause
I am running the following command. I can't seem to figure out how to substitute the nested selects with joins. Any help would be much appreciated!
create view student_fee_basic as
select fsbc.*, ffp.name, ffp.amount 'fee'
from
(select sbc.*, ffc.name 'fname', ffc.id 'fid'
from (select s.admission_no, s.first_name, bc.id 'bid', bc.code, bc.name
from (select b.id, b.name, c.code
from batches b, courses c
where b.name = '2014-2015'
and b.course_id = c.id) bc
left join students s on bc.id = s.batch_id) sbc
left join finance_fee_categories ffc on ffc.batch_id = sbc.bid
where ffc.name = 'Basic Monthly') fsbc
left join finance_fee_particulars ffp on ffp.finance_fee_category_id = fsbc.fid;
MySQL does not support subqueries in views:
Subqueries cannot be used in the FROM clause of a view.
The documentation is here.
The easiest fix is to use a series of different views for each level.
You can probably rewrite this query to remove the subqueries. However, I find it very hard to help without explicit joins.
Version 5.7 supports it.
So one way to fix it is to migrate your database to newer version
upgrade to mysql-8 and your problem is solved.

Syntax error in MySQL Join Query

I'm getting a syntax error in MySQL query. Is MySQL and SQL server work differently? Can anyone suggest, what is wrong and where ?
select b.component, d.matter, d.bug, d.timestamp, d.os
from bugs.profiles p, ops_reports.BPR_TAG_DATA d
left join (Select * from bugs where product='test') b
on d.bug=b.bug_id
where d.tagid = 6
and timestamp between "2014-04-21" and "2014-04-24"
and login_name like 'test'
and p.userid = d.user
Error Message 24/04/2014 23:14:10 0:00:00.037 MySQL Database Error: 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 'Select * from bugs where product='Conversions') as b
on (d.bu 1 0
You should not mix implicit and explicit joins. A simple rule: just don't use commas in the from clause.
select b.component, d.matter, d.bug, d.timestamp, d.os
from ops_reports.BPR_TAG_DATA d left join
bugs b
on b.product = 'test' and d.bug = b.bug_id left join
bugs.profiles p
on p.userid = d.user
where d.tagid = 6 and
timestamp between '2014-04-21' and '2014-04-24' and
login_name like 'test';
I also removed the subquery, moving the condition to the on clause. This makes the query more efficient. And changed the delimiters for the date constants to single quotes. Using double quotes for strings can lead to confusion.
EDIT:
All this said, the query in the question looks like it is syntactically correct. I notice that the error message does not refer to this exact query. The query has product='test') b and the error message has product='Conversions') as b. Perhaps there are other differences as well.

MySQL left join subquery fail

Following query runs well in MySQL 5.x
SELECT
m_area.id, m_area.cn_areaName, m_area.de_areaName,
m_area.en_areaName,m_area.jp_areaName,t_shop.count
FROM
m_area left join
(
select t_shop.areaID, count(areaID) AS count
from t_shop
group by t_shop.areaID
) t_shop
on m_area.id = t_shop.areaID
However, when I have to run it in a 4.0.23 MySQL DB with same DB structure and data it just return following message:
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 '[
select t_shop.areaID, count(areaID) AS count
from t_s
I tried many times but still failed. Is left join to subquery not allowed in MySQL 4.x ? Then that mean I have to do it with a temp table?
Thanks in advance!
Subqueries were quite not well supported with MySQL 4.0 : it became possible to use them (at least, in some real, useful way) with MySQL 4.1 -- and MySQL 4.0 is really old, now...
See for instance this page of the MySQL manual : 12.2.8. Subquery Syntax (quoting, emphasis mine) :
Starting with MySQL 4.1, all subquery forms and operations that the
SQL standard requires are supported,
as well as a few features that are
MySQL-specific.
With MySQL versions prior to 4.1, it
was necessary to work around or
avoid the use of subqueries. In
many cases, subqueries can
successfully be rewritten using joins
and other methods. See Section
12.2.8.11, “Rewriting Subqueries as Joins for Earlier MySQL Versions”.
take out ", count(areaID) AS count"
The multiple columns in the subquery is messing up the join.
A temp table should work fine ....
Have fun!
Only thing I could think of is adding the tablename to your areaID in the subquery or renaming the reserved word count to cnt.
SELECT m_area.id
, m_area.cn_areaName
, m_area.de_areaName
, m_area.en_areaName
,m_area.jp_areaName
,t_shop.cnt
FROM m_area
left join (
select t_shop.areaID
, count(t_shop.areaID) AS cnt
from t_shop
group by t_shop.areaID
) t_shop on m_area.id = t_shop.areaID