How to check for a character in a string and replace that character before insert - mysql

Ok, this question involves one part of a complicated stored procedure which inserts new entities into several tables.
The part that I'm currently having difficulty with needs to work like so:
insert entity with original name
check if name of new entity contains any special characters listed in table A 'Characters'
if yes, than replace that character with a 'replacement character' from table A
EDIT: I've gotten this to partially work but still not finished. I'm still having a problem showing each combination of character replacements. Also in the case of a replacement character occurring more than once, such as the '.', the substitutions needs to happen independently of one another.
ex: #www.test&aol.com -> #wwwtest&aol.com, #www.test&aolcom
Here's a rough start, I know parts of this aren't going to work, but I thought it was a decent starting point:
declare #test varchar(50)
set #test = '#www.test&aol.com'
declare #len int, #ctr int
set #len = LEN(#test)
set #ctr = 1
declare #newName varchar(50)
declare #matchedChar table(match varchar(10),replaceChar varchar(10),processed int default(0))
declare #alternateEntities table(name varchar(50))
declare #repChar varchar(10)
declare #selectedChar varchar(1)
while #ctr<=#len
begin
--Insert matching characters and replacement characters into table variable,
--this is necessary for the # character, which has multiple replacement characters
insert into #matchedChar (match,replaceChar) select Character,ReplacementCharacter from tblTransliterations where Character = SUBSTRING(#test,#ctr,1)
--loop
while (select COUNT(*) from #matchedChar where processed = 0)>0
begin
--get the top character from table variable
set #selectedChar = (select top 1 match from #matchedChar where processed = 0)
--get replacement character
set #repChar = (select top 1 replaceChar from #matchedChar where processed = 0)
--replace character in name string
--set #newName = (select Replace(#test,#selectedChar,#repChar))
set #newName = (select STUFF(#test,CHARINDEX(#selectedChar,#test),1,#repChar))
--update table variable to move onto next character
update #matchedChar set processed = 1 where #repChar = replaceChar
--add name with replaced character to alternate entities table
insert into #alternateEntities (name) values (#newName)
end
set #ctr = #ctr+1
set #len = LEN(#test)
end
select * from #alternateEntities

Instead of looping, use set-based approach.
Create a temp table and populate column 'Words' of type NVARCHAR(100), call the temp table Invalid_Words
Create a column on Invalid_Words for each token and make the col type = bit
Update the temp table bit columns if a word contains the token through a series of update statements
You now have defined which tokens were matched for each word.
The next part is to replace.

Related

want to Write a Stored Procedure in MYSQL [duplicate]

I have made a stored procedure. I want it to filter the data by different parameters. If I pass one parameter, it should be filtered by one; if I pass two, it should be filtered by two, and so on, but it is not working.
Can anyone help me please?
DROP PROCEDURE IF EXISTS medatabase.SP_rptProvince2;
CREATE PROCEDURE medatabase.`SP_rptProvince2`(
IN e_Region VARCHAR(45)
)
BEGIN
DECLARE strQuery VARCHAR(1024);
DECLARE stmtp VARCHAR(1024);
SET #strQuery = CONCAT('SELECT * FROM alldata where 1=1');
IF e_region IS NOT NULL THEN
SET #strQuery = CONCAT(#strQuery, ' AND (regionName)'=e_Region);
END IF;
PREPARE stmtp FROM #strQuery;
EXECUTE stmtp;
END;
AFAIK, you can't have a variable argument list like that. You can do one of a couple of things:
Take a fixed maximum number of parameters, and check them for null-ness before concatenating:
CREATE PROCEDURE SP_rptProvince2(a1 VARCHAR(45), a2 VARCHAR(45), ...)
...
IF a1 IS NOT NULL THEN
SET #strQuery = CONCAT(#strQuery, ' AND ', a2);
END IF;
If you need predetermined fields to which the criteria in the argument apply (like the e_Region parameter in your existing code), then you modify the CONCAT operation appropriately.
Possible invocation:
CALL SP_rptProvince2('''North''', 'column3 = ''South''')
Take a single parameter that is much bigger than just 45 characters, and simply append it to the query (assuming it is not null).
Clearly, this places the onus on the user to provide the correct SQL code.
Possible invocation:
CALL SP_rptProvince2('RegionName = ''North'' AND column3 = ''South''')
There's not a lot to choose between the two. Either can be made to work; neither is entirely satisfactory.
You might note that there was a need to protect the strings in the arguments with extra quotes; that is the sort of thing that makes this problematic.
I found a JSON-based approach which works with the latest MySQL/MariaDB systems. Check the link below (Original Author is Federico Razzoli): https://federico-razzoli.com/variable-number-of-parameters-and-optional-parameters-in-mysql-mariadb-procedures
Basically, you take a BLOB parameter which is actually a JSON object and then do JSON_UNQUOTE(JSON_EXTRACT(json object, key)) as appropriate.
Lifted an extract here:
CREATE FUNCTION table_exists(params BLOB)
RETURNS BOOL
NOT DETERMINISTIC
READS SQL DATA
COMMENT '
Return whether a table exists.
Parameters must be passed in a JSON document:
* schema (optional). : Schema that could contain the table.
By default, the schema containing this procedure.
* table : Name of the table to check.
'
BEGIN
DECLARE v_table VARCHAR(64)
DEFAULT JSON_UNQUOTE(JSON_EXTRACT(params, '$.table'));
DECLARE v_schema VARCHAR(64)
DEFAULT JSON_UNQUOTE(JSON_EXTRACT(params, '$.schema'));
IF v_schema IS NULL THEN
RETURN EXISTS (
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA = SCHEMA()
AND TABLE_NAME = v_table
);
ELSE
RETURN EXISTS (
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA = v_schema
AND TABLE_NAME = v_table
);
END IF;
END;

How to use split function in mysql or store split value in different column?

String x "3_SJ454FH";
I want to store '3'(Before _ ) in one column and 'SJ454FH'(After _ ) in another column in mysql. I have tried substring_index function in stored procedure but it did not work. So Is there any way to store value like this in stored procedure.?
CREATE DEFINER=`root`#`localhost` PROCEDURE `insert_csv`(_list MEDIUMTEXT)
BEGIN
DECLARE _next TEXT DEFAULT NULL;
DECLARE _nextlen INT DEFAULT NULL;
DECLARE _value TEXT DEFAULT NULL;
iterator:
LOOP
-- exit the loop if the list seems empty or was null;
-- this extra caution is necessary to avoid an endless loop in the proc.
IF LENGTH(TRIM(_list)) = 0 OR _list IS NULL THEN
LEAVE iterator;
END IF;
-- capture the next value from the list
SET _next = SUBSTRING_INDEX(_list,'_',1);
-- save the length of the captured value; we will need to remove this
-- many characters + 1 from the beginning of the string
-- before the next iteration
SET _nextlen = LENGTH(_next);
-- trim the value of leading and trailing spaces, in case of sloppy CSV strings
SET _value = TRIM(_next);
-- insert the extracted value into the target table
INSERT INTO t1 (c1) VALUES (_value);
-- rewrite the original string using the `INSERT()` string function,
-- args are original string, start position, how many characters to remove,
-- and what to "insert" in their place (in this case, we "insert"
-- an empty string, which removes _nextlen + 1 characters)
SET _list = INSERT(_list,1,_nextlen + 1,'');
END LOOP;
END
Output:
id|c1
-----------
1 |3
-----------
2 |SJ454FH
-----------
I have tried this code but it store in next row.
You can use the logic of the following query in combination with INTO to fill two variables with the desired values and then INSERT them in a separate call.
SELECT LEFT("3_SJ454FH", INSTR("3_SJ454FH", "_")-1) AS Prefix,
MID("3_SJ454FH", INSTR("3_SJ454FH", "_")+1, LENGTH("3_SJ454FH")) AS Tail

BULK INSERT from comma delimited string

I have a table with the following data in one column:
abc,2,2,34,5,3,2,34,32,2,3,2,2
def,2,2,34,5,3,2,34,32,2,3,2,2
I want to take this data and insert it into another table, using the commas as delimiters, just like how you can specify the FIELDTERMINATOR in BULK INSERT statements.
Is there a way to do this using T-SQL?
I'm not sure if there is any direct way to do in the T-SQL , but if you want to use Bulk Insert you can use sqlcmd to export to CSV file and then Import the file back into server using Bulk Insert.
Create a dbo.Split Functionm, you can refer here split string into multiple record
There are tons of good examples.
if you want to execute as batch process, You can execute sqlcmd and 'Bulk Insert'
sqlcmd -S MyServer -d myDB -E -Q "select dbo.Split(col1) from SomeTable"
-o "MyData.csv" -h-1 -s"," -w 700
-s"," sets the column seperator to
bulk insert destTable
from "MyData.csv"
with
(
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)
Otherwise, You can manipulate directly in the T-SQL, but given you have the same identify of columns definition.
INSERT INTO DestinationTable
SELECT dbo.Split(col1) FROM SomeTable
You need to use a Split function to split your string into a table variable, and then insert those values into your table.
There are tons of those split functions out there, with various pros and cons and various number of parameters and so forth.
Here is one that I quite like - very nicely done, clearly explained.
With that function, you should have no trouble converting your column into individual entries for your other table.
EDIT: allow multiple char separators
This is how I solved it, with two functions to do the splitting into columns (if you want a more complete solution with line splitting as well, see my other post here). It involves:
A scalar function (fSubstrNth) for extracting the n-th field of a line, given an separator
A scalar function (fPatIndexMulti) for finding the n-th index of the separator
(Optional) alternative Right function to accept negative values
Finally, some specific code to use in your solution, since SQL doesn't allow dynamic table-function definitions (in other words, you can't SELECT from a function with dynamic columns)
Now, for the code snippets:
fSubstrNth
-- =============================================
-- Author: Bernardo A. Dal Corno
-- Create date: 18/07/2017
-- Description: substring com 2 PatIndex limitando inicio e fim
-- =============================================
CREATE FUNCTION fSubstrNth
(
#Text varchar(max),
#Sep varchar(3),
#N int --Nth campo
)
RETURNS varchar(max)
AS
BEGIN
DECLARE #Result varchar(max)
IF #N<1 RETURN ''
IF #N=1
SET #Result = substring(#Text, 1, dbo.fPatIndexMulti(#Sep,#Text,1)-1)
ELSE
SET #Result = substring(#Text, dbo.fPatIndexMulti(#Sep,#Text,#N-1)+LEN(#Sep), CASE WHEN dbo.fPatIndexMulti(#Sep,#Text,#N)>0 THEN dbo.fPatIndexMulti(#Sep,#Text,#N)-dbo.fPatIndexMulti(#Sep,#Text,#N-1)-LEN(#Sep) ELSE LEN(#Text)+1 END)
RETURN #Result
END
fPatIndexMulti
-- =============================================
-- Author: Bernardo A. Dal Corno
-- Create date: 17/07/2017
-- Description: recursive patIndex
-- =============================================
CREATE FUNCTION [dbo].[fPatIndexMulti]
(
#Find varchar(max),
#In varchar(max),
#N tinyint
)
RETURNS int
AS
BEGIN
DECLARE #lenFind int, #Result int, #Texto varchar(max), #index int
DECLARE #i tinyint=1
SET #lenFind = LEN(#Find)-1
SET #Result = 0
SET #Texto = #In
WHILE (#i <= #N) BEGIN
SET #index = patindex('%'+#Find+'%',#Texto)
IF #index = 0 RETURN 0
SET #Result = #Result + #index
SET #Texto = dbo.xRight(#Texto, (#index + #lenFind)*-1)
SET #i = #i + 1
END
SET #Result = #Result + #lenFind*(#i-2)
RETURN #Result
END
xRight
-- =============================================
-- Author: Bernardo A. Dal Corno
-- Create date: 06/01/2015
-- Description: Right inverso (para nros < 0)
-- =============================================
CREATE FUNCTION [dbo].[xRight]
(
#Texto varchar(8000),
#Qntd int
)
RETURNS varchar(8000)
AS
BEGIN
DECLARE #Result varchar(8000)
IF (Len(#Texto) = 0) OR (#Qntd = 0)
SET #Result = ''
ELSE IF (#Qntd > 0)
SET #Result = Right(#Texto, #Qntd)
ELSE IF (#Qntd < 0)
SET #Result = Right(#Texto, Len(#Texto) + #Qntd)
RETURN #Result
END
Specific code
SELECT
acolumn = 'any value',
field1 = dbo.fSubstrNth(table.datacolumn,',',1),
field2 = dbo.fSubstrNth(table.datacolumn,',',2),
anothercolumn = 'set your query as you would normally do',
field3 = (CASE dbo.fSubstrNth(table.datacolumn,',',3) WHEN 'C' THEN 1 ELSE 0 END)
FROM table
Note that:
fSubstrNth receives the n-th field to extract from the 'datacolumn'
The query can be as any other. This means it can be stored in a procedure, tabled-function, view, etc. You can extract some or all fields, in any order you wish, and process however you want
If used in a stored procedure, you could create a generic way of creating a query and temp table that loads the string with dynamic columns, but you have to make a call to another procedure to use the data OR create a specific query like above in the same procedure (which would make it non-generic, just more reusable)

ms sql query on count occurence of words in text column

I have a table called webqueries with a column named qQuestion of data type text(sql server 2008). I want to create a count on words used in qQuestion (excluding 'and', 'is' etc). My goal is to see how many times a person has asked a question about a specific product.
You could create a table-valued function to parse words and join it to your query against qQuestion. In your schema, I recommend using varchar(8000) or varchar(max) instead of text. Meanwhile, the following should get you started:
create function [dbo].[fnParseWords](#str varchar(max), #delimiter varchar(30)='%[^a-zA-Z0-9\_]%')
returns #result table(word varchar(max))
begin
if left(#delimiter,1)<>'%' set #delimiter='%'+#delimiter;
if right(#delimiter,1)<>'%' set #delimiter+='%';
set #str=rtrim(#str);
declare #pi int=PATINDEX(#delimiter,#str);
while #pi>0 begin
insert into #result select LEFT(#str,#pi-1) where #pi>1;
set #str=RIGHT(#str,len(#str)-#pi);
set #pi=PATINDEX(#delimiter,#str);
end
insert into #result select #str where LEN(#str)>0;
return;
end
go
select COUNT(*)
from webqueries q
cross apply dbo.fnParseWords(cast(q.qQuestion as varchar(max)),default) pw
where pw.word not in ('and','is','a','the'/* plus whatever else you need to exclude */)

using mysql string functions

I have a table with following Data in a table
abcd
abcd , pgw
ABcd , Pgw
I want output as
Abcd
Abcd , Pgw
Abcd , Pgw
the First letter in capitals and letter after ',' in capital.
to make first letter upper-case use this
select CONCAT(UPPER(LEFT(`field`, 1)), SUBSTRING(`field`, 2)) from mytable
to do it to also to word after comma declare a function like :
DELIMITER ||
CREATE FUNCTION `ucwords`( str VARCHAR(128) ) RETURNS VARCHAR(128) CHARSET latin1
BEGIN
DECLARE c CHAR(1);
DECLARE s VARCHAR(128);
DECLARE i INT DEFAULT 1;
DECLARE BOOL INT DEFAULT 1;
SET s = LCASE( str );
WHILE i < LENGTH( str ) DO
BEGIN
SET c = SUBSTRING( s, i, 1 );
IF c = ',' THEN
SET BOOL = 1;
ELSEIF BOOL=1 THEN
BEGIN
IF c >= 'a' AND c <= 'z' THEN
BEGIN
SET s = CONCAT(LEFT(s,i-1),UCASE(c),SUBSTRING(s,i+1));
SET BOOL = 0;
END;
END IF;
END;
END IF;
SET i = i+1;
END;
END WHILE;
RETURN s;
END ||
DELIMITER ;
and use
select ucwords(`field`) from mytable
Please don't do this if you ever expect your database to scale well. If you're only ever going to be using this for your personal phone book application or equally tiny data sets, you can get away with doing this at select time, as you suggest.
But, if you want a robust scalable database, you should sacrifice space (disk storage) for time (speed). Per-row functions on select statement rarely scale well.
One time-honored and tested way of doing this is to add another column of identical specifications to the one you already have and use an insert/update trigger to populate it with the data in the format you desire.
Then the cost of the transformation is incurred only when you have to (when the data is updated), not every single time you read the data. This amortises the cost across the reads which, in the vast majority of databases, outweigh writes considerably.
This answer shows an SQL formula which seems close to what you want but I do strongly suggest that you use it in a trigger rather than inefficiently running it every select.
take a look at this solution mysql-update-script-problem-all-txt-is-lower-case-just-first-letter-is-upper-cas , it should give you enough information to adapt to what you're trying to do. Change update to SELECT, etc. etc.