how to make multiple LIKE AND statements inside stored procedured? - mysql

For example, I got an array :
carBrand: 'ford','volve','honda'
I would like to search a field which contained all elements of it, just like :
SELECT * FROM car_company WHERE car_company.brandStory LIKE '%ford%' AND car_company.brandStory LIKE '%volve%'
AND car_company.brandStory LIKE '%honda%';
And the array is an input parameter outside stored procedure
So far, I got two approachs nearly but still not good to me:
use REGEXP
for example:
SET carBrand = 'ford|volve|honda'; //input parameter
SELECT * FROM car_company WHERE car_company.brandStory REGEXP carBrand;
But the code make the OR statement actually, all I want is AND statement.
use full-text index
for example:
SET carBrand = '+ford +volve +honda'; //input parameter
SELECT * FROM car_company WHERE MATCH (brandStory) AGAINST (carBrand IN BOOLEAN MODE);
This is the closest solution I have found,but I really don't want to set the full-text index.
Is there any other solution ? Thanks everyone !

This stored procedure
USE `testdb`;
DROP procedure IF EXISTS `BuildSelect`;
DELIMITER $$
USE `testdb`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `BuildSelect`(IN carbrand TEXT)
BEGIN
DECLARE brand TEXT DEFAULT NULL;
DECLARE brandlength INT DEFAULT 0;
DECLARE Tempbrand TEXT DEFAULT NULL;
SET #Selectstr = "SELECT * FROM car_company WHERE";
iterator:
LOOP
IF LENGTH(TRIM(carbrand)) = 0 OR carbrand IS NULL THEN
LEAVE iterator;
END IF;
SET brand = SUBSTRING_INDEX(carbrand,',',1);
SET Tempbrand = CONCAT(" car_company.brandStory LIKE '%",TRIM(brand),"%'");
IF brandlength > 0 THEN
SET Tempbrand = CONCAT(" AND",Tempbrand);
END IF;
SET #Selectstr = CONCAT(#Selectstr,COALESCE(Tempbrand,''));
SET carbrand = SUBSTRING_INDEX(carbrand,',',-(LENGTH(carbrand) - LENGTH(REPLACE(carbrand, ',', '')) ));
SET brandlength = brandlength +1;
END LOOP iterator;
SET #Selectstr = CONCAT(#Selectstr ,";");
PREPARE id FROM #Selectstr;
EXECUTE id;
DEALLOCATE PREPARE id;
#SELECT #Selectstr;
END$$
DELIMITER ;
Would run with this call
call BuildSelect('Ford,Audi,Mercedes');
Run this query
SELECT * FROM car_company
WHERE car_company.brandStory LIKE '%Ford%'
AND car_company.brandStory LIKE '%Audi%'
AND car_company.brandStory LIKE '%Mercedes%';

Related

MySql Stored procedure WHERE "variabilised" according to a parameter

I'm totally new with stored procedure and I'm trying to understand its basic concepts. This is my first one and of course there is something wrong.
Basically the query is going to be the same (the original is more complex and there are other operations) but the WHERE clause changes according to the selType param. So what I'm trying to do is a sort of "variabilisation" of the WHERE clause according to the param value.
I don't know whether this is the correct approach and, if yes, what's wrong with it.
DELIMITER //
CREATE PROCEDURE `testProcedure` (IN addressId INT, IN selType BOOLEAN)
BEGIN
DECLARE whereUserCriteria VARCHAR(127);
IF selType = 1 THEN
SET whereUserCriteria = CONCAT('address_id = ', addressId);
ELSE
SET whereUserCriteria = 'address_id = 1';
END IF;
SELECT whatever
FROM wherever AS ad
WHERE whereUserCriteria ;
END //
It's nice to see that when it's not variabilised, it works perfectly but, as soon as i use a variable to make it dynamic, it stops working.
Of course this is a mere example aimed to understand what's the best approach in cases like this.
You can prepare query concatenating the queries and condition together and execute that using Prepared Execute statement as follows(as mentioned in the comment above):
DELIMITER //
CREATE PROCEDURE `testProcedure` (IN addressId INT, IN selType BOOLEAN)
BEGIN
DECLARE whereUserCriteria VARCHAR(127);
IF selType = 1 THEN
SET whereUserCriteria = CONCAT('address_id = ', addressId);
ELSE
SET whereUserCriteria = 'address_id = 1';
END IF;
SET #myQuery = '';
SET #myQuery = CONCAT("SELECT whatever FROM wherever AS ad
WHERE ",whereUserCriteria,") ;
PREPARE stmQuery FROM #myQuery;
EXECUTE stmQuery;
DEALLOCATE PREPARE stmQuery;
END //
DELIMITER ;
You probably want do dynamic query.
But you can rewrite your sample using CASE like this (but not sure if that is what you want):
SELECT whatever
FROM wherever AS ad
WHERE address_id = CASE WHEN selType = 1
THEN addressId
ELSE 1
END;

how to handle \ in query mysql

I have a procedure in which I am making query as string then prepare query and execute.
Here is the procedure
CREATE DEFINER=`root`#`%` PROCEDURE `dim_add_customer`(
IN _customer_id BIGINT(20) ,
IN _first_name VARCHAR(50) ,
)
BEGIN
SET #_query := CONCAT('first_name = "',_first_name,'"');
SET #_query := CONCAT('UPDATE customer_detail SET ',#_query,' WHERE customer_id = ',_customer_id);
PREPARE stmt FROM #_query;
END$$
DELIMITER ;
Now when I call
call dim_add_customer(1,'abc\\')
Then there is issue in creating string query.The query it made
UPDATE customer_detail SET first_name = "abc\" WHERE customer_id = 1
is there any best solution to solve this ?
You shouldn't build the queries by concat.
You should use the parameters in the query like
SET #_query="UPDATE customer_detail
SET first_name=#_first_name
WHERE customer_id = #_customer_id"
I'm not sure if you can declare your variables directly from the input parameters like
CREATE DEFINER=`root`#`%` PROCEDURE `dim_add_customer`(
IN #_customer_id BIGINT(20) ,
IN #_first_name VARCHAR(50) ,
)
or you have to
SET #_customer_id = _customer_id
SET #_first_name = _first_name
CAVEAT: I'm used to the MsSql-way of creating procedures with variables; I might have misunderstood something, but at least creating sql by concat should be your last resort.
Creating queries by concat is the equivalent of
x=1
q=concat("y=",x,"+2")
eval (q)
instead of
x=1
y=x+2

Query to find all words in a field that contains the letters

I am trying to run a query that can find all the records from a field contains the letters.
For example suppose a state field contains a record value "New York" and another record conatains NY. Now i am searching for NY or New york will return these 2 records. What will be the query.
Currently i am using
like %New York%" or "%NY%"
Any suggestion
No your query is not correct as it searches for anything containing New York or NY.
So if there is PENNY that will be matched although it shouldn't be....
Your query must be something like this.
SELECT * from TABLE where field in ('NEW YORK','NY')
Now to fetch acronym,you can use
delimiter $$
drop function if exists `initials`$$
CREATE FUNCTION `initials`(str text, expr text) RETURNS text CHARSET utf8
begin
declare result text default '';
declare buffer text default '';
declare i int default 1;
if(str is null) then
return null;
end if;
set buffer = trim(str);
while i <= length(buffer) do
if substr(buffer, i, 1) regexp expr then
set result = concat( result, substr( buffer, i, 1 ));
set i = i + 1;
while i <= length( buffer ) and substr(buffer, i, 1) regexp expr do
set i = i + 1;
end while;
while i <= length( buffer ) and substr(buffer, i, 1) not regexp expr do
set i = i + 1;
end while;
else
set i = i + 1;
end if;
end while;
return result;
end$$
drop function if exists `acronym`$$
CREATE FUNCTION `acronym`(str text) RETURNS text CHARSET utf8
begin
declare result text default '';
set result = initials( str, '[[:alnum:]]' );
return result;
end$$
delimiter ;
So,your final query will be something like this.
SELECT * from TABLE where field in ('NEW YORK',select acronym('Come Again? That Cant Help!'))
Source:- Mysql extract first letter of each word in a specific column
Presumably, the logic that you want is:
col like '%New York%' or col like '%NY%'
or, if you want to use regular expressions:
col regexp 'New York|NY'
Select * from table where col like '%N' or col like '%n'

