Having issues on a validation for email on a table - mysql

What i'm trying to do is to validate the email of a certain register on a trigger. For example if a customer is registered for the first time it should create an email with both first and last name (example: user.last#email.com) however if there's already a register with the same name it should create the email the same way with the first and last name following the customer_id (example: user.last2#email.com).
The code that i have so far is the following:
delimiter $
create trigger before_customer_insert
before insert on customer
for each row
begin
declare emailVal varchar(45);
declare checkData int;
declare checkData2 int;
set checkData = (select count(first_name) from customer where first_name = new.first_name);
set checkData2 = (select count(last_name) from customer where last_name = new.last_name);
if((checkData = 1) and (checkData2 = 1)) then
set new.email = (concat(new.first_name,'.', new.last_name, '#sakilacustomer.org'));
else
set new.email = (concat(new.first_name,'.', new.last_name, new.customer_id, '#sakilacustomer.oeg'));
end if;
if(new.kind_customer is null) then
set new.kind_customer = '1';
end if;
set new.active = 1;
end $
The problem that i'm having is that when it's the first register it inserts the email but with a 0 for example "name.last0#email.com" and if i try and insert with the same information shows the same email. I tried to change the logic in the if statements but still showed the same issues.

Your logic is backward. You have:
if((checkData = 1) and (checkData2 = 1)) then
But you want:
if((checkData = 0) and (checkData2 = 0)) then
You want to use the name without a customer ID when there AREN'T any existing records in the database with those names.
Note I'm not sure your check of looking for first names and last names separately is quite what you want. Wouldn't you want to check if the whole address is found as-is?
The way it's written, if someone named Joe Bloggs tries to join, he'll be joe.bloggs1 if there's anybody else named Joe in the system, even if there is no other Joe Bloggs.

Related

MySQL foreach loop

I have to iterate each row in my table User in MySQL.
I need to create a new row Address for each iteration in User with some conditions described below.
I have 3 tables:
User: id, stuff, id_person, email
Person: id, stuff, id_address
Address: id, email
I need to create a new row in Address if the User.id_person is NOT NULL and that person.id_address IS NULL.
I have to create the row with the same email that User.email.
I have to do that for each row in User.
I tried to use MySQL cursor's but I do not know how to use them very well.
How can I do that? Is there any other way instead of using cursor's for that?
Thanks in advance.
EDIT: I have just realized that I also have to update person.id_address with the id of the address' row I have just created.
From what I can gather, the following should suffice, so long as the fields are what you have provided.
INSERT INTO Address (email)
SELECT User.email
FROM User JOIN person ON User.id_person = person.id
WHERE person.id_address IS NULL
;
EDIT (with Cursor)
This should be pretty simple with a cursor, however I highly advise you familiarize yourself with these and the implications.
DROP PROCEDURE IF EXISTS _tmp_update_address;
DELIMITER $$
CREATE PROCEDURE _tmp_update_address()
BEGIN
DECLARE cursor_List_isdone BOOLEAN DEFAULT FALSE;
DECLARE cur_userId, cur_personId INT;
DECLARE cur_email VARCHAR(250) DEFAULT '';
DECLARE cursor_List CURSOR FOR
SELECT User.id, person.id_address, User.email
FROM User JOIN person ON User.id_person = person.id
WHERE person.id_address IS NULL
;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_List_isdone = TRUE;
OPEN cursor_List;
loop_List: LOOP
FETCH cursor_List INTO cur_userId, cur_personId, cur_email;
IF cursor_List_isdone THEN
LEAVE loop_List;
END IF;
INSERT INTO Address (email) VALUES (cur_email);
UPDATE person SET person.id_address = LAST_INSERT_ID()
WHERE person.id = cur_personId;
END LOOP loop_List;
CLOSE cursor_List;
END
$$
DELIMITER ;
CALL _tmp_update_address();
You don't want to use a cursor for this. Based on what you describe:
insert into address (address)
select u.email
from user u join
person p
on u.id_person = p.id;
When I search "mysql foreach loop" this is what I found, and since I found a better alternative than the cursor for doing a foreach loop, here we are :
delimiter $$
create or replace procedure _tmp_procedure() begin
for var_user in (
select User.id, person.id_address, User.email
from User join person on User.id_person = person.id
where person.id_address is null
) do
INSERT INTO Address (email) VALUES (var_user.email);
UPDATE person SET person.id_address = LAST_INSERT_ID()
WHERE person.id = var_user.id_person;
end for;
end;
$$
call _tmp_procedure();

MySQL Stored procedure SELECT var wrong value after INSERT

I have a problem on MySQL stored procedure, already did another logic with IF THEN ELSE, but I still have problems which I cant understand...
The procedure consists on two user inputs: user_id and meet_id. The procedure define a variable called 'ue' which stores result of a bunch of validation (if user exists, if event exists, if event date is still valid, etc.).
After that, it does INSERT and UPDATE some data on multiple tables in IF THEN ELSE selector, and SELECT 1 (or 0) AS result depending of validation.
But my problem is: it always return me 0 as 'result', as if my validation variable was 0 when I do INSERT... And there is where things get weird, if I remove the INSERT [...]; line of the code, it returns me the value of validation correctly (1 or 0).
The code of the procedure is this one:
CREATE DEFINER=`nwouserf`#`localhost`
PROCEDURE `join_event`(IN `user_id` BIGINT(64), IN `event_id` INT)
NOT DETERMINISTIC MODIFIES SQL DATA SQL SECURITY DEFINER
begin
DECLARE ue INT;
SET ue = EXISTS(SELECT 1 FROM users WHERE fb_uid=user_id)
AND EXISTS(SELECT 1 FROM meetup WHERE meet_id=event_id)
AND EXISTS(SELECT 1 FROM meetup WHERE date > NOW() AND meet_id = event_id)
AND EXISTS(SELECT 1 FROM meetup WHERE meet_id = event_id AND participants <= max_participants)
AND NOT EXISTS(SELECT 1 FROM meetup_participation WHERE fb_uid = user_id AND meet_id = event_id);
IF ue = 1 THEN
INSERT INTO meetup_participation (fb_uid, meet_id) VALUES (user_id, event_id);
UPDATE users SET events_participated = events_participated + 1 WHERE fb_uid=user_id;
UPDATE meetup SET participants = participants + 1 WHERE meet_id=event_id;
SELECT 1 AS result;
ELSEIF ue = 0 THEN
SELECT 0 AS result;
ELSE
SELECT null AS result;
END IF;
end
Thanks in advance!
I have been stuck on this for a while now, and can not figure out why.
You should define OUT parameter. Add
", OUT result INT"
immediately after the last IN parameter.

Updating a column name of a same table which has a parent child relationship using mysql

I searched a lot of doing a task but found no appropriate solution.
Basically the scenario is. I have a user_comment table in which there are 5 column(id,parent_id,user_comments,is_deleted,modified_datetime). There is a parent child relationship like 1->2,1->3,2->4,2->5,5->7 etc. Now i am sending the id from the front end and i want to update the column is_deleted to 1 and modified_datetime on all the records on
this id as well as the all the children and children's of children.
I am trying to doing this by using a recursive procedure. Below is the code of my procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_comments`(
IN mode varchar(45),
IN comment_id int,
)
BEGIN
DECLARE p_id INT DEFAULT NULL ;
if(mode = 'delete')
then
update user_comment set is_deleted = 1, modified_datetime = now()
where id = comment_id ;
select id from user_comment where parent_id = comment_id into p_id ;
if p_id is not null
then
SET ##GLOBAL.max_sp_recursion_depth = 255;
SET ##session.max_sp_recursion_depth = 255;
call user_comments('delete', p_id);
end if;
end if;
END
By using this procedure it give me an error of more than one row.
If i return the select query without giving it to variable then shows me the the appropriate results on the select query but i have to call this procedure recursively based on getting the ids of the select query.
I need help i have already passed 2 days into this.
I used cursor also. Below is the code of cursor
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_comments`(
IN mode varchar(45),
IN comment_id int,
)
BEGIN
DECLARE p_emp int;
DECLARE noMoreRow INT;
DECLARE cur_emp CURSOR FOR select id from user_comment where parent_id = comment_id ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET noMoreRow = 0;
if(mode = 'delete')
then
OPEN cur_emp;
LOOPROWS: LOOP
IF noMoreRow = 0 THEN
update user_comment set is_deleted = 1, modified_datetime = now() where id = comment_id
CLOSE cur_emp;
LEAVE LOOPROWS;
END IF;
FETCH cur_emp INTO p_emp;
update user_comment set is_deleted = 1, modified_datetime = now() where id = p_emp ;
SET ##GLOBAL.max_sp_recursion_depth = 255;
SET ##session.max_sp_recursion_depth = 255;
call user_comments('delete', p_emp);
END LOOP;
end if;
END
After using cursor i am getting a thread error.i don't know how can overcome this problem!!!
Mysql's documentation on select ... into varlist clearly says:
The selected values are assigned to the variables. The number of
variables must match the number of columns. The query should return a
single row. If the query returns no rows, a warning with error code
1329 occurs (No data), and the variable values remain unchanged. If
the query returns multiple rows, error 1172 occurs (Result consisted
of more than one row). If it is possible that the statement may
retrieve multiple rows, you can use LIMIT 1 to limit the result set to
a single row.
Since you wrote in the OP that a comment can be parent of many comments, using simple variables cannot be a solution. You should use a CURSOR instead, that can store an entire resultset.
You loop through the records within the cursos as shown in the sample code in the above link and call user_comments() in a recursive way.
UPDATE
If your receive
Error Code: 1436. Thread stack overrun
error, then you can do 2 things:
Increase the thread_stack setting in the config file and restart mysql server.
You can try to simplify your code to use less recursions and therefore less stack space. For example, when you fetch all children into the cursor, then rather calling the user_comments() recursively for each, you can set all direct children's status within the code and call the function recirsively on grand-childrens only (if any). You can also change your data structure and use nested set model to approach hierarchical structures.
Nested set model is more complex to understand, it is less resource intensive to traverse, but more resource intensive to maintain.

Trigger Loop keeps putting currentStock on 0

So I'm trying to work out a trigger that automatically adds the ingredients from a order (dhh_purchaseorder) to the table dhh_ingredients. It should get the new ordered amount out of dhh_purchaseorderingredient and dump it into the orderAmount variable. Then it should combine the current available stock with the ordered amount in newStock. Then it should update the ingredient table and set the new amounts for the correct ingredients but for some reason it keeps putting the currentStock column in ingredients to 0.
Here is my trigger:
BEGIN
DECLARE orderStatus, orderIngredientName VARCHAR(50);
DECLARE finished INTEGER DEFAULT 0;
DECLARE currentStock, newStock DECIMAL(10,2);
DECLARE orderNo, orderAmount int(10);
DECLARE lookupCursor CURSOR FOR SELECT INGREDIENTingredientName from dhh_purchaseorderingredient WHERE dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
SET orderNo = NEW.purchaseOrderNo;
SET orderStatus = NEW.status;
IF(orderStatus = "DELIVERED") THEN
OPEN lookupCursor;
update_loop: LOOP
FETCH lookupCursor INTO orderIngredientName;
IF finished = 1 THEN
LEAVE update_loop;
END IF;
SET orderAmount = (SELECT amount from dhh_purchaseorderingredient WHERE dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = orderNo AND dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName);
SET currentStock = (SELECT currentStock FROM dhh_ingredient WHERE ingredientName = orderIngredientName);
SET newStock = currentStock + orderAmount;
INSERT INTO temp VALUES(currentStock);
UPDATE dhh_ingredient
SET currentStock = newStock
WHERE ingredientName = ingredientName;
END LOOP update_loop;
CLOSE lookupCursor;
END IF;
END
This is partly an answer and partly a suggestion for a better approach.
One of the many reasons I believe stored procedures are a very bad idea is that they are hard to debug. In this case, it's not easy to figure out what value is being assigned to the various stock amount variables.
A better approach is to use query that does update - without the need for SP variable. It's really easy to test it; just execute and inspect the results. By using such a query in your SP, you eliminate incorrect query logic from your code, allowing you to condense the SP to just the bits that matter - the update.
Fortunately, MySQL provides a way to update through a join, via its multi-table update syntax.
This is what the query could look like:
UPDATE dhh_ingredient, dhh_purchaseorderingredient
SET = currentStock = currentStock - amount
WHERE dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName
AND ingredientName = orderIngredientName
AND dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo
AND NEW.status = 'DELIVERED';
No need for local variables or any loops, because the update query does a join and the NEW values are used directly in the query.
So your entire stored procedure becomes simply:
BEGIN
UPDATE dhh_ingredient, dhh_purchaseorderingredient
SET = currentStock = currentStock - amount
WHERE dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName
AND ingredientName = orderIngredientName
AND dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo
AND NEW.status = 'DELIVERED';
END
which is easy to understand, maintain and debug - a better solution.

SQL Emailing Different Rows to Different Email addresses

I've written a stored procedure that runs every day and finds checks we are unable to process due to insufficient information on the check. The results of this query gives me a table with 4 columns, one of which is an email address, the three other columns being check info for the unprocessed check.
CHECK_AMT | CHECK_DATE | CUSTOMER_NAME | EMAIL_ADDRESS
So each row has information for a different check and or different customer. I want SQL to send an email to Mr/s. [EMAIL_ADDRESS] at each company [COMPANY_NAME] notifying them that they need to contact us regarding a check dated [CHECK_DATE] for $[CHECK_AMT]. Each customer can only receive an email the information for their own check, not the entire table that is being produced by the stored procedure.
I've looked at other SQL generated emails, but havent found one that sends emails this way. Any guidance on emailing information from row1 to the email address in row1, info from row2 to the email address in row2...etc.? I've only been working with sql for about 3 weeks now, so I'm still very new to everything. If anyone has any questions, I'll be happy to answer them. Thank you!
DECLARE
#ch_amt DECIMAL(20,2),
#ch_date DATE,
#c_name NVARCHAR(255),
#email VARCHAR(320),
#msg NVARCHAR(MAX);
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
SELECT CHECK_AMT, CHECK_DATE, CUSTOMER_NAME, EMAIL_ADDRESS
FROM dbo.SomeTable
WHERE [some clause that lets you know this check was nOt processed];
OPEN c;
FETCH c INTO #ch_amt, #ch_date, #c_name, #email;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #msg = N'Dear ' + #c_name + ', we could not process your check...';
EXEC msdb.dbo.sp_send_dbmail
#recipients = #email,
#subject = N'Check processing failure',
#body = #msg;
FETCH c INTO #ch_amt, #ch_date, #c_name, #email;
END
CLOSE c;
DEALLOCATE c;
This assumes Database Mail is configured and that you want to send the mail from the default profile. See the documentation for sp_send_dbmail for more info.
I'm thinking you just need a loop of some kind. Either a cursor, or a while loop, maybe something like:
declare #check_id int;
declare #email varchar(100);
declare #check_date date;
...etc...
while exists (select 1 from emails where ... and msg_sent = 0)
begin
select top 1 #check_id = check_id, #email = email, #check_date = check_date, ...
from emails
where ... and msg_sent = 0;
' Now you've got variables which you can use to send your email
update emails set msg_sent = 1
where check_id = #check_id;
end;
You can probably clean up the loop a bit to avoid doubling up the queries, but you get the idea...