Related
We need a function / stored procedure to return a column in a row in a table, where we pass the table, id field name, id and column to return.
We have a table which is an activity log with columns such as
log_datetime - the date/time the user did something
log_table - the table affected
log_idcol - the column in log_table that holds the id
log_id - the id of the row in log_table
log_titlecol - the column in log_table which holds the 'title' of the
item
So in a report/ MySql statement I need to be able to list the title which is held in the column name held in log_titlecol, read from the table with the name in the log_table column, and id of whatever is in log_idcol.
so like
SELECT log_titlecol FROM log_table where log_idcol = log_id
where all the 'log_; parts are replaceable. I can't see a way to do this in MySQL, so is there a way to do this in a stored procedure/function?
For example:
CREATE TABLE jokes (
JokeID smallint(4) NOT NULL, **(autoincrement, unique)**
Title varchar(50) CHARACTER SET utf8 DEFAULT NULL,
Note text CHARACTER SET utf8
)
CREATE TABLE idioms (
IdiomID smallint(4) NOT NULL, **(autoincrement, unique)**
IdiomTitle varchar(50) CHARACTER SET utf8 DEFAULT NULL,
Note text CHARACTER SET utf8
)
CREATE TABLE `log` (
id smallint(4) NOT NULL, **(autoincrement, unique)**
log_datetime datetime,
log_table varchar(50) CHARACTER SET utf8 DEFAULT NULL,
log_idcol varchar(50) CHARACTER SET utf8 DEFAULT NULL,
log_id smallint(4) NOT NULL,
log_titlecol varchar(50) CHARACTER SET utf8 DEFAULT NULL,
)
INSERT INTO jokes (Title, Notes)
VALUES ('Funny joke','This is note1')
INSERT INTO jokes (Title, Notes)
VALUES ('Another Funny joke','This is another note')
INSERT INTO idioms (IdiomTitle, Notes)
VALUES ('Bird in the hand','What this means..')
INSERT INTO jokes (Title, Notes)
VALUES ('Another Funny joke','This is another note')
INSERT INTO log (log_datetime, log_table, log_idcol, log_id , log_titlecol)
VALUES (now(), 'jokes','JokeID',1,'Title')
INSERT INTO log (log_datetime, log_table, log_idcol, log_id , log_titlecol)
VALUES (now(), 'jokes','JokeID',2,'Title')
INSERT INTO log (log_datetime, log_table, log_idcol, log_id , log_titlecol)
VALUES (now(), 'idioms','IdiomID',2,'IdiomTitle')
So now how can I have a report from log showing the date time and the Title column from the Jokes table, row id as in the log_id column? I need a function.
eg SELECT log_datetime, log_table, log_id, GetTitle(log_table, log_idcol, log_id, log_titlecol) from log
Where GetTitle is a function which will return the column held in 'log_titlecol' form the table passed as log_table, from the row with an id (held in the log_idcol column) of log_id
So for example the output would show:
2018-01-01 12:00 jokes 1 Funny Joke
2018-01-01 12:10 jokes 2 Another Funny joke
2018-01-01 12:11 idioms 1 Bird in the hand
I have tried
CREATE PROCEDURE Getcol(IN tab TEXT CHARSET utf8mb4, IN col TEXT CHARSET utf8mb4, IN idcol TEXT CHARSET utf8mb4, IN id INT(15), OUT outcol TEXT CHARSET utf8mb4)
DETERMINISTIC
COMMENT 'Return a column from any table'
BEGIN
SET #Expression = CONCAT('SELECT ', col,' INTO #outcol FROM ', tab, ' where ', idcol, ' = ', id);
PREPARE myquery FROM #Expression;
EXECUTE myquery;
SELECT #outcol;
END
I can call this like
CALL GetCol('Jokes','Title','JokeID',1)
And this works to return the Title column for ID 1, but I am cant seem to then put this call into a function
CREATE FUNCTION getrowcol(tab TEXT, col TEXT, idcol TEXT, id INT) RETURNS text CHARSET utf8mb4
NO SQL
COMMENT 'Return a column from any table'
BEGIN
DECLARE outvar TEXT;
CALL GetCol(tab, col, idcol, id, #out1);
SELECT #out1 INTO outvar;
RETURN outvar;
END
This returns blank. How can I return #out1?
This is essentially the same question (with no answer)
https://dba.stackexchange.com/questions/151328/dynamic-sql-stored-procedure-called-by-a-function
Try something like,
DELIMITER //
CREATE PROCEDURE demo(tab VARCHAR(50), tit VARCHAR(50))
BEGIN
SET #Expression = CONCAT('SELECT l.log_datetime, j.',tit,' FROM `log` l INNER JOIN ',tab,' j;');
PREPARE myquery FROM #Expression;
EXECUTE myquery;
END
//
DELIMITER ;
Call it like
CALL demo('jokes', 'title')
from Reference
It seems something is wrong with my procedure. When I execute it, it should create an entry, directly modify a column and then return the id of it.
But nothing is created and it also returns nothing (not even an error is shown)
When I use hard coded values instead of using the parameters, the entry is created, but not edited and the ID is still not returned.
Could you guys have a look over it? This is the whole procedure result using the export function of phpMyAdmin:
CREATE DEFINER=`dbuser`#`localhost` PROCEDURE `createRecruitment`(IN `p_user_id` INT(11), IN `p_platform` ENUM('uplay','steam','ps4','xb1') CHARSET utf8, IN `p_activity` ENUM('pve','pvp') CHARSET utf8, IN `p_description` TEXT CHARSET utf8)
MODIFIES SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE v_id INT(11);
INSERT INTO
recruitments (
user_id,
creationDate,
platform_id,
activity,
description
)
SELECT
p_user_id,
CURRENT_TIMESTAMP,
platforms.id,
p_activity,
p_description
FROM
platforms
WHERE
platforms.platform = p_platform;
SET v_id = LAST_INSERTED_ID();
UPDATE
recruitments
SET
lastActivity = creationDate
WHERE
id = v_id;
SELECT v_id;
END
EDIT
It seems the main problem is the "p_platform" parameter. I wanted to limit the input to the given ENUM but it seems the WHERE platforms.platform = p_platform doesn't work proper with this.
Use the below SP. I have use OUT v_id field and assign the value
CREATE DEFINER=`dbuser`#`localhost` PROCEDURE `createRecruitment`(IN `p_user_id` INT(11), IN `p_platform` ENUM('uplay','steam','ps4','xb1') CHARSET utf8, IN `p_activity` ENUM('pve','pvp') CHARSET utf8, IN `p_description` TEXT CHARSET utf8, OUT `v_id` INT(11))
MODIFIES SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
INSERT INTO
recruitments (
user_id,
creationDate,
platform_id,
activity,
description
)
SELECT
p_user_id,
CURRENT_TIMESTAMP,
platforms.id,
p_activity,
p_description
FROM
platforms
WHERE
platforms.platform = p_platform;
SET v_id = LAST_INSERTED_ID();
UPDATE
recruitments
SET
lastActivity = creationDate
WHERE
id = v_id;
END
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.
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.
I am writing a stored procedure to create a player:
CREATE PROCEDURE `create_player` (
firstName TEXT CHARACTER SET utf8,
lastName TEXT CHARACTER SET utf8,
username TEXT CHARACTER SET utf8,
password TEXT CHARACTER SET utf8,
email TEXT CHARACTER SET utf8,
gender ENUM('m','f'),
avatar INTEGER,
OUT result INTEGER)
BEGIN
DECLARE insertVal INTEGER DEFAULT 0;
INSERT INTO `Players`
(`PlayerFirstName`,
`PlayerLastName`,
`PlayerName`,
`PlayerPassword`,
`PlayerEmail`,
`PlayerGender`,
`PlayerAvatar`,
`PlayerJoinDate`) VALUES (
firstName,lastName,username,player_hash_password(password),email,gender,avatar,NOW());
END
Say I wanted to let the user know if this insert succeeded or not, how can I find out if it succeeded or not (number of rows affected). I tried to set an integer variable = to the insert statement but that did not work.
What is the typical way of error checking for stored procedures?
As per MySQL documentation on ROW_COUNT(),
ROW_COUNT() returns the number of rows changed, deleted, or inserted by the last statement if it was an UPDATE, DELETE, or INSERT. For other statements, the value may not be meaningful
After insert statement you can read row_count() into OUT parameter result. Use the same result value in the calling program to let the user know the number of rows affected.
Change your procedure as follows:
DROP PROCEDURE IF EXISTS `create_player`;
delimiter //
CREATE PROCEDURE `create_player` (
firstName TEXT CHARACTER SET utf8,
lastName TEXT CHARACTER SET utf8,
username TEXT CHARACTER SET utf8,
password TEXT CHARACTER SET utf8,
email TEXT CHARACTER SET utf8,
gender ENUM('m','f'),
avatar INTEGER,
OUT result INTEGER )
BEGIN
DECLARE insertVal INTEGER DEFAULT 0;
INSERT INTO `Players`(
`PlayerFirstName`,
`PlayerLastName`,
`PlayerName`,
`PlayerPassword`,
`PlayerEmail`,
`PlayerGender`,
`PlayerAvatar`,
`PlayerJoinDate`)
VALUES (
firstName, lastName, username,
player_hash_password( password ),
email, gender, avatar, NOW() );
SELECT ROW_COUNT() INTO result;
END;
//
delimiter ;
When you insert data through ExecuteNonQuery() method. This method returns number of rows affected in database as integer.
For Example.
int i=0;
i= cmd.ExecuteNonQuery();
if( i >0)
{
msg ="Inserted Successfully";
}
else
msg="Not Inserted";
using php code would help you
<?php
include("connect.php");//..your db connection file
$result = mysql_query("SELECT * FROM Players");
if (mysql_num_rows($result)){
echo "Data entered successfully";
}
else
{echo "failed";}
?>