How to split a column in Mysql?

I have a column which value is combine two elements, Just like "Class_Name"。For example , "Websites_Google"。But for some historical reasons, there have some special value which have no "Class_" prefix, like "yahoo".
Now I need to extract the class from columns. If there is no "class_" prefix , i expect return "" .
I just try SUBSTRING_INDEX functions like this:
select SUBSTRING_INDEX(column1, '_', 1) .......
It works very well when the value have "class_", but in the no "class_" situation, it return the whole value, "yahoo" return "yahoo", not the empty string which i want.
How to solve it?
Kindly check with the following
select case when CHARINDEX ('_', column1)>0 then SUBSTRING_INDEX(column1, '_', 1) else '' end .....
You can use IF to check if the underscore is there:
select if(locate('_',column1)=0,'',SUBSTRING_INDEX(column1, '_', 1))
from mytable
You will have to create a mysql function.
Here's a working example:
DELIMITER $$
DROP FUNCTION IF EXISTS `test`.`split_class` $$
CREATE FUNCTION `test`.`split_class` (`class` varchar(25)) RETURNS varchar(25)
BEGIN
DECLARE `tmp1` varchar(25);
DECLARE `tmp2` varchar(25);
DECLARE `ret` varchar(25);
set `tmp1` = SUBSTRING_INDEX(`class`, '_', 1);
set `tmp2` = SUBSTRING_INDEX(`class`, '_', -1);
set `ret` = '';
IF `tmp1` != `tmp2`
then set `ret` = `tmp1`;
END IF;
RETURN `ret`;
END $$
DELIMITER ;
Then, try the select
SELECT split_class(last) FROM test.`table` t;

How can I pass an "array" of values to my stored procedure?

