Making wildcards work in a variable MySQL Stored Procedure - mysql

I wrote a Stored Procedure, that uses a variable with the sql query to make it dynamic depending on a set of parameters, my problem is when I try the LIKE clause with % wildcards, the query is not filtering, I tested with a regular query and it works.
CREATE DEFINER=`root`#`localhost` PROCEDURE `seleccionar_exp_repGral`(in p_fecha date, in p_fecha2 date, in p_compania int, in p_institucion int, in p_sector int,
in p_clasificacion int, in p_analista int, in p_decision int, in p_fuenteFinanciamiento int, in p_tipo int, in p_objeto varchar(3500))
BEGIN
set #qrystr = "select Expe.exp, Expe.mes, Expe.anio, Expe.fecha_recibido, cia.nombre_com, inst.nombre_inst, Expe.monto_egreso,
Expe.anio_egreso, Expe.monto_ingreso, Expe.anio_ingreso, an.nombre_ana, deci.nombre_dec, Expe.fecha_sesion, sec.nombre_sec,
clas.nombre_cla, finan.nombre_fin, tipo.nombre_tipo from expediente Expe
left outer join compania cia on (Expe.compania = cia.idcompania)
left outer join institucion inst on (Expe.institucion = inst.idinstitucion)
left outer join analista an on (Expe.analista = an.idanalista)
left outer join decision deci on (Expe.decision = deci.iddecision)
left outer join sector sec on (Expe.sector = idsector)
left outer join clasificacion clas on (Expe.clasificacion = idclasificacion)
left outer join financiamiento finan on (Expe.financiamiento = finan.idfinanciamiento)
left outer join tipo_expediente tipo on (Expe.tipo = tipo.idtipo_expediente)";
-- variables for filter
set #pcia = p_compania;
set #pinst = p_institucion;
set #psector = p_sector;
set #pclasificacion = p_clasificacion;
set #panalista = p_analista;
set #pdecision = p_decision;
set #pfuenteFinanciamiento = p_fuenteFinanciamiento;
set #ptipo = p_tipo;
set #pObjeto = p_objeto;
-- concatenate conditions for filter
set #qrystr = CONCAT(#qrystr,'where Expe.fecha_recibido >=\'',p_fecha,'\' and Expe.fecha_recibido <=\'',p_fecha2,
'\'');
-- Filters
IF #pcia > 0 THEN
set #qrystr = CONCAT(#qrystr,' and Expe.compania = ',#pcia);
end if;
IF #pinst > 0 THEN
set #qrystr = CONCAT(#qrystr,' and Expe.institucion = ',#pinst);
end if;
IF #psector > 0 THEN
set #qrystr = CONCAT(#qrystr,' and Expe.sector = ',#psector);
end if;
IF #pclasificacion > 0 THEN
set #qrystr = CONCAT(#qrystr,' and Expe.clasificacion = ',#pclasificacion);
end if;
IF #panalista > 0 THEN
set #qrystr = CONCAT(#qrystr,' and Expe.analista = ',#panalista);
end if;
IF #pdecision > 0 THEN
set #qrystr = CONCAT(#qrystr,' and Expe.decision = ',#pdecision);
end if;
IF #pfuenteFinanciamiento > 0 THEN
set #qrystr = CONCAT(#qrystr,' and Expe.financiamiento = ',#pfuenteFinanciamiento);
end if;
IF #ptipo > 0 THEN
set #qrystr = CONCAT(#qrystr,' and Expe.tipo = ',#ptipo);
end if;
IF #pObjeto <> "" THEN
set #qrtystr = CONCAT(#qrystr," and Expe.objeto LIKE '",#pObjeto,"'");
end if;
-- preparamos el ultimo statement del query
set #qrystr = CONCAT(#qrystr,' order by Expe.idexpediente asc');
-- preparamos datos finales
PREPARE stmt from #qrystr;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
From that long SP I have this part:
IF #pObjeto <> "" THEN
set #qrtystr = CONCAT(#qrystr," and Expe.objeto LIKE '",#pObjeto,"'");
end if;
This line I added the wildcard % before the #pObjeto and after that and it's not working, I even added wildcard to the variable, and my query still shows all registers without the text filtering.
I wonder if it has to do with string formatting or something.
I appreciate any help.

