MySQL delete query is not working as expected - mysql

I am running a query directly in MySQL Workbench. It is a work in progress, but I can't get past this issue.
The query is as follows:
#Temporary table to hold dcsIds that are past grace period
CREATE TABLE pastGracePeriod (
dcsId varchar(50) NOT NULL
);
INSERT INTO pastGracePeriod
SELECT dcsId FROM loyaltyOptOutGracePeriod
WHERE optOutDate <= ADDDATE(CURRENT_TIMESTAMP(), INTERVAL -15 DAY);
#temporary table to hold dcsIds that are validated against subscriptions table
CREATE TABLE validatedDcsIds (
dcsId varchar(50) NOT NULL
);
INSERT INTO validatedDcsIds
SELECT subscriptions.dcsId
FROM subscriptions
INNER JOIN pastGracePeriod ON subscriptions.dcsId = pastGracePeriod.dcsId
WHERE subscriptions.optOutDate <= ADDDATE(CURRENT_TIMESTAMP(), INTERVAL -15 DAY)
AND subscriptions.subscriptionId IN (24,25,30)
AND subscriptions.optInStatus='N';
#delete items in validatedDcsIds table from externalId table
DELETE FROM externalId
WHERE EXISTS (SELECT dcsId FROM validatedDcsIds
WHERE externalId.dcsId = validatedDcsIds.dcsId);
DROP TABLE pastGracePeriod;
DROP TABLE validatedDcsIds;
The query takes dcsIds (PK) from a table called loyaltyOptOutGracePeriod, validates them against another table called subscriptions, then takes those validated dcsIds and deletes them from a table called externalId.
The query works fine until it gets to the DELETE clause. In the externalId table, the PK is a composite key consisting of fields dcsId, appId, and appName. As it is written above, I get Error 1175: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column.
If I change it to:
DELETE FROM externalId
WHERE EXISTS (SELECT dcsId FROM validatedDcsIds
WHERE externalId_backup.dcsId = validatedDcsIds.dcsId)
AND appId <> NULL;
I get no errors, but nothing gets deleted from the externalId table as expected.
I am at a loss, any suggestions are very welcome!
NOTE: I do not need to know how to disable safe update mode.

Do not use WHERE EXISTS. Use Multiple-Table Syntax:
DELETE table1
FROM table1
JOIN table2 ON {joining conditions}
WHERE {additional conditions}

Related

Multiple conditions on a temporary table - MySQL

I have a temporary table called 'tempaction'. I wanted to select rows where 'ActionID' matches that of another table. I got the safe update mode error, I think as ActionID is part of a compound primary key. However, when I try
UPDATE action
SET Status = 'Sent'
WHERE ActionID IN( select ActionID from tempaction)
AND DeviceID IN( select DeviceID from tempaction);
I get temporary table cannot be reopened error.
Checking both parts of primary key has worked for the safe update error in the past. I also understand that I cannot reference a temporary table twice in the same statement.
How can I select rows with matching ActionID's or matching ActionID's AND DeviceID's from this temporary table?
Tempory Table
CREATE TEMPORARY TABLE tempaction (ActionID BIGINT)
SELECT *
FROM action
WHERE DeviceID = '1234'
AND Status = 'Pending'
You can try Update using Join with sub-query.
UPDATE action a
JOIN
tempaction t ON a.ActionID = t.ActionID
SET
a.Status = 'Sent';

SQL normalized data INSERT WHERE NOT EXISTS ; ON DUPLICATE KEY UPDATE

