How to delete rows inserted unintentionally with a given column value - mysql

I am a beginner with MySQL. I made a stored procedure to insert 1,000 random names from a table. It has 3 fields with num, course_name and grade. num is foreign key--as this was for a test purpose, I just kept incremented the num only. So I didn't mark it as a PRIMARY KEY/AUTO_INCREMENT. I called the procedure, and it inserted 1,000 random names in the table. Unknowingly, I called the procedure again, and stopped it after some time. Then the table got 500 more entries after that previous 1000 entries. I wanted to delete the rows that created after the second procedure call.
Below is my statements in a stored procedure: (course_name and grade_details are additional tables with course names and grades.)
DELIMITER //
CREATE PROCEDURE course_grade(IN name_entries int)
BEGIN
DECLARE i int DEFAULT 0;
DECLARE course varchar(20);
DECLARE crs_grade char(1);
gradeloop : LOOP
SELECT name INTO course FROM course_name ORDER BY rand() LIMIT 1;
SELECT grade INTO crs_grade FROM grade_details ORDER BY rand() LIMIT 1;
INSERT INTO tbl_grade(fk_int_roll_no,vchr_course,vchr_grade)
VALUES (i+1,course,crs_grade);
SET i = i + 1;
IF (i=name_entries)
THEN LEAVE gradeloop;
END IF;
END LOOP gradeloop;
SELECT COUNT(*) FROM tbl_grade;
END //
DELIMITER ;
And my table is like :
+----------------+-------------+------------+
| fk_int_roll_no | vchr_course | vchr_grade |
+----------------+-------------+------------+
| 1 | AE | A |
| 2 | MECH | B |
| 3 | EC | A |
| . | .... | . |
| . | .... | . |
| 1000 | IT | E |
| 1 | MARINE | F |
| 2 | BIOTECH | F |
| . | .... | . |
| . | .... | . |
| . | .... | . |
| . | .... | . |
| 500 | RM | A |
+----------------+-------------+------------+
Wanted to delete the last 1 to 500 rows made by mistake!

Related

Calculation in Nested Stored Procedure

I'm using a data sample looking like that :
| Parent | Child | Order | Quantity |
|:-----------|:--------:| -----:|---------:|
| Meal | Element 1| 10 |1 |
| Meal | Element 2| 20 |1 |
| Element 1 | Recipe 1 | 10 |0.2 |
| Element 1 | Recipe 2 | 20 |0.5 |
| Recipe 1 | Recipe 3 | 10 |0.1 |
| Recipe 3 | Raw Mat1 | 10 |1 |
| Element 2 | Recipe 4 | 10 |0.6 |
| Element 2 | Recipe 5 | 20 |0.3 |
| Recipe 4 | Recipe 6 | 10 |1.2 |
| Recipe 6 | Raw Mat2 | 10 |1.5 |
I know the unit weight of each Recipe and Raw material but I need to calculate the weight of Element1 and 2 as well as Meal parent, which depends from the calculated weight of both elements.
My result should look like that :
| Material | Order full | Quantity | U.Weight |
|:-----------|:------------:| --------:|---------:|
| Meal | / | 1 | TOBECALC | --0.2*1+0.5*1 + 0.6*1+0.3*1
| Element 1 | /10 | 1 | TOBECALC | --0.2*1+0.5*1
| Recipe 1 | /10/10 | 0.2 | 1 |
| Recipe 3 | /10/10/10 | 0.02 | 1 |
| Raw Mat1 | /10/10/10/10 | 0.02 | 1 |
| Recipe 2 | /10/20 | 0.5 | 1 |
| Element 2 | /20 | 1 | TOBECALC | --0.6*1+0.3*1
| Recipe 4 | /20/10 | 0.6 | 1 |
| Recipe 6 | /20/10/10 | 0.72 | 1 |
| Raw Mat2 | /20/10/10/10 | 1.08 | 1 |
| Recipe 5 | /20/20 | 0.3 | 1 |
Here is what I did so far :
A procedure PROC_INCO calling PROC_DETAILED_INCO calling PROC_UNIT_INCO that inserts lines in the final table. PROC_DETAILED_INCO is recursively called for each child and this is where I implemented the weight calculation.
DELIMITER $$
CREATE PROCEDURE PROC_UNIT_INCO
(IN ID_ARTICLE INTEGER,
IN ID_ROOT_EXE INTEGER,
IN T_ORDER_FULL VARCHAR(100),
IN QUANTITY FLOAT,
OUT U_WEIGHT FLOAT)
BEGIN
DECLARE WEIGHT FLOAT;
SELECT F_WEIGHT INTO WEIGHT FROM MATERIAL WHERE I_ID = ID_ARTICLE;
IF T_ORDER_FULL IS NULL THEN
SET T_ORDER_FULL = '/';
END IF;
IF TYPEART IN ('RawMat','Recipe') THEN
SET WEIGHT = WEIGHT;
SET U_WEIGHT = WEIGHT * QUANTITY;
ELSE
SET WEIGHT = 0;
SET U_WEIGHT = 0;
END IF;
INSERT INTO FINAL_RESULT (T_CODE, TT_ORDER_FULL, F_WEIGHT, F_QUANTITY, I_ID_ROOT_EXE)
VALUES(CODEART, T_ORDER_FULL, WEIGHT, QUANTITY, ID_ROOT_EXE);
END$$
CREATE PROCEDURE PROC_DETAILED_INCO
(IN ID_ARTICLE INTEGER,
IN ID_ROOT_EXE INTEGER,
IN T_ORDER_FULL VARCHAR(100),
IN QUANTITY FLOAT,
OUT CHILD_WEIGHT FLOAT)
BEGIN
DECLARE IS_DONE INT DEFAULT FALSE;
DECLARE ID_CHILD INTEGER;
DECLARE TEMP_ORDER INTEGER;
DECLARE NEW_ORDER_FULL VARCHAR(100);
DECLARE TYPE INTEGER;
DECLARE CHILD_CURSOR CURSOR FOR
SELECT A.CHILD, A.ORDER, QUANTITY * A.QUANTITY AS QUANTITY
FROM BOM A
INNER JOIN MATERIAL B ON A.CHILD = B.CODE
WHERE A.PARENT = ID_ARTICLE
ORDER BY A.ORDER;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET IS_DONE = TRUE;
CALL PROC_UNIT_INCO (ID_ARTICLE, ID_ROOT_EXE, T_ORDER_FULL, QUANTITY, #U_WEIGHT);
SET #FIN_WEIGHT:= 0;
SELECT #U_WEIGHT INTO CHILD_WEIGHT;
OPEN FILS_CURSOR;
get_list: LOOP
FETCH FILS_CURSOR INTO ID_CHILD, TEMP_ORDER, QUANTITY, PERTE;
IF IS_DONE THEN
LEAVE get_list;
END IF;
SET NEW_ORDER_FULL = CONCAT(COALESCE(T_ORDER_FULL,''),'/',lpad(cast(TEMP_ORDER AS char(200)),5,'0'));
CALL PROC_DETAILED_INCO(ID_CHILD, ID_ROOT_EXE, NEW_ORDER_FULL, QUANTITY, #CHILD_WEIGHT);
--this is where i want to calculate my weight by summing my children's weight
SET #FIN_WEIGHT := #FIN_WEIGHT + #CHILD_WEIGHT;
END LOOP get_list;
IF TYPE NOT IN ('RawMat','Recipe') THEN
UPDATE FINAL_RESULT SET F_WEIGHT = #FIN_WEIGHT WHERE TT_ORDER_FULL = T_ORDER_FULL;
END IF;
CLOSE FILS_CURSOR;
END$$
CREATE PROCEDURE PROC_INCO
(IN CODE_ART VARCHAR(50))
BEGIN
DECLARE ID_ARTICLE INTEGER;
SELECT I_ID INTO ID_ARTICLE FROM MATERIAL WHERE T_CODE = CODE_ART;
DELETE FROM DETAILED_INCO WHERE I_ID_ROOT_EXE = ID_ARTICLE;
CALL PROC_DETAILED_INCO (ID_ARTICLE, ID_ARTICLE, NULL, 1, #CHILD_WEIGHT);
select
*
from final_result
order by tt_order_full;
END$$
DELIMITER ;
CALL PROC_INCO ('Meal');
Apologies if there is any mistakes, I simplified the procedure for better readability.
The weight calculation is not working as I want it... I do not really understand the calculated weight that I obtain but it seems that it resets each time the procedure enters in the loop and then fetches the latest child weight at the parent level.
Thanks in advance for your help on this,
Nico

SQL query - Fetch data from one column separated by comma and display it by row

I have a problem with fetching data separated by a comma. I want the
Here is my problem
Table
ID | TDNO | PREVIOUS_TD |
1 | 14 | 13,12,11 |
2 | 23 | 45,12 |
3 | 32 | 89 |
4 | 55 | NEW |
I want to have a result like this. Example when the user will choose 14 in TD the result should be like this:
ID | TD |
1 | 14 |
2 | 13 |
3 | 12 |
4 | 11 |
And when the user will choose 32 in TD the result should be like this:
ID | TD |
1 | 32 |
2 | 89 |
when the user will select 23 the result should be like this:
ID | TD |
1 | 23 |
2 | 45 |
3 | 12 |
how to achieve this?
You might try a stored procedure or function in your version of SQL. This is MySql pseudo code and could be very buggy. Some SQL flavors do not support returning tables:
create function returnCommaSepList (IN myId INT)
begin
--
-- is mtId in the source table?
SET #previousTD = (
select PREVIOUS_TD
from TheTable
where ID = myId
)
--
-- if the result is NULL then id was not in the table, return
if #previousTD IS NULL then return
--
-- create a temporary table
create table #temp (
id INT primary key autoincrement,
td int
)
--
-- add myId to the temp table
insert into #temp (td) values(myId)
--
-- prepare to do the string handling. Step through
-- #previousTD looking for commas
SET #startPos = 0
SET #commaPos = LOCATE(',', #previousTD, #startPos)
--
-- #commaPos will be NULL if the string is NULL
if #commaPos IS NULL then return
--
-- #commaPos will be 0 if there are no commas in the string
if #commaPos = 0 then
SET #previousTD = TRIM(#previousTD)
--
-- if #previousTD is empty then return
if LENGTH(#previousTD) = 0 then return
--
-- #previousTD has something in it that is not a comma.
-- try to insert it and return
insert into #temp (td) values(#previousTD)
select * from #temp order bu id
return
endif
--
-- should have a #previousTD with at least 1 comma
while #commaPos > 0
begin
SET #item = substring(#previousTD, #startPos, #commaPos)
insert into #temp (td) values(TRIM(#item))
SET #startPos = #commaPos + 1
SET #commaPos = LOCATE(',', #previousTD, #startPos)
end
select * from #temp order bu id
end
In order to make your database, you need to create a new table which has Id of Td and tdNos and have relationships with this. for example:
Table TdNos
ID | TDNO | PREVIOUS_TD |
1 | 14 | 13,12,11 |
2 | 23 | 45,12 |
3 | 32 | 89 |
4 | 55 | NEW |
Table TdNoHistory
TdID|Priority| PREVIOUS_TD |
1 | 1 | 13 |
1 | 2 | 12 |
1 | 3 | 11 |
2 | 1 | 45 |
2 | 2 | 12 |
3 | 1 | 89 |
Which for the second table the combination of TdId and Priority are the primary key and it has a relation with table TdNos through TdId column

NOT IN not working in mysql procedure

I have this table that represents friendships
+--------+--------+
| user_1 | user_2 |
+--------+--------+
| 1 | 5 |
| 2 | 67 |
| 3 | 23 |
| ... | ... |
+--------+--------+
My goal is to create a procedure that returns friends of friends of a user (that does not include friends).
I started by creating a procedure to return friends for a given user
CREATE DEFINER=`user`#`localhost` PROCEDURE `getFriends`(IN `myuser` BIGINT(20))
NO SQL
BEGIN
DROP TABLE IF EXISTS friends;
CREATE TEMPORARY TABLE friends
SELECT user_2
FROM fb_friends
WHERE user_1=myuser;
END
This procedure, for user 534477793, creates the following temporary table
+------------+
| user_2 |
+------------+
| 527419864 |
| 580101923 |
| 620972114 |
| 651861323 |
| 662123645 |
| 676185145 |
| 682866129 |
| 718761310 |
| 729611272 |
| 1036862839 |
+------------+
Then I created another procedure that calls the first one and return friends of friends
CREATE DEFINER=`user`#`localhost` PROCEDURE `getFriendsOfFriends`(IN `myuser` BIGINT(20))
BEGIN
-- Creates the table friends
CALL getFriends(myuser);
SELECT DISTINCT(fb.user_2)
FROM fb_friends fb, friends f
-- This works
WHERE fb.user_1 IN (f.user_2)
-- This doesn't
AND fb.user_2 NOT IN (f.user_2);
END
And the query returns the following:
+------------+
| user_2 |
+------------+
| 729611272 |
| 527419864 |
| 651861323 |
| 676185145 |
| 1036862839 |
| 502741322 |
| 546744626 |
| 636845886 |
| 652813833 |
| 663713246 |
| 682866129 |
| 781419583 |
| 845134109 |
| 1355751897 |
| 1359286892 |
| 1275961636 |
| 620972114 |
| 509609160 |
| 662123645 |
| 1460283586 |
+------------+
So it's clear that the NOT IN didn't work since all values from getFriends are in the second results set.
I managed to get the results I wanted by doing ugly stuff but still, I'd like to understand what's wrong here. And there's no NULL value anywhere by the way.
Thanks!
That's because f.user_2 in your NOT IN is just a single user, not the entire set.
The correct way to do it is
SELECT DISTINCT(fb.user_2)
FROM fb_friends fb, friends f
WHERE fb.user_1 IN (f.user_2)
AND fb.user_2 NOT IN (SELECT * FROM friends);
Note the the first IN can be replaced with an = operator, because again there's only one element in the IN list.
Or you can completely remove the join to make the query consistent (but it will probably regress the performance)
SELECT DISTINCT(fb.user_2)
FROM fb_friends fb
WHERE fb.user_1 IN (SELECT * FROM friends)
AND fb.user_2 NOT IN (SELECT * FROM friends);
I thought the problem might be with temporary table and I was right. By executing the query in MySQLWorkbench the following error was ouput:
Error Code: 1137. Can't reopen table: 'friends'
So I went online to check if there was a limit on the number of times a temporary table can be used in the same query and in fact there is.
So I might just the temporary table into a permanent one (and drop it every time beforehand) and all will work as expected. sigh

MySQL AFTER Update trigger with New.col<> OLD.col

The goal here is to update and concatenate a text field from one table to another based on conditions. When a text col gets updated with some text from a user form, run After update trigger.
The trigger runs but updates all the columns not just the one that has changed.Trying to understand how to just update the correct row only.
Here's what i have so far-
SQLFiddle
| ID | SUBID | SWNOTES |
|----|-------|---------| Table 1
| 1 | 40 | test |
| 2 | 60 | |
| ID | SUBID | CONTENT | SWFLAG |
|----|-------|---------|--------|
| 1 | 40 | hello | 0 | Table 2
| 2 | 60 | nothing | 0 |
CREATE TRIGGER `updatecontentnotes` AFTER UPDATE ON `tab1`
FOR EACH ROW
BEGIN
IF New.swnotes <> OLD.swnotes
THEN
Update `tab2`
Inner Join `tab1` ON `tab2`.`subid` = `tab1`.`subid`
Set `tab2`.`swflag` = '1',`tab2`.`content` = CONCAT(`tab2`.`content`, `tab1`.`swnotes`)
Where `tab2`.`subid` = `tab1`.`subid` AND `tab2`.`swflag` = '0';
END IF;
END//
And if I update the table with say:
Update `tab1`
set `swnotes` = "new"
where `subid` = '60'
I get :
| ID | SUBID | CONTENT | SWFLAG |
|----|-------|------------|--------|
| 1 | 40 | hellotest | 1 |
| 2 | 60 | nothingnew | 1 |
Now I know it is doing what I am telling it to do. I want to update just the row that is updated. Thanks for any help on this.
Since the contents of your trigger will run for every row on tab1, you need to be more restrictive in your WHERE clause so that it will only affect the rows that have been updated. Right now it updates every row on tab2
Try adding this to your trigger SQL:
From:
Where `tab2`.`subid` = `tab1`.`subid` AND `tab2`.`swflag` = '0';
To:
Where `tab2`.`subid` = `tab1`.`subid` AND `tab2`.`swflag` = '0' AND `tab2`.`id` = OLD.id;
This produces the results that I think you are looking for:
| ID | SUBID | CONTENT | SWFLAG |
|----|-------|------------|--------|
| 1 | 40 | hello | 0 |
| 2 | 60 | nothingnew | 1 |
The updated SQLFiddle is here:
http://sqlfiddle.com/#!2/9fa2d2/1/0

MySQL create trigger on insert in tbl1 also insert to tbl2

I'm currently working with a client that wants their database to duplicate info into a second table in a different format when it is initially inserted.
Basically like the following:
Table 1
| ID | NAME | EMAIL | password |
--------------------------------------
| 1 | david | x#x.co | asx234 |
| 2 | anthony | y#x.co | 24gss3 |
| 3 | jillian | z#x.co | hdfg5d |
Every time a row gets inserted into table 1 they also want to take that information from table 1 and add it to Table 2
Table 2
| ID | NAME | EMAIL | password | signature | level | lastenter | register_date |
-----------------------------------------------------------------------------------------------
| 1 | david | x#x.co | asx234 | text | 3 | 0000-00-00 | Date of insert |
| 2 | anthony | y#x.co | 24gss3 | text | 3 | 0000-00-00 | Date of insert |
| 3 | jillian | z#x.co | hdfg5d | text | 3 | 0000-00-00 | Date of insert |
How do I set up a trigger to insert the data into Table 2 whenever a row is inserted into table 1?
Thanks!
Some thoughts below, but the trigger would look something like this:
DELIMITER $$
DROP TRIGGER IF EXISTS trgMyNewTrigger $$
CREATE TRIGGER trgMyNewTrigger AFTER INSERT ON Table1
FOR EACH ROW
BEGIN
INSERT into Table2 (ID,NAME,EMAIL,`password`,signature,`level`,lastenter,register_date) VALUES (
new.ID, new.NAME, new.EMAIL, new.password, 'text', 3, '0000-00-00', CURDATE() );
END $$
DELIMITER ;
This is not a great solution. Triggers in general can cause some nasty issues and limit your capabilities down the road. It would be better to reconsider the design of the table to be inclusive of the data you need, have the application perform the extra step or use some sort of ETL process to get the data at set intervals.
I will all assume the clear text passwords are for the example.