Related

What does "The following query failed" ";"" mean in MySQL?

While trying to update a trigger, MySQL tells me the query ";" failed. How is ";" even a query in MySQL's view is beyond me.
The exact message is:
The following query has failed: ";" MySQL said: #1065 - Query was empty
Here's the new trigger (AFTER INSERT):
BEGIN
DECLARE vIdPlacet VARCHAR(40);
DECLARE vTypeTravaux VARCHAR(32);
DECLARE vEssence VARCHAR(3) DEFAULT '-';
DECLARE vClasseHau VARCHAR(5) DEFAULT '-';
DECLARE vNoMesurag int;
DECLARE new_id_parcelle INT UNSIGNED DEFAULT 0;
DECLARE new_no_microplacette INT UNSIGNED DEFAULT 0;
IF NEW.deleted = 0 THEN
SELECT id_parcelle, no_microplacette
INTO new_id_parcelle, new_no_microplacette
FROM microplacette
WHERE id_microplacette = NEW.id_microplacette;
SELECT travaux, no_mesurag, id__placet
INTO vTypeTravaux, vNoMesurag, vIdPlacet
FROM secteur
LEFT JOIN parcelle ON secteur.id_secteur = parcelle.id_secteur
WHERE id_parcelle = new_id_parcelle;
IF vTypeTravaux = 'inventaire' THEN
SELECT abbreviation INTO vEssence FROM essences WHERE _id = NEW.id_essence;
IF NEW.hauteur_15 = 1 THEN
SET vClasseHau = '15CM+';
END IF;
IF (SELECT COUNT(*) FROM imported_pres_ess WHERE id__placet = vIdPlacet AND
caracteris = '-' AND
classe_hau = vClasseHau AND
essence = vEssence AND
no_mesurag = vNoMesurag AND
no_micro_p = new_no_microplacette) = 0 THEN
INSERT INTO imported_pres_ess (id__placet, caracteris, classe_hau, essence, no_mesurag, no_micro_p)
VALUES (vIdPlacet, '-', vClasseHau, vEssence, vNoMesurag, new_no_microplacette);
END IF;
IF (SELECT COUNT(*) FROM imported_semi_gau WHERE id__placet = vIdPlacet AND
classe_hau = vClasseHau AND
essence = vEssence AND
no_mesurag = vNoMesurag AND
no_micro_p = new_no_microplacette) = 0 THEN
INSERT INTO imported_semi_gau (id__placet, classe_hau, essence, no_mesurag, no_micro_p)
VALUES (vIdPlacet, vClasseHau, vEssence, vNoMesurag, new_no_microplacette);
END IF;
IF NEW.diametre > 0 THEN
SET vClasseHau = 'D2_D8';
ELSE
SET vClasseHau = '-';
END IF;
IF (SELECT COUNT(*) FROM imported_pres_ess WHERE id__placet = vIdPlacet AND
caracteris = '-' AND
classe_hau = vClasseHau AND
essence = vEssence AND
no_mesurag = vNoMesurag AND
no_micro_p = new_no_microplacette) = 0 THEN
INSERT INTO imported_pres_ess (id__placet, caracteris, classe_hau, essence, no_mesurag, no_micro_p)
VALUES (vIdPlacet, '-', vClasseHau, vEssence, vNoMesurag, new_no_microplacette);
END IF;
IF (SELECT COUNT(*) FROM imported_semi_gau WHERE id__placet = vIdPlacet AND
classe_hau = vClasseHau AND
essence = vEssence AND
no_mesurag = vNoMesurag AND
no_micro_p = new_no_microplacette) = 0 THEN
INSERT INTO imported_semi_gau (id__placet, classe_hau, essence, no_mesurag, no_micro_p)
VALUES (vIdPlacet, vClasseHau, vEssence, vNoMesurag, new_no_microplacette);
END IF;
END IF;
END IF;
END
I tried creating the procedure you show, but I don't get any error.
The error about "empty statement" happens when you try to execute a query through the API but the query string is empty.
I can duplicate the error in the mysql client this way:
mysql> set #s = '';
mysql> prepare stmt from #s;
ERROR 1065 (42000): Query was empty
So I suggest you look not at the stored procedure, but whatever code you're executing this from, and check that every time you try to execute a query, that you submit a non-empty string.
It turns out, the trigger I was updating got deleted in the meantime, so I was updating a trigger that didn't exist anymore.
I found out after refreshing the page (the trigger was gone from the trigger list).
I simply recreated the trigger anew and it worked.