I want to be able to pass an "array" of values to my stored procedure, instead of calling "Add value" procedure serially.
Can anyone suggest a way to do it? am I missing something here?
Edit: I will be using PostgreSQL / MySQL, I haven't decided yet.
As Chris pointed, in PostgreSQL it's no problem - any base type (like int, text) has it's own array subtype, and you can also create custom types including composite ones. For example:
CREATE TYPE test as (
n int4,
m int4
);
Now you can easily create array of test:
select ARRAY[
row(1,2)::test,
row(3,4)::test,
row(5,6)::test
];
You can write a function that will multiply n*m for each item in array, and return sum of products:
CREATE OR REPLACE FUNCTION test_test(IN work_array test[]) RETURNS INT4 as $$
DECLARE
i INT4;
result INT4 := 0;
BEGIN
FOR i IN SELECT generate_subscripts( work_array, 1 ) LOOP
result := result + work_array[i].n * work_array[i].m;
END LOOP;
RETURN result;
END;
$$ language plpgsql;
and run it:
# SELECT test_test(
ARRAY[
row(1, 2)::test,
row(3,4)::test,
row(5,6)::test
]
);
test_test
-----------
44
(1 row)
If you plan to use MySQL 5.1, it is not possible to pass in an array.
See the MySQL 5.1 faq
If you plan to use PostgreSQL, it is possible look here
I don't know about passing an actual array into those engines (I work with sqlserver) but here's an idea for passing a delimited string and parsing it in your sproc with this function.
CREATE FUNCTION [dbo].[Split]
(
#ItemList NVARCHAR(4000),
#delimiter CHAR(1)
)
RETURNS #IDTable TABLE (Item VARCHAR(50))
AS
BEGIN
DECLARE #tempItemList NVARCHAR(4000)
SET #tempItemList = #ItemList
DECLARE #i INT
DECLARE #Item NVARCHAR(4000)
SET #tempItemList = REPLACE (#tempItemList, ' ', '')
SET #i = CHARINDEX(#delimiter, #tempItemList)
WHILE (LEN(#tempItemList) > 0)
BEGIN
IF #i = 0
SET #Item = #tempItemList
ELSE
SET #Item = LEFT(#tempItemList, #i - 1)
INSERT INTO #IDTable(Item) VALUES(#Item)
IF #i = 0
SET #tempItemList = ''
ELSE
SET #tempItemList = RIGHT(#tempItemList, LEN(#tempItemList) - #i)
SET #i = CHARINDEX(#delimiter, #tempItemList)
END
RETURN
END
You didn't indicate, but if you are referring to SQL server, here's one way.
And the MS support ref.
For PostgreSQL, you could do something like this:
CREATE OR REPLACE FUNCTION fnExplode(in_array anyarray) RETURNS SETOF ANYELEMENT AS
$$
SELECT ($1)[s] FROM generate_series(1,array_upper($1, 1)) AS s;
$$
LANGUAGE SQL IMMUTABLE;
Then, you could pass a delimited string to your stored procedure.
Say, param1 was an input param containing '1|2|3|4|5'
The statement:
SELECT CAST(fnExplode(string_to_array(param1, '|')) AS INTEGER);
results in a result set that can be joined or inserted.
Likewise, for MySQL, you could do something like this:
DELIMITER $$
CREATE PROCEDURE `spTest_Array`
(
v_id_arr TEXT
)
BEGIN
DECLARE v_cur_position INT;
DECLARE v_remainder TEXT;
DECLARE v_cur_string VARCHAR(255);
CREATE TEMPORARY TABLE tmp_test
(
id INT
) ENGINE=MEMORY;
SET v_remainder = v_id_arr;
SET v_cur_position = 1;
WHILE CHAR_LENGTH(v_remainder) > 0 AND v_cur_position > 0 DO
SET v_cur_position = INSTR(v_remainder, '|');
IF v_cur_position = 0 THEN
SET v_cur_string = v_remainder;
ELSE
SET v_cur_string = LEFT(v_remainder, v_cur_position - 1);
END IF;
IF TRIM(v_cur_string) != '' THEN
INSERT INTO tmp_test
(id)
VALUES
(v_cur_string);
END IF;
SET v_remainder = SUBSTRING(v_remainder, v_cur_position + 1);
END WHILE;
SELECT
id
FROM
tmp_test;
DROP TEMPORARY TABLE tmp_test;
END
$$
Then simply CALL spTest_Array('1|2|3|4|5') should produce the same result set as the above PostgreSQL query.
Thanks to JSON support in MySQL you now actually have the ability to pass an array to your MySQL stored procedure. Create a JSON_ARRAY and simply pass it as a JSON argument to your stored procedure.
Then in procedure, using MySQL's WHILE loop and MySQL's JSON "pathing" , access each of the elements in the JSON_ARRAY and do as you wish.
An example here https://gist.githubusercontent.com/jonathanvx/513066eea8cb5919b648b2453db47890/raw/22f33fdf64a2f292688edbc67392ba2ccf8da47c/json.sql
Incidently, here is how you would add the array to a function (stored-proc) call:
CallableStatement proc = null;
List<Integer> faultcd_array = Arrays.asList(1003, 1234, 5678);
//conn - your connection manager
conn = DriverManager.getConnection(connection string here);
proc = conn.prepareCall("{ ? = call procedureName(?) }");
proc.registerOutParameter(1, Types.OTHER);
//This sets-up the array
Integer[] dataFaults = faultcd_array.toArray(new Integer[faultcd_array.size()]);
java.sql.Array sqlFaultsArray = conn.createArrayOf("int4", dataFaults);
proc.setArray(2, sqlFaultsArray);
//:
//add code to retrieve cursor, use the data.
//: