MySQL 1443: what does it mean? - mysql

I'm trying to do an update, in MySQL 5.0, of the form:
update mytable.myfield t
set f = 'blah'
where t.id in (select v.id from myview v where ...);
MySQL tells me:
ErrorNr. 1443
The definition of table 'v' prevents operation UPDATE on table 't'.
The MySQL docs list this error, but (as usual) don't say what it means. Can anyone shed some light on this for me? I only reference the view in the subquery, and I only reference the table in the main query, and I don't know why these would prevent the update. The only thing I found with google is a bug in the MySQL bug db related to triggers, but (AFAIK) there are no triggers in my db.

I think the view myview must be based on the table mytable, so that as it makes changes to myfield, it loses track of what's in the view and therefore makes for an illegal update.
I would recommend looking at the definition of myview, so that you can write your query without referencing it. Then you may be able to work it out.
Alternatively, dump the list of ids to a temporary table and use that for your subquery.
Rob

Related

mysql triggers using vs select query

I use mysql trigger to update column in one of ,y DB tables called comments_count but I want to know what is best and faster??
Using mysql triggers or select query like this below:
select count(*) from comments where discussion_id=something
different types of overhead:
with the trigger you will have extra time during insert, and may get out of synch over time for some unforseen reason.
with the query, you will always get the right answer but you will need to calculate at runtime. usually, this should be very fast especially with an index on the discussion_id

Mysql 5.6.12 bug : error 1356 when using order by alias in a view definition

I have a very simple Query, which works fine:
SELECT *
FROM images i
INNER JOIN v_images_stats s
ON i.id = s.id
By SELECT * I end up creating duplicate column names so I edited the query to be more specific
and ignore the duplicate column names but MySQL throws a #1356 error:
SELECT i.is as id, s.id as imageid
FROM images i
INNER JOIN v_images_stats s
ON i.id = s.id
#1356 - View 'events.v_image_stats' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
The v_image_stats view and the underlying views and tables it is based have the correct privileges, I'm completely flummoxed.
I'm using MySQL 5.6 and phpmyadmin to create and manage the views, I am also accessing the DB via PHP PDO within the Phalcon framework but I'm pretty sure this a MySQL issue rather than anything else.
Any help gratefully received.
I originally posted the full output of SHOW CREATE VIEW but it was too verbose, here is the simplest example I can create to recreate the problem:
CREATE ALGORITHM=UNDEFINED DEFINER=`events`#`localhost` SQL SECURITY DEFINER VIEW `v_image_stats` AS
(
select it.*, 1 AS `my_alias`
from `v_image_totals` `it`
order by `my_alias` asc)
)
It also fails if I use a table instead of a view in the FROM clause.
This appears to be a bug affecting MySQL 5.6.10 and above (I have not tested against MySQL 5.7).
I have raised a bug with MySQL so this can be tracked and hopefully fixed:
http://bugs.mysql.com/bug.php?id=69678
See the comments for an effective workaround.

DELETE with alias reference impossible without selecting a db since MySQL 5.5.3?

