How to split a column in Mysql? - 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;

Related

how to make multiple LIKE AND statements inside stored procedured?

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%';

Convert postgresql trigger to mysql trigger

I'm trying to transpose a postgres trigger to a mysql trigger. It automatically adds fields to the row according to the date added
CREATE FUNCTION convert_date ()
RETURNS trigger
AS $$
declare
date_min DATE;
date_max DATE;
temp_year INTEGER;
begin
SELECT SUBSTRING(NEW."dc_date_label",0,5)::integer
INTO temp_year;
SELECT date(temp_year || '-01-10')
INTO date_min;
SELECT date(temp_year +1 || '-09-30')
INTO date_max;
NEW."dc_date_start" = date_min;
NEW."dc_date_end" = date_max;
RETURN new;
end;
CREATE TRIGGER trig_b_i_compute_date()
BEFORE INSERT
ON campaigns
FOR EACH ROW
EXECUTE PROCEDURE convert_date();
This is what i've done on mysql :
DELIMITER //
CREATE TRIGGER trig_b_i_compute_date
BEFORE INSERT ON campaigns
FOR EACH ROW
BEGIN
DECLARE date_min DATE;
DECLARE date_max DATE;
DECLARE temp_year INTEGER;
SET temp_year = SELECT CONVERT( SUBSTRING(NEW.dc_date_label,1,5), UNSIGNED INTEGER) ;
SET date_min = SELECT CONVERT( CONCAT(temp_year,'-01-10'), DATE);
SET date_max = SELECT CONVERT( CONCAT(temp_year + 1, '09-30'), DATE);
SET NEW.dc_date_start = date_min;
SET NEW.dc_date_end = date_max;
END;
//
DELIMITER ;
However I get an error :
MySQL server version for the right syntax to use near 'SELECT CONVERT( SUBSTRING(NEW.dc_date_label,1,5), UNSIGNED INTEGER) ;
What is wrong with the procedure ?
If you use SELECT in a SET statement, you need to put it in parentheses:
SET temp_year = (SELECT ...);
But in your case you don't need a SELECT and you can just skip it:
SET temp_year = CONVERT(...);
You can also use the SELECT INTO syntax in MySQL:
SELECT CONVERT(...) INTO temp_year;
And there is no need to declare date_min and date_max. Also no need to cast everything explicitly. Your trigger body could be:
DECLARE temp_year INTEGER;
SET temp_year = CONVERT( SUBSTRING(NEW.dc_date_label,1,5), UNSIGNED);
SET NEW.dc_date_start = CONCAT(temp_year, '-01-10');
SET NEW.dc_date_end = CONCAT(temp_year + 1, '-09-30');
I don't know how dc_date_label looks like, and why the year should be 5 characters long. So I kept the year extraction as it is. But if it's a DATE, DATETIME or TIMESTAMP, you can just use the YEAR function:
SET temp_year = YEAR(NEW.dc_date_label);
And since it's much shorter, you could also use it inline and skip the temp_year variable:
SET NEW.dc_date_start = CONCAT(YEAR(NEW.dc_date_label), '-01-10');
SET NEW.dc_date_end = CONCAT(YEAR(NEW.dc_date_label) + 1, '-09-30');
And last one: Remove the semicolon after END. It might work, but it doesn't belong there.

MySQL Select only part of column

How can i select only a part of a column ? I know I can use substring, but i need to select a string part of a column. As an example: Column can contain the following:
DIRL100
I need to select only the DIRL part in one column, and the 100 part as another.
I could do it with this specific column like so:
SELECT SUBSTRING(column, 5) AS part1,
SUBSTRING(column, 1, 4) AS part2 ....
But i cannot be sure that its always 4 letters (DIRL) before it gets numeric .. Can i somehow use REGEXP or something to extract only the numeric part and the letter part in each column ?
In other words.. Can i split a column by where the letters end. It could as an example contain DIRL100 or AB100200 which should be split into two columns each containing the letters from the column (DIRL or AB) and the digits from the column (100 or 100200) ?
Try this request:
SELECT LEFT(column, patindex('%[0-9]%', column)-1) AS Part1
, RIGHT(column, LEN(column) - patindex('%[0-9]%', column)+1) AS Part2
Unfortunately, the regexp functions in MySQL is limited. You have to write a custom function to help you.
DROP FUNCTION IF EXISTS get_index;
DELIMITER $$
CREATE FUNCTION get_index(targetString VARCHAR(255)) RETURNS INTEGER
BEGIN
DECLARE i INTEGER;
DECLARE min_index INTEGER;
DECLARE current_index INTEGER;
SET i = 0;
SET min_index = NULL;
loopNumbers: WHILE (i <= 9) DO
SET current_index = LOCATE(i, targetString);
IF current_index > 0 THEN
IF min_index IS NULL OR current_index < min_index THEN
SET min_index = current_index;
END IF;
END IF;
SET i = i + 1;
END WHILE;
RETURN(min_index);
END
$$
DELIMITER ;
What this function does is to obtain the first position of the numbers. Then using this function, you can modify your query to:
SELECT SUBSTRING(column_name FROM 1 FOR get_index(column_name)-1) AS first, SUBSTRING(column_name FROM get_index(column_name)) AS second
FROM table_name
WHERE column_name REGEXP '^[a-Z]+[0-9]+$'
The additional WHERE condition is optional to ensure that only data in the correct format is selected.

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 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.
//: