I am trying to update a value in the database but also want to set a local variable VAR_IS_RATE_LIMITED. The reason for this is because I do not want to use a select statement and want to execute it within one. How can I set the VAR_IS_RATE_LIMITED? I looked at other questions but their CASE statements weren't embedded in an update statement.
DELIMITER //
CREATE FUNCTION F_RATE_LIMITED(P_IP varchar(45),
P_MAX_RATE int unsigned
)
RETURNS INT UNSIGNED
BEGIN
DECLARE VAR_IS_RATE_LIMITED INT UNSIGNED DEFAULT 0;
INSERT INTO rate_limit (ip, rate)
VALUES (P_IP, 1)
ON DUPLICATE KEY UPDATE
rate =
CASE
WHEN (rate + 1) > P_MAX_RATE THEN
SET VAR_IS_RATE_LIMITED = 1;
rate
ELSE
rate + 1
END;
RETURN VAR_IS_RATE_LIMITED;
END; //
DELIMITER ;
To set something during a data modification statement, it has to be part of an expression, not a statement. And as far as I know you can't set a stored function variable in an expression. But you can set a user variable:
DELIMITER //
CREATE FUNCTION F_RATE_LIMITED(P_IP varchar(45),
P_MAX_RATE int unsigned
)
RETURNS INT UNSIGNED
BEGIN
SET #VAR_IS_RATE_LIMITED = 0;
INSERT INTO rate_limit (ip, rate)
VALUES (P_IP, 1)
ON DUPLICATE KEY UPDATE
rate =
CASE
WHEN (rate + 1) > P_MAX_RATE THEN
CASE WHEN #VAR_IS_RATE_LIMITED := 1 THEN rate END
ELSE
rate + 1
END;
RETURN #VAR_IS_RATE_LIMITED;
END; //
DELIMITER ;
Here the variable is set in a CASE test (you can also use an IF expression) that is always true so rate is always returned from the CASE.
I have a JSON Values Like This.
{
"CHEQUE":[
{
"Cheque_No":"1",
"Cheque_Date":"2018-05-30",
"Cheque_Amount":"10000",
"Bank_Name":"ICICI"
},
{
"Cheque_No":"2",
"Cheque_Date":"2018-05-30",
"Cheque_Amount":"20000",
"Bank_Name":"ICICI"
},
{
"Cheque_No":"2",
"Cheque_Date":"2018-05-30",
"Cheque_Amount":"20000",
"Bank_Name":"SBI"
}
]
}'
How can i Remove Entire Index of 2 in a CHEQUE list using the search key SBI for Bank name?.
I need to Do this is in Mysql Stored Procedure without Store Values in DB. This is what I have tried:
CREATE DEFINER = root # PROCEDURE SP_TEST(
IN ls_json JSON,
OUT Message VARCHAR(1000)
)
BEGIN
SELECT
JSON_REMOVE(
ls_json,
CONCAT('$.CHEQUE[0].Bank_Name[0]')
)
INTO ls_json;
#SELECT JSON_REMOVE(ls_json,JSON_SEARCH(ls_json,'ALL',
CONCAT('$.CHEQUE[0].Cheque_No[*]')))
INTO ls_json; #select
JSON_SEARCH(ls_json, 'one', 10000);
SELECT
ls_json;
END
One option:
DELIMITER //
CREATE PROCEDURE `SP_TEST` (
IN `_ls_json` JSON,
IN `_bank_name` VARCHAR(64)
)
BEGIN
DECLARE `_base` CHAR(11) DEFAULT '$.CHEQUE[*]';
DECLARE `_base_attr` CHAR(10) DEFAULT '.Bank_Name';
DECLARE `_element` VARCHAR(1024);
`_loop`: LOOP
SET `_element` :=
JSON_SEARCH(
`_ls_json`,
'one',
`_bank_name`,
NULL,
CONCAT(`_base`, `_base_attr`)
);
IF (`_element` IS NOT NULL) THEN
SET `_ls_json` :=
JSON_REMOVE(
`_ls_json`,
JSON_UNQUOTE(
REPLACE(`_element`, `_base_attr`, '')
)
);
ELSE
LEAVE `_loop`;
END IF;
END LOOP `_loop`;
SELECT JSON_PRETTY(`_ls_json`);
END//
DELIMITER ;
See db-fiddle.
There is a MySQL function in our web system to generate Code. The structure of the code is
district_cd(length:2) + date(length:8) + sequence no(length:5,start at 1).<like : ab2016090800001>
The sequence no was saved in table and will be updated (+1) when generate a new code.
But sometimes it returned two same codes and makes us fall in trouble. Here are the captures to replicate this problem, I will attach the DDL after this.
Step 1.Client1->change to manual commit then generate a code, but do not commit.
SET autocommit = 0;
select * from applies;
select * from sequence where apply_date = "2016-09-08";
select nextval("ab");
insert into applies (apply_id,apply_no,created,district_cd) values (2,"ab2016090800002","ab",now());
select * from sequence where apply_date = "2016-09-08";
Step2.Client2->change to manual commit then generate a code, stuck as Client1 locked
SET autocommit = 0;
select * from applies;
select * from sequence where apply_date = "2016-09-08";
insert into applies (apply_id,apply_no,created,district_cd) values (3,"ab20160908123456780","ab",now());
Step3.Client1->commit;
commit;
select * from sequence where apply_date = "2016-09-08";
Step4.Client2->code was generated and two records appeared in sequence table
select * from sequence where apply_date = "2016-09-08";
capture of Step4
Step5.Client2->commit;one of the two records that appeared in sequence table was deleted.The codes generated are duplicated.
commit;
select * from sequence where apply_date = "2016-09-08";
select * from applies;
capture of Step5
※DDL
Table:applies (apply_no:save the code)
CREATE TABLE `applies` (
`apply_id` varchar(100) NOT NULL DEFAULT '',
`apply_no` varchar(100) NOT NULL DEFAULT '',
`district_cd` varchar(100) DEFAULT NULL,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`apply_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Table:sequence (current_value:save current sequnce value)
CREATE TABLE `sequence` (
`district_cd` varchar(3) NOT NULL DEFAULT '',
`current_value` int(11) NOT NULL DEFAULT '0',
`apply_date` date NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (`district_cd`,`current_value`,`apply_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Function:currval->get current sequence value by district_cd
DELIMITER ;;
CREATE DEFINER=`usr`#`%` FUNCTION `currval`(d VARCHAR(3)) RETURNS int(11)
DETERMINISTIC
BEGIN
DECLARE value INTEGER;
DECLARE needInitSequence INTEGER;
DECLARE today DATE;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET needInitSequence = 1;
SET value = 0;
SET today = current_date();
SELECT `current_value` INTO value
FROM `sequence`
WHERE `district_cd` = d AND `apply_date` = today limit 1;
IF needInitSequence = 1 THEN
INSERT INTO `sequence` (`district_cd`, `current_value`, `apply_date`) VALUES (d, value, today);
END IF;
RETURN value;
END
;;
DELIMITER ;
Function:nextval->generate code by district_cd
DELIMITER ;;
CREATE DEFINER=`usr`#`%` FUNCTION `nextval`(d VARCHAR(3)) RETURNS varchar(16) CHARSET utf8
DETERMINISTIC
BEGIN
DECLARE value INTEGER;
SET value = currval(d);
UPDATE `sequence`
SET `current_value` = `current_value` + 1
WHERE `district_cd` = d AND `apply_date` = current_date();
RETURN concat(d, date_format(now(), '%Y%m%d'), LPAD(currval(d), 5, '0'));
END
;;
DELIMITER ;
Triggers of applies->a business logic,if the length of apply_no is greater than 18,it will call the function:nextval to generate a new code
DELIMITER ;;
CREATE TRIGGER `convert_long_no` BEFORE INSERT ON `applies` FOR EACH ROW BEGIN
IF ((SELECT LENGTH(NEW.apply_no)) >= 18) THEN
SET NEW.apply_no = (SELECT nextval(NEW.district_cd));
END IF;
END
;;
DELIMITER ;
My Questions:
Why did the function:nextval returns two same codes?
Why did two records appear in sequnce when update the record.
This is my first stored procedure so I am not sure I am doing this correctly. I have tried to optimize this as much as I can but still end up with a query timeout at 10 minutes of running. I really need this to scale even higher than what I am working with currently. Any assistance would be great.
I have a decent sized data set (108K rows) and one of the fields contains a comma delimited list (I wish the engineers hadn't done this). I need to break apart that field so each entry is on it's own row AND all other fields are assigned to that row as well. I have developed a stored procedure that loops through the table row by row then breaks apart the field and inserts it into a second table.
Here is the code I have used:
DROP TABLE IF EXISTS dwh_inventory.nas_share_temp;
CREATE TABLE dwh_inventory.nas_share_temp (
share_id int(11) NOT NULL,
fileShareId int(11) NOT NULL,
storageId int(11) NOT NULL,
identifier varchar(1024) NOT NULL,
name varchar(255) NOT NULL,
protocol enum('CIFS','NFS') NOT NULL,
ipInterfaces VARCHAR(100) NOT NULL
) ENGINE=INNODB DEFAULT CHARSET=utf8;
DROP PROCEDURE IF EXISTS dwh_inventory.share_step;
DELIMITER $$
CREATE PROCEDURE dwh_inventory.share_step()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE strLen INT DEFAULT 0;
DECLARE SubStrLen INT DEFAULT 0;
DECLARE ip VARCHAR(20);
SET autocommit = 0;
SELECT COUNT(*) FROM dwh_inventory.nas_share INTO n;
SET i=0;
WHILE i<n DO
SELECT id, fileShareId, storageId, identifier, name, protocol, ipInterfaces
INTO #share_id, #fileShareId, #storageId, #identifier, #name, #protocol, #ipInterfaces
FROM dwh_inventory.nas_share LIMIT i,1;
IF #ipInterfaces IS NULL THEN
SET #ipInterfaces = '';
END IF;
do_this:
LOOP
SET strLen = CHAR_LENGTH(#ipInterfaces);
SET ip = SUBSTRING_INDEX(#ipInterfaces, ',', 1);
INSERT INTO dwh_inventory.nas_share_temp
(share_id, fileShareId, storageId, identifier,name,protocol,ipInterfaces)
VALUES (#share_id,
#fileShareId,
#storageId,
#identifier,
#name,
#protocol,
ip
);
SET SubStrLen = CHAR_LENGTH(SUBSTRING_INDEX(#ipInterfaces, ',', 1)) + 2;
SET #ipInterfaces = MID(#ipInterfaces, SubStrLen, strLen);
IF #ipInterfaces = '' THEN
LEAVE do_this;
END IF;
END LOOP do_this;
COMMIT;
SET i = i + 1;
END WHILE;
SET autocommit = 1;
END;
$$
DELIMITER ;
CALL dwh_inventory.share_step();
Example of the data:
id,fileShareId,storageId,identifier,name,protocol,ipInterfaces
1325548,1128971,33309,/vol/vol0/:NFS,/vol/vol0/,NFS,"10.66.213.118,10.68.208.76"
1325549,1128991,33309,/vol/vol0/:NFS,/vol/vol0/,NFS,"10.66.213.119,10.68.208.77"
1325550,1128992,33325,/vol/aggr2_64_hs2032/EPS_ROOT/:NFS,/vol/aggr2_64_hs2032/EPS_ROOT/,NFS,10.17.124.10
1325551,1128993,33325,/vol/aggr2_64_hs2032/GCO_Report/:NFS,/vol/aggr2_64_hs2032/GCO_Report/,NFS,10.17.124.10
1325552,1128995,33325,/vol/aggr2_64_hs2032/PI/:NFS,/vol/aggr2_64_hs2032/PI/,NFS,10.17.124.10
1325553,1128996,33325,/vol/aggr2_64_hs2032/a/:NFS,/vol/aggr2_64_hs2032/a/,NFS,10.17.124.10
1325554,1128997,33325,/vol/aggr1_64_sapserv/:NFS,/vol/aggr1_64_sapserv/,NFS,147.204.2.13
1325555,1128999,33325,/vol/aggr2_64_hs2032/:NFS,/vol/aggr2_64_hs2032/,NFS,10.17.124.10
1325556,1129001,33325,/vol/aggr2_64_hs2032/central/:NFS,/vol/aggr2_64_hs2032/central/,NFS,10.17.124.10
1325557,1129004,33325,/vol/nsvfm0079b_E5V/db_clients/:NFS,/vol/nsvfm0079b_E5V/db_clients/,NFS,"10.21.188.161,10.70.151.93"
1325558,1129006,33325,/vol/aggr2_64_hs2032/istrans/:NFS,/vol/aggr2_64_hs2032/istrans/,NFS,10.17.124.10
1325559,1129008,33325,/vol/nsvfm0017_DEWDFGLD00603/:NFS,/vol/nsvfm0017_DEWDFGLD00603/,NFS,"10.21.188.115,10.70.151.138"
1325560,1129009,33325,/vol/nsvfm0017_vol0/:NFS,/vol/nsvfm0017_vol0/,NFS,"10.21.188.115,10.70.151.138"
1325561,1129011,33325,/vol/nsvfm0017a_ls2278/:NFS,/vol/nsvfm0017a_ls2278/,NFS,"10.21.188.115,10.70.151.138"
1325562,1129015,33325,/vol/nsvfm0051passive_vol0/:NFS,/vol/nsvfm0051passive_vol0/,NFS,10.17.144.249
1325563,1129017,33325,/vol/nsvfm0053_vol0/:NFS,/vol/nsvfm0053_vol0/,NFS,"10.21.189.251,10.70.151.109"
InnoDB tables must have a PRIMARY KEY.
LIMIT i,1 will get slower and slower as you go through the table -- it hast to skip over i rows before finding the one you need.
Don't try to split comma-separated text in SQL; use a real language (PHP / Perl / etc). Or, as Lew suggests, write out that column, then use LOAD DATA to bring it into another table.
LIMIT should be preceded by an ORDER BY.
I have developed a function for split string in tsql but mysql don't have some built in functions. I needed to function in MYSQL as i am new in mysql. Function should accept 2 parameters
1. String to be split
2. separator (',' or whatever)
Kindly reply me.
i had found solution on the internet you can into that.
DELIMITER //
DROP FUNCTION IF EXISTS `splitAndTranslate` //
CREATE FUNCTION splitAndTranslate(str TEXT, delim VARCHAR(124))
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE i INT DEFAULT 0; -- total number of delimiters
DECLARE ctr INT DEFAULT 0; -- counter for the loop
DECLARE str_len INT; -- string length,self explanatory
DECLARE out_str text DEFAULT ''; -- return string holder
DECLARE temp_str text DEFAULT ''; -- temporary string holder
DECLARE temp_val VARCHAR(255) DEFAULT ''; -- temporary string holder for query
-- get length
SET str_len=LENGTH(str);
SET i = (LENGTH(str)-LENGTH(REPLACE(str, delim, '')))/LENGTH(delim) + 1;
-- get total number delimeters and add 1
-- add 1 since total separated values are 1 more than the number of delimiters
-- start of while loop
WHILE(ctr<i) DO
-- add 1 to the counter, which will also be used to get the value of the string
SET ctr=ctr+1;
-- get value separated by delimiter using ctr as the index
SET temp_str = REPLACE(SUBSTRING(SUBSTRING_INDEX(str, delim, ctr), LENGTH(SUBSTRING_INDEX(str, delim,ctr - 1)) + 1), delim, '');
-- query real value and insert into temporary value holder, temp_str contains the exploded ID
SELECT <real_value_column> INTO temp_val FROM <my_table> WHERE <table_id>=temp_str;
-- concat real value into output string separated by delimiter
SET out_str=CONCAT(out_str, temp_val, ',');
END WHILE;
-- end of while loop
-- trim delimiter from end of string
SET out_str=TRIM(TRAILING delim FROM out_str);
RETURN(out_str); -- return
END//
reference http://www.slickdev.com/2008/09/15/mysql-query-real-values-from-delimiter-separated-string-ids/
In mysql they they dont support some functionality like sqlserver. so spliting will be difficult in mysql
SELECT e.`studentId`, SPLIT(",", c.`courseNames`)[e.`courseId`]
FROM ..
SELECT TRIM(SUBSTRING_INDEX(yourcolumn,',',1)), TRIM(SUBSTRING_INDEX(yourcolumn,',',-1)) FROM yourtable
CREATE FUNCTION [dbo].[SplitString]
(
#RowData nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
--Id int identity(1,1),
Data nvarchar(100)
)
AS
BEGIN
Declare #Cnt int
Set #Cnt = 1
While (Charindex(#SplitOn,#RowData)>0)
Begin
Insert Into #RtnValue (data)
Select
Data = ltrim(rtrim(Substring(#RowData,1,Charindex(#SplitOn,#RowData)-1)))
Set #RowData = Substring(#RowData,Charindex(#SplitOn,#RowData)+1,len(#RowData))
Set #Cnt = #Cnt + 1
End
Insert Into #RtnValue (data)
Select Data = ltrim(rtrim(#RowData))
Return
END