Delete, Update with derived tables? - mysql

I have just studied FROM clause and derived tables in mysql and most of the websites provided the examples using SELECT command
Example SELECT * FROM (SELECT * FROM usrs) as u WHERE u.name = 'john'
But when I have tried using delete or update command it does not seem to work.
Example DELETE FROM (SELECT * FROM usrs) as u WHERE u.name = 'john'
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 * FROM usrs) as u WHERE u.name = 'john' at line
UPDATE (SELECT * FROM usrs) as u SET u.lname ='smith' WHERE u.name = 'john'
1288 The target table e of the UPDATE is not updatable
So derived tables does not work with delete or update commands? or is there a way to make it work.
Instead of writing the table name for update and delete I want to write a subquery that gets the records and perform the delete operation on that records? Is that possible in mysql?
UPDATED I have to delete a record and i have three tables, the record may exist in any of the table
My approach delete from first table rows effected? quit: else check second table rows effected? quit : else check third table
But if I use UNION ALL I can do this way
Delete from (select * from tb1 union all select * from tb2 union all select * from tb3) e as e.uname = 'john'
but this query does not seem to work , now could anyone tell me how do i delete or update a record when i have more than one table to search. Any help is greatly appreciated.

You can't directly delete from the subquery, but you can still use it if you'd like, you'll just need to use it in a JOIN:
DELETE usrs
FROM usrs
INNER JOIN (
SELECT * FROM usrs WHERE name = 'john'
) t ON usrs.Id = t.Id
Or you could use IN:
DELETE usrs
WHERE ID IN (
SELECT ID
FROM usrs
WHERE name = 'John'
)
With this said, for this example, I don't know why you'd want a subquery:
DELETE usrs WHERE name = 'John'
Edit base on comments. To delete from multiple tables at the same time, you can either have multiple DELETE statements, or you can use something like the following:
delete t1, t2, t3
from (select 'john' as usr) t
left join t1 on t.usr=t1.usr
left join t2 on t.usr=t2.usr
left join t3 on t.usr=t3.usr
SQL Fiddle Demo

Derived tables exist only for the duration of the parent query they're a member of. Assuming that this syntax and the operations were allowed by MySQL, consider what happens:
a) Your main query starts executing
b) the sub-query executes and returns its results as a temporary table
c) the parent update changes that temporary table
d) the parent query finishes
e) temporary tables are cleaned up and deleted
Essentially you'll have done nothing except waste a bunch of cpu cycles and disk bandwidth.
UPDATE queries DO allow you to join against other tables to use in the WHERE clause, e.g..
UPDATE maintable
LEFT JOIN othertable ON maintable.pk = othertable.fk
SET maintable.somefield='foo'
WHERE othertable.otherfield = 'bar'

Related

Error 1093 when trying to perform delete in MySQL