i am using MySql Workbench version 6.3.9 with mySql 5.6.35.
i have the following tables:
EQUIPMENT
eID | caochID | eName
COACH
coachID | coachName
SQLfiddle prepared http://sqlfiddle.com/#!9/e333d/1
eID is a primary key. there are multiple coachID's in different equipment, so there will be duplicate coachIDs with different equipment, but the eID will be unique as it is a primary key.
REQUIRED
i need to insert a row in the equipment table, if it does not already exist. If it exists, do nothing.
various posts online have pointed me towards two options:
a) INSERT...ON DUPLICATE KEY UPDATE...
b)INSERT...WHERE NOT EXISTS
PROBLEM i have problems with both of these solutions. for the first solution (ON DUPLICATE KEY UPDATE) the query inserts the row as required but does not update the existing row. instead it creates a new entry. for the second solution (WHERE NOT EXISTS) i get an error : SYNTAX ERROR: 'WHERE' (WHERE) is not a valid input at this position.
the sql query doesnt need to make any joins. i listed both tables so that you can see how they are related. the insert query i need will only insert for the equipment table.
You can insert by using a tmp table and ensuring that the same record is not existing from current table. Add limit 1 to ensure only one record is inserted. Below query will not insert since 1 and small ball exists.
INSERT INTO `Equipment` (`c_id`, `eName`)
SELECT * FROM (SELECT '1', 'small ball') tmp
WHERE NOT EXISTS (
SELECT c_id FROM Equipment WHERE `c_id`='1' and `eName` = 'small ball'
) LIMIT 1;
NOT EXISTS
insert into table2 (....) --- all if not columns ... destination
select ....
from table1 t1 --- source of data to check
where not exists (
select 1
from table2 t2
where t2.col = t1.col --- match source and destination table making sure table1 data is not in table2
)

Delete Duplicates from large mysql Address DB

I know, deleting duplicates from mysql is often discussed here. But none of the solution work fine within my case.
So, I have a DB with Address Data nearly like this:
ID; Anrede; Vorname; Nachname; Strasse; Hausnummer; PLZ; Ort; Nummer_Art; Vorwahl; Rufnummer
ID is primary Key and unique.
And i have entrys for example like this:
1;Herr;Michael;Müller;Testweg;1;55555;Testhausen;Mobile;012345;67890
2;Herr;Michael;Müller;Testweg;1;55555;Testhausen;Fixed;045678;877656
The different PhoneNumber are not the problem, because they are not relevant for me. So i just want to delete the duplicates in Lastname, Street and Zipcode. In that case ID 1 or ID 2. Which one of both doesn't matter.
I tried it actually like this with delete:
DELETE db
FROM Import_Daten db,
Import_Daten dbl
WHERE db.id > dbl.id AND
db.Lastname = dbl.Lastname AND
db.Strasse = dbl.Strasse AND
db.PLZ = dbl.PLZ;
And insert into a copy table:
INSERT INTO Import_Daten_1
SELECT MIN(db.id),
db.Anrede,
db.Firstname,
db.Lastname,
db.Branche,
db.Strasse,
db.Hausnummer,
db.Ortsteil,
db.Land,
db.PLZ,
db.Ort,
db.Kontaktart,
db.Vorwahl,
db.Durchwahl
FROM Import_Daten db,
Import_Daten dbl
WHERE db.lastname = dbl.lastname AND
db.Strasse = dbl.Strasse And
db.PLZ = dbl.PLZ;
The complete table contains over 10Mio rows. The size is actually my problem. The mysql runs on a MAMP Server on a Macbook with 1,5GHZ and 4GB RAM. So not really fast. SQL Statements run in a phpmyadmin. Actually i have no other system possibilities.
You can write a stored procedure that will each time select a different chunk of data (for example by rownumber between two values) and delete only from that range. This way you will slowly bit by bit delete your duplicates
A more effective two table solution can look like following.
We can store only the data we really need to delete and only the fields that contain duplicate information.
Let's assume we are looking for duplicate data in Lastname , Branche, Haushummer fields.
Create table to hold the duplicate data
DROP TABLE data_to_delete;
Populate the table with data we need to delete ( I assume all fields have VARCHAR(255) type )
CREATE TABLE data_to_delete (
id BIGINT COMMENT 'this field will contain ID of row that we will not delete',
cnt INT,
Lastname VARCHAR(255),
Branche VARCHAR(255),
Hausnummer VARCHAR(255)
) AS SELECT
min(t1.id) AS id,
count(*) AS cnt,
t1.Lastname,
t1.Branche,
t1.Hausnummer
FROM Import_Daten AS t1
GROUP BY t1.Lastname, t1.Branche, t1.Hausnummer
HAVING count(*)>1 ;
Now let's delete duplicate data and leave only one record of all duplicate sets
DELETE Import_Daten
FROM Import_Daten LEFT JOIN data_to_delete
ON Import_Daten.Lastname=data_to_delete.Lastname
AND Import_Daten.Branche=data_to_delete.Branche
AND Import_Daten.Hausnummer = data_to_delete.Hausnummer
WHERE Import_Daten.id != data_to_delete.id;
DROP TABLE data_to_delete;
You can add a new column e.g. uq and make it UNIQUE.
ALTER TABLE Import_Daten
ADD COLUMN `uq` BINARY(16) NULL,
ADD UNIQUE INDEX `uq_UNIQUE` (`uq` ASC);
When this is done you can execute an UPDATE query like this
UPDATE IGNORE Import_Daten
SET
uq = UNHEX(
MD5(
CONCAT(
Import_Daten.Lastname,
Import_Daten.Street,
Import_Daten.Zipcode
)
)
)
WHERE
uq IS NULL;
Once all entries are updated and the query is executed again, all duplicates will have the uq field with a value=NULL and can be removed.
The result then is:
0 row(s) affected, 1 warning(s): 1062 Duplicate entry...
For newly added rows always create the uq hash and and consider using this as the primary key once all entries are unique.

How to assign a foreign key value using a a before insert trigger

I have a scenario like this:
There are two tables table1 and table2. The table1 has a primary key pkey and table2 has a foreign key fkey now during an insert if the foreign key is provided the value should be inserted as it is. Otherwise, it has to get a primary key from table1 using some computation and determine the foreign key to be inserted. How do i do this??
I am using MySql 5.0
EDIT
In my scenario, table1 holds the billing details, that is, the table1 has the bills and the total amount to be paid. The customer pays some amount of the total outstanding balance or will pay for a particular bill. What i want to do here is. When i am not provided with a bill_id (which is primary key in table1 and foreign key in table2) i would like to search for the oldest bill that is due in table1 and deduct the amount due and further deduct the remaining amount if any from the next bill in the billed order. I want to do this in the database layer rather than the upper layer. So when an insert is being done without a value for the foreign key the value should be retrieved and placed by the trigger or otherwise directly inserted. How do i achieve this?
Using the answers provided here, i tried this:
CREATE DEFINER=`root`#`localhost` TRIGGER `inflow_pay_done_insert` BEFORE INSERT ON `inflow_pay_done` FOR EACH ROW BEGIN
DECLARE pkey INT;
SET pkey = (SELECT bill_id from inflow_bills where payment_stat = 0 and rs_id = NEW.rs_id order by time_stamp limit 1);
SET NEW.bill_id = IF(NEW.bill_id , NEW.bill_id , pkey);
UPDATE raw_mat_sup rms SET rms.outstanding_bal_payable = rms.outstanding_bal_payable - NEW.amount where rms.rs_id = NEW.rs_id;
END|
and i am getting the following error when i am trying to insert in inflow_pay_done:
/* SQL Error (1048): Column 'bill_id' cannot be null */
you could use a subquery in the BEFORE INSERT trigger for this..
DELIMITER |
DROP TRIGGER `inflow_pay_done_insert`|
CREATE TRIGGER `inflow_pay_done_insert` BEFORE INSERT ON `inflow_pay_done`
FOR EACH ROW
BEGIN
UPDATE raw_mat_sup rms
SET rms.outstanding_bal_payable = rms.outstanding_bal_payable - NEW.amount
WHERE rms.rs_id = NEW.rs_id;
NEW.bill_id = IF(NEW.bill_id,
/* if "bill_id" is provided in INSERT statement, use provided value */
NEW.bill_id,
/* if not, query other table for the correct value */
( /* this subquery is just an example, put your own query here*/
SELECT bill_id FROM inflow_bills
/* find customers newest bill based on newest date and customer id */
WHERE payment_stat = 0 AND rs_id = NEW.rs_id
ORDER BY time_stamp DESC LIMIT 1
)
);
END;
|
delimiter;
UPDATE
Because of a MySQL Bug, this will only work when the column is allowed to be NULL and there is no constraint on the column (-> foreign key). The reason is that MySQL, unlike other DBMS, checks for constraints before a BEFORE INSERT trigger is executed and effectively avoids the execution of the trigger which would correct the data to insert.
The only solution for this, until the behaviour of MySQL changes, is to use a STORED PROCEDURE instead of plain INSERT. The stored procedure is then called with the values that should be inserted. In the procedure, the data correction (like in this case: selecting the right bill_id) is done and then INSERT is executed from within the stored procedure.
UPDATE II
This bug seems to be fixed in 5.7.1. Changelog says:
If a column is declared as NOT NULL, it is not permitted to insert
NULL into the column or update it to NULL. However, this constraint
was enforced even if there was a BEFORE INSERT (or BEFORE UPDATE
trigger) that set the column to a non-NULL value. Now the constraint
is checked at the end of the statement, per the SQL standard.

Delete statement in a same table

I need to query a delete statement for the same table based on column conditions from the same table for a correlated subquery.
I can't directly run a delete statement and check a condition for the same table in mysql for a correlated subquery.
I want to know whether using temp table will affect mysql's memory/performance?
Any help will be highly appreciated.
Thanks.
You can make mysql do the temp table for you by wrapping your "where" query as an inline from table.
This original query will give you the dreaded "You can't specify target table for update in FROM clause":
DELETE FROM sametable
WHERE id IN (
SELECT id FROM sametable WHERE stuff=true
)
Rewriting it to use inline temp becomes...
DELETE FROM sametable
WHERE id IN (
SELECT implicitTemp.id from (SELECT id FROM sametable WHERE stuff=true) implicitTemp
)
Your question is really not clear, but I would guess you have a correlated subquery and you're having trouble doing a SELECT from the same table that is locked by the DELETE. For instance to delete all but the most recent revision of a document:
DELETE FROM document_revisions d1 WHERE edit_date <
(SELECT MAX(edit_date) FROM document_revisions d2
WHERE d2.document_id = d1.document_id);
This is a problem for MySQL.
Many examples of these types of problems can be solved using MySQL multi-table delete syntax:
DELETE d1 FROM document_revisions d1 JOIN document_revisions d2
ON d1.document_id = d2.document_id AND d1.edit_date < d2.edit_date;
But these solutions are best designed on a case-by-case basis, so if you edit your question and be more specific about the problem you're trying to solve, perhaps we can help you.
In other cases you may be right, using a temp table is the simplest solution.
can't directly run a delete statement and check a condition for the same table
Sure you can. If you want to delete from table1 while checking the condition that col1 = 'somevalue', you could do this:
DELETE
FROM table1
WHERE col1 = 'somevalue'
EDIT
To delete using a correlated subquery, please see the following example:
create table project (id int);
create table emp_project (id int, project_id int);
insert into project values (1);
insert into project values (2);
insert into emp_project values (100, 1);
insert into emp_project values (200, 1);
/* Delete any project record that doesn't have associated emp_project records */
DELETE
FROM project
WHERE NOT EXISTS
(SELECT *
FROM emp_project e
WHERE e.project_id = project.id);
/* project 2 doesn't have any emp_project records, so it was deleted, now
we have 1 project record remaining */
SELECT * FROM project;
Result:
id
1
Create a temp table with the values you want to delete, then join it to the table while deleting. In this example I have a table "Games" with an ID column. I will delete ids greater than 3. I will gather the targets in a temp table first so I can report on them later.
DECLARE #DeletedRows TABLE (ID int)
insert
#DeletedRows
(ID)
select
ID
from
Games
where
ID > 3
DELETE
Games
from
Games g
join
#DeletedRows x
on x.ID = g.ID
I have used group by aggregate with having clause and same table, where the query was like
DELETE
FROM TableName
WHERE id in
(select implicitTable.id
FROM (
SELECT id
FROM `TableName`
GROUP by id
HAVING count(id)>1
) as implicitTable
)
You mean something like:
DELETE FROM table WHERE someColumn = "someValue";
?
This is definitely possible, read about the DELETE syntax in the reference manual.
You can delete from same table. Delete statement is as follows
DELETE FROM table_name
WHERE some_column=some_value