How use UPDATE in CONCAT() of PROCEDURE MySQL

I try update exist tables via PROCEDURE. But I get an error: MySQL: #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 '= iOS, phone_manufacture = Apple, phone_model = iPhone, os_vers = iOS X.X' at line 1
My Procedure:
BEGIN
DECLARE a, b INT;
DECLARE a1, a2, a3, a4 TEXT;
DECLARE cur1 CURSOR FOR SELECT id FROM z7_group;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1;
OPEN cur1;
SET b = 0;
SET a1 = "iOS";
SET a2 = "Apple";
SET a3 = "iPhone";
SET a4 = "iOS X.X";
WHILE b = 0 DO
FETCH cur1 INTO a;
IF b = 0 THEN
SET #data_ all_group = (SELECT `myTables` FROM `all_group` WHERE id= a);
SET #s = CONCAT('UPDATE ', #data_ all_group, 'SET `os` = ',a1,', `phone_manufacture` = ',a2,', `phone_model` = ',a3', `os_vers` = ',a4,');
PREPARE stmt3 FROM #s;
EXECUTE stmt3;
END IF;
END WHILE;
CLOSE cur1;
END;
You have two options:
(there is more but I think it is what you want)
Enclose values in single quotes at inicialization:
SET a1 = "'iOS'";
SET a2 = "'Apple'";
SET a3 = "'iPhone'";
SET a4 = "'iOS X.X'";
and fix the sentence:
CONCAT('UPDATE ', #data_all_group, ' SET `os` = ',a1,', `phone_manufacture` = ',a2,', `phone_model` = ',a3,', `os_vers` = ',a4)
The result will be:
UPDATE your_table SET `os` = 'iOS', `phone_manufacture` = 'Apple', `phone_model` = 'iPhone', `os_vers` = 'iOS X.X'
OR: fix the query build:
SET #s = CONCAT('UPDATE ', #data_all_group
, "SET `os` = '" , a1 ,"',"
, "`phone_manufacture` = '",a2,"',"
, "`phone_model` = '",a3,"',"
, "`os_vers` = '",a4,"'");

Mysql error #1064 when trying to create trigger

I am trying to create a MySQL trigger but don't know why error #1064 occurs. Here is the full error message:
#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 'DECLARE popularity_cur CURSOR FOR select (sum(tbl_reviews.vote_value)/count(tbl_' at line 22
Line #22 is close cur;
and here is what I've done so far:
delimiter //
CREATE TRIGGER business_popularity_trigger_after_review_insert
AFTER INSERT ON tbl_reviews FOR EACH ROW
BEGIN
DECLARE data_finished INTEGER DEFAULT 0;
DECLARE businessId, reviewId, dealId, businessServiceId INT DEFAULT 0;
DECLARE businessPopularity INTEGER DEFAULT 0;
DECLARE popularity_finished INTEGER DEFAULT 0;
DECLARE cur CURSOR FOR select tbl_reviews.review_id, tbl_reviews.deal_id, deals.deal_id, deals.business_service_id, business_services.business_service_id, business_services.business_id from tbl_reviews INNER JOIN deals ON deals.deal_id = new.deal_id INNER JOIN business_services ON business_services.business_service_id = deals.business_service_id
where tbl_reviews.review_id = new.review_id
group by business_services.business_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET data_finished = 1;
OPEN cur;
get_data: LOOP
fetch cur into reviewId, dealId, businessServiceId, businessId;
IF data_finished = 1 THEN
LEAVE get_data;
END IF;
END LOOP get_data;
close cur;
DECLARE popularity_cur CURSOR FOR select (sum(tbl_reviews.vote_value)/count(tbl_reviews.vote_value)) from tbl_reviews INNER JOIN business_services ON business_services.business_id = businessId INNER JOIN deals ON deals.business_service_id = business_services.business_service_id where deals.deal_is_deleted = 'N' and tbl_reviews.review_is_deleted = 'N' and tbl_reviews.review_status = 'Y';
set data_finished = 0;
IF businessId > 0 THEN
OPEN popularity_cur;
get_popularity: LOOP
fetch popularity_cur into businessPopularity;
IF data_finished = 1 THEN
LEAVE get_popularity;
END IF;
END LOOP get_popularity;
close popularity_cur;
Update business
set business.business_popularity = businessPopularity
where business.business_id = businessId;
END IF;
delimiter;
Please help me.
Thanks
You should use correct delimiter // at the end and close the BEGIN ... END construction. So change last lines to:
END IF;
END//
DELIMITER;
UPDATE Since you use aggregating functions SUM, COUNT you need GROUP BY statement. So try this way:
DECLARE popularity_cur CURSOR FOR
SELECT (sum(tbl_reviews.vote_value)/count(tbl_reviews.vote_value))
FROM tbl_reviews
INNER JOIN business_services
ON business_services.business_id = tbl_reviews.businessId
INNER JOIN deals
ON deals.business_service_id = business_services.business_service_id
AND deals.deal_is_deleted = 'N'
WHERE tbl_reviews.review_is_deleted = 'N'
AND tbl_reviews.review_status = 'Y'
GROUP BY tbl_reviews.businessId;
I did some optimization and you had wrong condition business_services.business_id = businessId so I did change to business_services.business_id = tbl_reviews.businessId.
NOTE Probably that wasn't a mistake and you need that business_services.business_id = businessId condition. But even if you need that you should use one condition for JOIN and another one to filter. so it could be like:
INNER JOIN business_services
ON business_services.business_id = tbl_reviews.business_id
AND business_services.business_id = businessId
UPDATE 2 According to the screenshot with your database schema your query could be like:
DECLARE popularity_cur CURSOR FOR
SELECT (sum(tbl_reviews.vote_value)/count(tbl_reviews.vote_value))
FROM tbl_reviews
INNER JOIN deals
ON tbl_reviews.deal_id = deals.deal_id
AND deals.deal_is_deleted = 'N'
INNER JOIN business_services
ON business_services.business_service_id = deals.business_service_id
WHERE tbl_reviews.review_is_deleted = 'N'
AND tbl_reviews.review_status = 'Y'
GROUP BY deals.business_service_id;

Asking about stored function in mysql