A little help with this query please. When trying to perform a delete in MySQL, it returns an error: "MySQL Error 1093 - Can't specify target table for update in FROM clause". The select statement works fine otherwise. Here's the script:
DELETE
FROM redcap_data
WHERE record IN (SELECT DISTINCT
redcap_data.record,
redcap_data.field_name,
redcap_data.value
FROM redcap_metadata
INNER JOIN redcap_data
ON redcap_metadata.project_id = redcap_data.project_id
INNER JOIN redcap_events_metadata
ON redcap_data.event_id = redcap_events_metadata.event_id
INNER JOIN redcap_events_arms
ON redcap_events_metadata.arm_id = redcap_events_arms.arm_id
WHERE (redcap_data.project_id = '50'
AND redcap_events_arms.arm_num = '6'
AND redcap_data.record IN ('record_ids go here')
))
Try using an inner join on the subselect you are using as IN clause instead of the IN clause
DELETE r.*
FROM redcap_data r
INNER JOIN (
SELECT DISTINCT
redcap_data.record,
redcap_data.field_name,
redcap_data.value
FROM redcap_metadata
INNER JOIN redcap_data
ON redcap_metadata.project_id = redcap_data.project_id
INNER JOIN redcap_events_metadata
ON redcap_data.event_id = redcap_events_metadata.event_id
INNER JOIN redcap_events_arms
ON redcap_events_metadata.arm_id = redcap_events_arms.arm_id
WHERE (redcap_data.project_id = '50'
AND redcap_events_arms.arm_num = '6'
AND redcap_data.record IN ('record_ids go here')
)) t ON r.record = t.record
It's a documented restriction in MySQL.
One workaround is to use an inline view (derived table), and reference that in a JOIN operation.
Write it as SELECT first
SELECT t.*
FROM target_table t
JOIN ( SELECT q.id
FROM target_table q
JOIN ... r
ON ...
WHERE ...
GROUP BY ...
HAVING ...
) s
ON s.id = t.id
Then we can convert that to a DELETE statement. In my experience, the inline view gives enough separation of target_table in the outer query, that it doesn't raise the MySQL error.
At the time the outer query runs, the query that materializes derived table s has already been executed. The operations that MySQL performs is akin to this (not exactly this, but this illustrates the pattern, how we can think about the derived table ...
CREATE TEMPORARY TABLE _s_ ... ;
INSERT INTO _s_ (...) SELECT ... FROM target_table q ... ;
DELETE t.*
FROM target_table t
JOIN _s_
ON ...
;
DROP TEMPORARY TABLE _s_ ... ;
At the time the DELETE statement runs, it doesn't matter that target_table is referenced in the preceding SELECT. The work to materialize the temporary table s has already been done.

MySql - Multitable - AND is not the correct choice, but what is?

I have two tables:
mytable1
UserId (int) (primary_key)
Save (blob)
mytable2
UserId (int) (primary_key)
Save (blob)
I make the following mysql command:
UPDATE mytable1 tb1, mytable2 tb2 SET tb1.Save='', tb2 .Save='' WHERE tb1.UserId=25 AND dbSv1.UserId=25
When both tables have a user with UserId = 25, then this works and Save is set to ''. However, if one table does not have a user with UserId = 25, but the other one does, then Save is not set to '' in the one that does. This is not the behaviour I want.
OR is not the thing to use, as other Saves will be set to '' which do not have an UserId of 25. So what do I need?
Your query is using the old-school comma syntax for a join operation. (There's some problems in the SQL... dbSv1 is used as a qualifier, but it doesn't appear as a table name or table alias. We're going to assume that was supposed to be tb2.
Your query is equivalent to:
UPDATE mytable1 tb1
JOIN mytable2 tb2
SET tb1.save=''
, tb2.save=''
WHERE tb1.userid=25
AND tb2.userid=25
If a matching row is not found in either tb1 or tb2, the the JOIN operation will produce an empty set. This is expected behavior.
Consider the result set returned from this query:
SELECT tb1.userid
, tb2.userid
FROM mytable1 tb1
JOIN mytable2 tb2
WHERE tb1.userid=25
AND tb2.userid=25
when there are no rows in tb2 that satisfy the predicates, the query won't return any rows.
You could use an "outer" join to make returning rows from one of the tables optional. For example, to update mytable1 even when no matching rows exist in mytable2...
UPDATE mytable1 tb1
LEFT
JOIN mytable2 tb2
ON tb2.userid=25
SET tb1.save=''
, tb2.save=''
WHERE tb1.userid=25
If there are no rows in mytable1 that have userid=25, then this won't update any rows.
MySQL doesn't support FULL OUTER JOIN. But you try something like this, using an inline view to return a row, and then performing outer joins to both mytable1 and mytable2...
UPDATE ( SELECT 25 + 0 AS userid ) i
LEFT
JOIN mytable1 tb1
ON tb1.userid = i.userid
LEFT
JOIN mytable2 tb2
ON tb2.userid = i.userid
SET tb1.save = ''
, tb2.save = ''
SQLFiddle demonstration: http://sqlfiddle.com/#!9/6f8598/1
FOLLOWUP
A "join" is a common SQL operation. You shouldn't have any trouble finding out information about what that is what it does.
The "+ 0" isn't strictly necessary. It's a convenient shorthand in MySQL to CAST to numeric. As a test, see what MySQL returns for this:
SELECT '25' + 0
, '25xyz' + 0
, 'abc' + 0
The purpose of the inline view was to return a single row. We could have written the query to hardcode the user_id two times, and ignore what's returned from the line view ....
SELECT t1.user_id AS t1_user_id
, t2.user_id AS t2_user_id
FROM ( SELECT 'foo' AS dontcare ) i
LEFT
JOIN mytable1 t1
ON t1.user_id = 25
LEFT
JOIN mytable t2
ON t2.user_id = 25
My preference is to make it more clear that our intent is for both of the values to be the same. We could code where one of them is 23 and the other is 27. That's syntactically valid to do that. When we convert this to a prepared statement with bind placeholders...
SELECT t1.user_id AS t1_user_id
, t2.user_id AS t2_user_id
FROM ( SELECT 'foo' AS dontcare ) i
LEFT
JOIN mytable1 t1
ON t1.user_id = ?
LEFT
JOIN mytable t2
ON t2.user_id = ?
We kind of "lose" the idea that those two values are the same. To get that hardcoded value specified only one time, I have the inline view return the value we want to "match" in the ON clause of the outer joins.
SELECT t1.user_id AS t1_user_id
, t2.user_id AS t2_user_id
FROM ( SELECT ? AS user_id ) i
LEFT
JOIN mytable1 t1
ON t1.user_id = i.user_id
LEFT
JOIN mytable t2
ON t2.user_id = i.user_id
Now my intent is more clear... I'm looking for "one" user_id value. Adding the "+ 0" indicates that whatever value gets passed in (e.g. '25', 'foo', or whatever), my statement is going to interpret that as a numeric value.
inline view
I used the term "inline view". That's just a SELECT query used in a context where we usually have a table.
e.g. if i have a table named mine, i can write a query...
SELECT m.id, m.name FROM mine m
test it and see that it returns rows, yada, yada.
I can also do this: wrap that query in parens and reference it in place of a table in another statement, like this...
SELECT t.*
FROM ( SELECT m.id, m.name FROM mine m ) t
MySQL requires that we assign an alias to that, like we can do if it were a table. We call that an inline view because it's similar to the pattern we use for a stored view. Let's look at a demonstration of doing that.
(This is just a demonstration of the pattern; there's some reasons we wouldn't want to do this.)
CREATE VIEW myview
AS
SELECT m.id, m.name FROM mine m
;
Then we can do this:
SELECT t.* FROM myview t
With the inline view we're following the same pattern, but we're bypassing a separate create view statement. (That's a DDL statement that causes an implicit commit, and creating a database object.) Bypassing that, we're effectively creating a view that exists only in the context of the statement, and doing that "inline", within the statement.
SELECT t.* FROM ( SELECT m.id, m.name FROM mine m ) t
The MySQL documentation refers to the inline view as a "derived table". If we (accidentally) forget the alias, the error we get back says something like "every derived table must have a alias". The more general term, used for databases other than MySQL is "inline view".

SQL syntax error. MySQL Update table SELECT

I want to update a row based on the existence of an id in other table that is also in that table. In other words that id is a primary key in other table and just a column in the table I want to update.
I want to update based in the same id.
I have this query but it doesn't work because of the SQL syntax.
UPDATE
transaction
SET
DaysRented = 3,
Cost = 3,
TotalCost= 5
FROM
transaction
INNER JOIN
rentals
ON
transaction.idRentals = rentals.idRentals;
syntax for mysql
http://www.mysqltutorial.org/mysql-update-join/
First, you specify the main table ( T1) and the table that you want
the main table to join to ( T2) after the UPDATE clause. Notice that
you must specify at least one table after the UPDATE clause. The
data in the table that is not specified after the UPDATE clause is
not updated.
Second, you specify a kind of join you want to use i.e., either
INNER JOIN or LEFT JOIN and a join condition. Notice that the JOIN
clause must appear right after the UPDATE clause.
Third, you assign new values to the columns in T1 and/or T2 tables
that you want to update.
UPDATE transaction
INNER JOIN rentals ON transaction.idRentals = rentals.idRentals
SET DaysRented = 3,
Cost = 3,
TotalCost = 5;
You are using SQL Server update/join syntax. The proper MySQL syntax is:
UPDATE transaction INNER JOIN
rentals
ON transaction.idRentals = rentals.idRentals
SET DaysRented = 3,
Cost = 3,
TotalCost = 5;

Eliminating duplicates from SQL query

What would be the best way to return one item from each id instead of all of the other items within the table. Currently the query below returns all manufacturers
SELECT m.name
FROM `default_ps_products` p
INNER JOIN `default_ps_products_manufacturers` m ON p.manufacturer_id = m.id
I have solved my question by using the DISTINCT value in my query:
SELECT DISTINCT m.name, m.id
FROM `default_ps_products` p
INNER JOIN `default_ps_products_manufacturers` m ON p.manufacturer_id = m.id
ORDER BY m.name
there are 4 main ways I can think of to delete duplicate rows
method 1
delete all rows bigger than smallest or less than greatest rowid value. Example
delete from tableName a where rowid> (select min(rowid) from tableName b where a.key=b.key and a.key2=b.key2)
method 2
usually faster but you must recreate all indexes, constraints and triggers afterward..
pull all as distinct to new table then drop 1st table and rename new table to old table name
example.
create table t1 as select distinct * from t2; drop table t1; rename t2 to t1;
method 3
delete uing where exists based on rowid. example
delete from tableName a where exists(select 'x' from tableName b where a.key1=b.key1 and a.key2=b.key2 and b.rowid >a.rowid) Note if nulls are on column use nvl on column name.
method 4
collect first row for each key value and delete rows not in this set. Example
delete from tableName a where rowid not in(select min(rowid) from tableName b group by key1, key2)
note that you don't have to use nvl for method 4
Using DISTINCT often is a bad practice. It may be a sing that there is something wrong with your SELECT statement, or your data structure is not normalized.
In your case I would use this (in assumption that default_ps_products_manufacturers has unique records).
SELECT m.id, m.name
FROM default_ps_products_manufacturers m
WHERE EXISTS (SELECT 1 FROM default_ps_products p WHERE p.manufacturer_id = m.id)
Or an equivalent query with IN:
SELECT m.id, m.name
FROM default_ps_products_manufacturers m
WHERE m.id IN (SELECT p.manufacturer_id FROM default_ps_products p)
The only thing - between all possible queries it is better to select the one with the better execution plan. Which may depend on your vendor and/or physical structure, statistics, etc... of your data base.
I think in most cases EXISTS will work better.

MYSQL delete all results having count(*)=1

I have a table taged with two fields sesskey (varchar32 , index) and products (int11), now I have to delete all rows that having group by sesskey count(*) = 1.
I'm trying a fews methods but all fails.
Example:
delete from taged where sesskey in (select sesskey from taged group by sesskey having count(*) = 1)
The sesskey field could not be a primary key because its repeated.
DELETE si
FROM t_session si
JOIN (
SELECT sesskey
FROM t_session so
GROUP BY
sesskey
HAVING COUNT(*) = 1
) q
ON q.sesskey = si.sesskey
You need to have a join here. Using a correlated subquery won't work.
See this article in my blog for more detail:
Keeping rows
Or if you're using an older (pre 4.1) version of MySQL and don't have access to subqueries you need to select your data into a table, then join that table with the original:
CREATE TABLE delete_me_table (sesskey varchar32, cur_total int);
INSERT INTO delete_me_table SELECT sesskey, count(*) as cur_total FROM orig_table
WHERE cur_total = 1 GROUP BY sesskey;
DELETE FROM orig_table INNER JOIN delete_me_table USING (sesskey);
Now you have a table left over named delete_me_table which contains a history of all the rows you deleted. You can use this for archiving, trending, other fun and unusual things to surprise yourself with.
The SubQuery should work
Delete from taged
Where sesskey in
(Select sesskey
From taged
Group by sesskey
Having count(*) = 1)
EDIT: Thanks to #Quassnoi comment below... The above will NOT work in MySql, as MySql restricts referencing the table being updated or deleted from, in a Subquery i you must do the same thing using a Join ...