I am writing an application that supports custom fields. Currently I store all custom fields in a XML formated text field ( e.g. '<root><field1>val1</field1><field2>val2</field2></root>' in cust_field)
I am able to to use updateXML(cust_field, '/root/field1', '<field1>new value</field1') to update those values, however if I use updateXML(cust_field, '/root/field3', '<field3>new value</field3>') then it does not work since field3 is not in the old value. Is there a way to let MySQL automatically insert the new field3 node and its value into cust_field? I am thinking about stored procedure or even stored function but not familiar with both, can anyone point me to the right direction?
The MySQL XML Functions will not do this automatically.
You can create a stored function to call UpdateXML() if the element is present, otherwise to add the element using your own logic.
Here's a basic template to get you started:
DELIMITER $$
CREATE FUNCTION update_xml(xml_target text, xpath_expr text, new_xml text) returns text
BEGIN
DECLARE return_val text;
IF (ExtractValue(xml_target,xpath_expr) != '')
THEN
RETURN updateXML(xml_target,xpath_expr,new_xml);
ELSE
SET return_val := xml_target;
-- add code here to insert the new element into your XML string
RETURN return_val;
END IF;
END $$
I actually ended up putting the logic in my app sql engine.
Related
in your opinion, what would be the best or most efficient way to do this.
I'm working with treegrid where you can edit a row, add a new one, or remove existing one.
Save button triggers creating a json file
EDIT
ADD (INSERT)
REMOVE
Now I have to write a procedure that is also triggered with save button that would actually store these changes into the database.
I have something like this for updating.
create or replace function updateDrivers (p_data varchar2)
return varchar2
IS
p_result varchar2(50);
p_team_name varchar2(50);
p_driver_name varchar2(50);
p_driver_lastName varchar2(50);
p_driverID varchar2(50);
Begin
SELECT JSON_value(p_data,'$.Changes.ime_tima') INTO p_team_name from dual;
SELECT JSON_value(p_data,'$.Changes.ime_vozaca') INTO p_driver_name from dual;
SELECT JSON_value(p_data,'$.Changes.prezime_vozaca') INTO p_driver_lastName from dual;
SELECT JSON_value(p_data,'$.Changes.id') INTO p_driverID from dual;
UPDATE drivers
SET ime_tima = p_team_name,
ime_vozaca = p_driver_name,
prezime_vozaca = p_driver_lastName
where id = p_driverID;
p_result := '1|success!';
return p_result;
EXCEPTION
WHEN OTHERS THEN
p_result := '-1|Error!';
RETURN p_result;
END updateDrivers;
The problem is, I have multiple tables, this one is example for drivers, but I have one for teams, races etc. Would it be better to make one procedure for every action (update/insert/delete) and for each table or should I make it one procedure which takes different arguments depending on the table and action.
I will make simpler than it is to get the answer I need without make you read a lot of code.
MySQL stored procedure:
CREATE PROCEDURE add_player
(IN name varchar(100),
IN isTrue boolean)
BEGIN
START TRANSACTION;
insert into tags (name,is_player) values (name,isTrue);
COMMIT;
END //
player_controller.rb
ActiveRecord::Base.connection.execute("call add_player('#{name}', #{is_player})")
Two problems I see(if you see more - say):
if name contains ' it breaks the call
sql injection - I don't use ? as parameters when I call the stored procedure. The reason is that it's just not working when I'm try with ?. I tried also change it to Player.where("add_player(?,?)",name,is_player)
Did you try something like this ?
ActiveRecord::Base.connection.execute("call add_player(#{ActiveRecord::Base.sanitize(name)}, #{is_player})")
There is another way suggested on the following SO link
using sanitize_sql_array to escape strings
The only problem is that sanitize_sql_array is not a public method
I am working on a project in VS2012 while using MySQL database and Entity Framework 5.0.0.
When I try to create a new Complex type from a stored procedure, I get an error when clicking "Get Column Information" button:
The selected stored procedure or function returns no columns.
My stored procedure code is as following:
DROP PROCEDURE IF EXISTS SearchAlgemeenSet;
Delimiter //
CREATE PROCEDURE SearchAlgemeenSet(IN in_searchQuery VARCHAR(255))
BEGIN
SELECT Blokken, Jaargang, Werk_Uren
FROM algemeensets
WHERE Blokken LIKE in_searchQuery
OR Jaargang LIKE in_searchQuery
OR Werk_Uren LIKE in_searchQuery;
END
//
Delimiter ;
I'm positive that it returns columns if the in_searchQuery parameter has a match.
In my research, I have found plenty solutions for Microsoft SQL database. But none of those solutions apply to MySQL database.
How to solve this?
I'm positive that it returns columns, if the in_searchQuery
parameter has a match.
Because you are not using any wild card for partial search, unless it finds an exact match, for in_searchQuery value, no rows will be returned. For partial match to happen, you need to use wild card symbol '%' with the in_searchQuery value.
Modified procedure, should be like the following:
DROP PROCEDURE IF EXISTS SearchAlgemeenSet;
Delimiter //
CREATE PROCEDURE SearchAlgemeenSet(IN in_searchQuery VARCHAR(255))
BEGIN
set #search_criteria := concat( '%', in_searchQuery, '%' );
SELECT Blokken, Jaargang, Werk_Uren
FROM algemeensets
WHERE Blokken LIKE #search_criteria
OR Jaargang LIKE #search_criteria
OR Werk_Uren LIKE #search_criteria;
END;
//
Delimiter ;
When no search criteria has any matches found, and empty set would be returned.
In your scripting language, you have to check, in advance, whether the procedure has returned any results or not. Based on that, you can show a message that no data found or you can perform other actions.
I want a MySQL stored procedure which effectively does:
foreach id in (SELECT id FROM objects WHERE ... ) CALL testProc(id)
I think I simply want the MySQL answer to this question but I don't understand cursors well: How do I execute a stored procedure once for each row returned by query?
Concepts such as “loops” (for-each, while, etc) and “branching” (if-else, call, etc) are procedural and do not exist in declarative languages like SQL. Usually one can express one’s desired result in a declarative way, which would be the correct way to solve this problem.
For example, if the testProc procedure that is to be called uses the given id as a lookup key into another table, then you could (and should) instead simply JOIN your tables together—for example:
SELECT ...
FROM objects JOIN other USING (id)
WHERE ...
Only in the extremely rare situations where your problem cannot be expressed declaratively should you then resort to solving it procedurally instead. Stored procedures are the only way to execute procedural code in MySQL. So you either need to modify your existing sproc so that it performs its current logic within a loop, or else create a new sproc that calls your existing one from within a loop:
CREATE PROCEDURE foo() BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE _id BIGINT UNSIGNED;
DECLARE cur CURSOR FOR SELECT id FROM objects WHERE ...;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;
OPEN cur;
testLoop: LOOP
FETCH cur INTO _id;
IF done THEN
LEAVE testLoop;
END IF;
CALL testProc(_id);
END LOOP testLoop;
CLOSE cur;
END
I'm a SQL noob, and I need a little bit of help understanding the big picture of if it is possible, and if so how to go about filtering a result set based on the return value of a function which is passed one of the fields of the record.
Let's say I have a table called "Numbers" with just one field: "Value".
How could I correctly specify the following "pseudo-sql"?:
SELECT Value FROM numbers WHERE IsPrime(Value)=true
Can I accomplish such a thing, and if so, where/how do I put/store "IsPrime"?
I'm using MySQL.
I agree with extraneon that it's usually better to store this value in the database rather than compute it in the where clause. This way you can compute it once per row and index the data for faster performance.
As long as you are on MySQL 5.x, I would recommend a stored function, as opposed to a UDF. You can add an IS_PRIME column of type TINYINT to your DB, add an index on that column, then use the stored function to calculate the value at insert time. You can even calculate the IS_PRIME value using a before insert trigger if you don't want to change the insert code.
The stored function would look something like this:
DELIMITER $$
DROP FUNCTION IF EXISTS IS_PRIME $$
CREATE FUNCTION IS_PRIME(P_INT BIGINT) RETURNS TINYINT
BEGIN
DECLARE V_FACTOR BIGINT;
DECLARE V_MAX_FACTOR BIGINT;
SET V_FACTOR := 2;
SET V_MAX_FACTOR := round(sqrt(P_INT),0);
WHILE (V_FACTOR <= V_MAX_FACTOR)
DO
IF (P_INT % V_FACTOR) = 0
THEN
RETURN FALSE;
END IF;
SET V_FACTOR := V_FACTOR + 1;
END WHILE;
RETURN TRUE;
END $$
DELIMITER ;
I think you may find some help with the doc about the user-defined functions: http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html
I don't know anything about user defined functions, but I could imagine that for computation-intensive functions it might be best to have that value precomputed and stored in the database somewhere.
Depending on how the data gets in the database you could require the client to compute isPrime (that can be done if the client is a web service or app on your own server), or perhaps a scheduled job which processes every record with isPrime is null or something like that. That is not instantaneous, but for some scenario's it may be good enough.
If isPrime is only used sometimes you could also post-process/filter the data on the client when retrieving data.