drop function if exists rty_check_member_info_status;
DELIMITER $$
--
-- Functions
CREATE DEFINER=root#localhost FUNCTION rty_check_member_info_status(memb_id int,field_name_1 varchar(100),field_name_2 varchar(100),login_member_amount int(11),login_status char(1)) RETURNS char(1) CHARSET latin1
begin
declare fn_field_name_1 varchar(100) ;
declare fn_field_name_2 varchar(100) ;
declare fn_amount_for_profile_visible int(11);
declare fn_return char(1) default 'N';
declare test_field varchar(100);
select field_name_1,field_name_2,amount_for_profile_visible into
fn_field_name_1,fn_field_name_2,fn_amount_for_profile_visible
from member_account_settings inner join tbl_members on member_account_settings.member_auto_id = tbl_members.member_id
where tbl_members.member_id = memb_id ;
if fn_field_name_1 = 'H' Then
set fn_return = 'N' ;
else
if fn_field_name_2 = 'Y' Then
if fn_amount_for_profile_visible = '0' Then
set fn_return = 'Y' ;
else
if login_status = 1 Then
if fn_amount_for_profile_visible > login_member_amount Then
set fn_return = 'N' ;
else
set fn_return = 'Y' ;
end if;
else
set fn_return = 'N';
end if ;
end if;
else
set fn_return = 'Y';
end if ;
end if ;
return fn_return ;
end$$
DELIMITER ;
You're two choices are pretty much generated SQL(normally a bad idea because it's harder to write, debug and document) and using a case statement to select the column based on the name matching a string(which is normally a pretty good solution).
Here's an example of the second, since it's the solution I'd definitely recommend.
SET #test_field1 = "last_name_display_status" ;
SET #test_field2 = "last_name_display_for_other_partcpnt" ;
SELECT
CASE #test_field1
-- List columns here that you might want to return:
WHEN 'last_name_display_status' THEN last_name_display_status
WHEN 'last_name_display_for_other_partcpnt' THEN last_name_display_for_other_partcpnt
WHEN 'create_date' THEN create_date
-- Return a value for an invalid name here:
ELSE NULL
END AS test_field1,
CASE #test_field2
-- List columns here that you might want to return:
WHEN 'last_name_display_status' THEN last_name_display_status
WHEN 'last_name_display_for_other_partcpnt' THEN last_name_display_for_other_partcpnt
WHEN 'create_date' THEN create_date
-- Return a value for an invalid name here:
ELSE NULL
END AS test_field2,
-- Rest of select unaffected by this change
amount_for_profile_visible
INTO
fn_field_name_1,
fn_field_name_2,
fn_amount_for_profile_visible
FROM member_account_settings
INNER JOIN tbl_members
ON member_account_settings.member_auto_id = tbl_members.member_id
WHERE
tbl_members.member_id = memb_id
;
And for the sake of completeness a copy of the first solution I proposed(the generated SQL):
-- Need to use #vars, since named vars aren't in scope for the generated SQL:
SET #output1 = '';
SET #output2 = '';
SET #output3 = '';
SET #input1 = memb_id;
-- We also need to store our generated SQL to a variable
SET #query = 'SELECT ' + #test_field1 + ',' + #test_field2 + ', amount_for_profile_visible INTO #output1, #output2, #output3 FROM member_account_settings INNER JOIN tbl_members ON member_account_settings.member_auto_id = tbl_members.member_id WHERE tbl_members.member_id = ?';
-- To execute the code we have to convert it to a prepared statement
-- named stmt here, because it's what most people use in this instance
PREPARE stmt FROM #query;
-- Execute the statement using our input variable
EXECUTE stmt USING #input1;
-- Delete the prepared statement now we've run it.
DEALLOCATE PREPARE stmt;
-- Store our #vars back into the named vars.
SET fn_field_name_1 = #output1;
SET fn_field_name_2 = #output2;
SET fn_amount_for_profile_visible = #output3;

mysql stored procedure error (1172, 'Result consisted of more than one row')

When trying to run the following stored procedure from django, I get an OperationError (1172, 'Result consisted of more than one row') Any idea what I might be doing wrong?
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `UpdatePrices`(IN storeId int, IN bottleSize VARCHAR(50))
BEGIN
DECLARE amount DECIMAL(10,2); DECLARE isCustom INT DEFAULT 0;
DECLARE changeType VARCHAR(50) DEFAULT 'State'; DECLARE updateType INT DEFAULT 0;
IF bottleSize = '1000 Ml' THEN
SELECT S1000IncreaseChoices INTO changeType FROM store_store WHERE StoreID = storeId;
IF changeType = 'State' THEN
SELECT updateType = 0;
END IF;
IF changeType = 'Flat' THEN
SELECT S1000IncreaseAmount INTO amount FROM store_store WHERE StoreID = storeId;
SELECT updateType = 1;
END IF;
IF changeType = 'Percent' THEN
SELECT 1 - S1000IncreaseAmount/100 INTO amount FROM store_store WHERE StoreID = storeId;
SELECT updateType = 2;
END IF;
END IF;
IF updateType = 0 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = ShelfPrice
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
IF updateType = 1 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = OffPremisePrice + amount
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
IF updateType = 1 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = OffPremisePrice / amount
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
END
I'm not sure if it matters, but I initiate the stored procedure like so:
def priceupdate(request, store_id):
cursor = connection.cursor()
cursor.callproc("UpdatePrices", (store_id, '1000 ML'))
cursor.close()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
Your SELECT...INTO queries give result sets with more then one record. The WHERE filters are incorrect - they compare two the same values StoreID = storeId. Rename IN storeId int parementer to another name. For example - IN storeId_param int
The query will be like this -
SELECT S1000IncreaseChoices INTO changeType FROM store_store WHERE StoreID = storeId_param;
This is a Bug and you need to apply something like that:
SELECT id,data INTO x,y FROM test.t1 LIMIT 1;