I used to do complex multi-table DELETEs like this:
DELETE db1.t1 FROM db1.table1 AS t1, db1.table2 AS t2...
now after upgrading to MySQL 5.5.5 that doesn't work anymore because according to documentation since 5.5.3 alias names are not allowed to be qualified with db name in a DELETE statement.
so I should do this:
DELETE t1 FROM db1.table1 AS t1, db1.table2 AS t2...
which doesn't work either due to bug #61376 and gives ERROR 1046 (3D000): No database selected.
I don't USE a database because my code should be integrateable in unknown environments and selecting a db might break existing code.
Should I assume that DELETE statement with alias reference is impossible without selecting a database since MySQL 5.5.3 and I should start looking for a dirty hack or do you know a clean solution to this?
update:
Adding a USE db1 before this query when there is no db selected (Bill Karwin's suggestion):
DELETE t1 FROM db1.table1 AS t1, db1.table2 AS t2...
makes it work in 5.5.5, but both in 4.1 and 5.1 it gives this error:
ERROR 1109 (42S02): Unknown table 't1' in MULTI DELETE
I think for now I'm going to give up using aliases altogether and instead use full db.table name everywhere which makes the queries big and unreadable but I can't think of any better solution.
I would just ready a default database. It's unfortunate that it's necessary, but USE db1 is really not a heavyweight statement to run, compared to any actual query that accesses a table.
You don't have to do two queries for every DELETE. The SELECT DATABASE() shown in the bug report isn't necessary, it's just showing that no default database is selected.
You only need to ready a default database once per session, e.g. right after you connect, not before every statement.
Re your comment:
If you're USEing another database, then the error goes away. It's only when you have no database as your default that you have an error with the aliases. You don't necessarily have to ready db1 to delete from it.
I don't have time to test all the permutations right now, so part of this is based on assumption.
Re your update:
MySQL has changed behavior a couple of times over the years. I suggest you recommend upgrade any MySQL prior to 5.1, and make sure you're using the InnoDB plugin. Not only will it simplify your development, but you'll gain the scalability improvements they've made in recent years.
MySQL 4.1/5.0/5.1 says:
As of MySQL 4.1.2, aliases can be used, but for alias references in the list of tables from which to delete rows, the default database is used unless one is specified explicitly.
To correctly match an alias that refers to a table outside the default database, you must explicitly qualify the reference with the name of the proper database:
DELETE a1, db2.a2 FROM db1.t1 AS a1 INNER JOIN db2.t2 AS a2
WHERE a1.id=a2.id;
MySQL 5.5 says:
As of MySQL 5.5.3, alias resolution does not require qualification and alias references should not be qualified with the database name. Qualified names are interpreted as referring to tables, not aliases.
So you're right, you can't uses aliases in a multi-table delete spanning multiple databases, and have the same code work against 4.1/5.0/5.1 versus 5.5. The workaround is to skip aliases and give the full names of tables, qualified by database.
This should work, except for cases when you have a multi-table DELETE involving both a self-join and a cross-database join.
My solution was to prefix each table alias with the name of the schema/database.
So instead of this:
DELETE a1, db2.a2 FROM db1.t1 AS a1 INNER JOIN db2.t2 AS a2 WHERE a1.id=a2.id;
You have to do it like this:
DELETE db1.a1, db2.a2 FROM db1.t1 AS a1 INNER JOIN db2.t2 AS a2 WHERE a1.id=a2.id;

Invalid Object Name for Table which is successfully updated in the same Stored Procedure

We are seeing 'interesting behavior with our SQL Sever Database. We have a merge statement which selects a table X. In the match clauses there is a subselect to table X. When we execute the stored procedure from the SQL Server Tools it works fine. But when executed from IPC (an ETL Tool) we get an exception Invalid object name 'X'.
So far nothing special as I understand there can get lots of things wrong with permissions and stuff.
The strange thing: The merge statement is in a try block and in the catch block the error message gets written into the table X via an update statement! How is this possible when Sql Server complains it can't find a table X?
Also everything works fine with another stored procedure which is constructed in the same way (via code generation) but on a different set of tables.
The code looks like this
merge ...
using
(select ...
from dbo.X
where ...
when not matched by target
and not exists (select 1 from dbo.X q2 where ...)
then insert (...
)
values (...
)
when matched and q.ACTION='D'
then delete
when matched AND NOT exists (select 1 from dbo.X q3 where ...)
then update
set
...
OUTPUT $action INTO #l_SummaryOfChanges;
-- Query the results of the table variable.
SELECT ACTION, COUNT(*) AS CountPerChange
FROM #l_SummaryOfChanges
GROUP BY ACTION;
end try
begin catch
update dbo.X
set LAST_ERROR_MSG=ERROR_MESSAGE(), ERROR_COUNTER=ERROR_COUNTER+1
where SYNC_ID=#l_SyncID
end catch
Any ideas what is going on?Invalid object name 'sync$_tabTeiledaten'.
We found it. gbn's question triggered the realization that the usage of X had nothing to do with the exception. In fact on target table of the merge was a trigger which is referencing X but from a different schema without actually specifying the schema.
May somebody will benefit from the way we debugged this shit:
we duplicated X with a new name (Y) and still got the error message saying 'Invalid object name 'X'. At that point we thought we might reference a view or something so ..
we removed all the columns (there where lots of) from the merge statement, except those which where necessary due to Not Null Constraints. The problem persisted
We removed 1 of the branches of the merge statement at a time. The problem persisted.
we removed the complete merge statement. The error was gone. At that point we realized that something fishy might go on with the target table.
On inspection we found the trigger from hell.

Is it possible a trigger on a select statement with MySQL?

I know that triggers can be used on insert, update and delete, but what about a trigger (or sort of) on a select statement. I want to use a trigger to insert data on a table B when it is selected an existent record on a table A, it could be possible?.
Thanks in advance.
You should design your application so that database access occurs only through certain methods, and in those methods, add the monitoring you need.
Not exactly a trigger, but you can:
CREATE FUNCTION myFunc(...) BEGIN INSERT INTO myTable VALUES(...) END;
And then
SELECT myFunc(...), ... FROM otherTable WHERE id = 1;
Not an elegant solution, though.
It is not possible in the database itself.
However there are monitoring/instrumentation products for databases (e.g. for Sybase - not sure about MySQL) which track every query executed by the server, and can do anything based on that - usually store the query log into a data warehouse for later analysis, but they can just as well insert a record into table B for you, I would guess.
You can write an application which will be monitoring the query log and doing something when a select occurs. A pretty crude way to solve the problem though...