I have a simple MySQL function for comparing versions:
CREATE FUNCTION `compareVersions` (
versionA VARCHAR(50),
versionB VARCHAR(50)) RETURNS INT DETERMINISTIC NO SQL
BEGIN
DECLARE a1 INT;
DECLARE b1 INT;
DECLARE c1 INT;
DECLARE d1 INT;
DECLARE a2 INT;
DECLARE b2 INT;
DECLARE c2 INT;
DECLARE d2 INT; SET a1 = SUBSTRING_INDEX( `versionA` , '.', 1 );
SET b1 = SUBSTRING_INDEX(SUBSTRING_INDEX( `versionA` , '.', 2 ),'.',-1);
SET c1 = SUBSTRING_INDEX(SUBSTRING_INDEX( `versionA` , '.', -2 ),'.',1);
SET d1 = SUBSTRING_INDEX( `versionA` , '.', -1 );
SET a2 = SUBSTRING_INDEX( `versionB` , '.', 1 );
SET b2 = SUBSTRING_INDEX(SUBSTRING_INDEX( `versionB` , '.', 2 ),'.',-1);
SET c2 = SUBSTRING_INDEX(SUBSTRING_INDEX( `versionB` , '.', -2 ),'.',1);
SET d2 = SUBSTRING_INDEX( `versionB` , '.', -1 );
IF (a1 > a2) THEN
RETURN -1;
ELSEIF ((a1 = a2) AND (b1 > b2)) THEN
RETURN -1;
ELSEIF ((a1 = a2) AND (b1 = b2) AND (c1 > c2)) THEN
RETURN -1;
ELSEIF ((a1 = a2) AND (b1 = b2) AND (c1 = c2) AND (d1 > d2)) THEN
RETURN -1;
ELSEIF ((a1 = a2) AND (b1 = b2) AND (c1 = c2) AND (d1 = d2)) THEN
RETURN 0;
ELSE
RETURN 1;
END IF;
END $$
and its creation fails with
You do not have the SUPER privilege and binary logging is enabled (you
might want to use the less safe log_bin_trust_function_creators variable)
This is almost the same as this question, but my function does not read any SQL data, it is simple, deterministic and I see no reason why it should require any extra privileges. It is not clear to me from the documentation if the SUPER privilege is required for creating all functions (which would be ridiculous, making stored functions unavailable to many users, everyone who does not have access to their database configuration). I do not even know if the function works, this was the first thing that came to mind, but the syntax should be correct (the delimiter is set in PHPMyAdmin). Getting all data from database and comparing them in the PHP application can be done, but I think it is easiest done this way. Is it possible? Does anybody have a better solution for comparing versions?
You can adjust the global variable for that:
/* allows to create functions as not root */
SET GLOBAL log_bin_trust_function_creators = 1;
I use this in setup-sql files for initializing a database for restoring a dump.
I have circumvented the problem by making a long and therefore ugly database query in PHP. Maybe using stored procedure instead of function would be better. After searching I found in the documentation here this statement:
The current conditions on the use of stored functions in MySQL 5.5 can
be summarized as follows. [...] To create or alter a stored function,
you must have the SUPER privilege, in addition to the CREATE ROUTINE
or ALTER ROUTINE privilege that is normally required.
So indeed, if binary logging is on, you really need SUPER privilege to create stored functions. This severely limits the use of stored functions on bigger servers and in my opinion decreases the value of the whole DBMS. Looks like the classical "it is not a bug, it is a feature". I fear to think about what happens to stored functions if the database server is restarted after changing binary logging to on.
Related
This my code
$_SESSION['poraw'] = substr(md5(time()), 0, 16);
$userid= $_SESSION["userid"];
$today = date('Y-m-d');
$month = $_POST['month'];
for($i = 0; $i < count($_POST['id']); $i++){
$id = $_POST['id'][$i];
$resin = $_POST['resin'][$i];
$hasil = $_POST['hasil'][$i];
$sql="INSERT INTO po_supplier_temp (unique_code,po_customer_month,resin,qty,username)
VALUES ('".$_SESSION['poraw']."','$month','$resin','$hasil','$userid')";
$sql = $conn->query($sql);
}
Current results:
if $resin same , i want $hasil plus it (+) , before save to dabatase and only one name $resin to database in same time
Like this:
thanks
You have two options: Create a stored procedure to handle the custom logic, or integrate the logic inside your php.
I'll only show you how you can make a stored procedure to do the task you wish to complete. If you need help on how to use a stored procedure, this has already been answered somewhere around here.
--- Transact-SQL Syntax
CREATE PROCEDURE [dbo].[InsertOrUpdateMyThing]
(
#SessionId int,
#Month date,
#Resin int,
#Hasil int,
#UserId int
)
AS
BEGIN
SET NOCOUNT ON
IF EXISTS(SELECT 1 FROM po_supplier_temp WHERE resin = #Resin)
BEGIN
UPDATE p
SET p.resin = p.resin + 1
FROM po_supplier_temp p
WHERE p.resin = #Resin
END
ELSE
BEGIN
INSERT INTO po_supplier_temp (unique_code, po_customer_month, resin,qty, username)
VALUES (#SessionId, #Month, #Resin, #Hasil, #UserId)
END
END
EDIT: If you're passing multiple values at the same time, you'll need to edit this to accept a table-valued parameter with all the necessary information, as well as modify the code inside the procedure to handle a set of data. It'd be better to do that, rather than invoke this procedure X amount of times.
I have table invoices and there is column 'total' varchar(255). There are values like these: "500.00", "5'199.00", "129.60", "1.00" and others.
I need select records and filter by total column. For example, find records where total is not more than 180.
I tried this:
SELECT total from invoices WHERE invoices.total <= '180'
But in result there are :
125.25
100.50
1593.55 - not correct
4'799.00 - not correct
1.00
-99.00
2406.52 -not correct
How can I fix it and write correct filter for this column? Thanks!
You can use cast() function to convert it in float
SELECT total from invoices WHERE cast(invoices.total as decimal(16,2)) <= 180
Why are you storing numbers as strings? That is a fundamental problem with your data model, and you should fix it.
Sometimes, we are stuck with other people's really, really, really bad decisions. If that is the case, you can attempt to solve this with explicit conversion:
SELECT i.total
FROM invoices i
WHERE CAST(REPLACE(i.total, '''', '') as DECIMAL(20, 4)) <= 180;
Note that this will return an error if you have other unexpected characters in your totals.
If the string starts with a number, then contains non-numeric characters, you can use the CAST() function or convert it to a numeric implicitly by adding a 0:
SELECT CAST('1234abc' AS UNSIGNED); -- 1234
SELECT '1234abc'+0; -- 1234
To extract numbers out of an arbitrary string you could add a custom function like this:
DELIMITER $$
CREATE FUNCTION `ExtractNumber`(in_string VARCHAR(50))
RETURNS INT
NO SQL
BEGIN
DECLARE ctrNumber VARCHAR(50);
DECLARE finNumber VARCHAR(50) DEFAULT '';
DECLARE sChar VARCHAR(1);
DECLARE inti INTEGER DEFAULT 1;
IF LENGTH(in_string) > 0 THEN
WHILE(inti <= LENGTH(in_string)) DO
SET sChar = SUBSTRING(in_string, inti, 1);
SET ctrNumber = FIND_IN_SET(sChar, '0,1,2,3,4,5,6,7,8,9');
IF ctrNumber > 0 THEN
SET finNumber = CONCAT(finNumber, sChar);
END IF;
SET inti = inti + 1;
END WHILE;
RETURN CAST(finNumber AS UNSIGNED);
ELSE
RETURN 0;
END IF;
END$$
DELIMITER ;
Once the function is defined, you can use it in your query:
SELECT total from invoices WHERE ExtractNumber(invoices.total) <= 180
I have created the following stored proc in MySQL:
BEGIN
DECLARE temp_mold VARCHAR(30);
DECLARE temp_time DATETIME;
DECLARE temp_shot_ct INT;
DECLARE temp_shot_count INT;
DECLARE temp_shot_mold VARCHAR(30);
DECLARE temp_shot_cavity_count INT;
DECLARE temp_job_count INT;
DECLARE temp_row_count INT;
DECLARE done INT DEFAULT 0;
DECLARE temp_shot_counter INT DEFAULT 1;
DECLARE setup_time INT;
DECLARE cursor1 CURSOR FOR
SELECT
(schedule_run_time*3600)/(schedule_qty/schedule_cavity_count) as shot_ct,
ROUND((schedule_qty/schedule_cavity_count)) as shot_count,
schedule_mold as shot_mold, schedule_cavity_count as shot_cavity_count
FROM `schedule`
WHERE `schedule`.schedule_cell = start_cell AND `schedule`.schedule_date = DATE(start_time) AND deleted = 0
ORDER BY schedule_order ASC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
SELECT COUNT(schedule_id) INTO temp_job_count FROM `schedule` WHERE schedule_cell = start_cell AND schedule_date = DATE(start_time) AND deleted = 0;
SELECT cell_setup_standard INTO setup_time FROM cells WHERE cell_name = start_cell;
DROP TEMPORARY TABLE IF EXISTS rst;
CREATE TEMPORARY TABLE rst (
shot_id INT NOT NULL AUTO_INCREMENT,
shot_datetime datetime,
shot_qty INT,
PRIMARY KEY (shot_id)
);
SET temp_time = start_time;
SET temp_mold = "";
SET temp_row_count = 0;
INSERT INTO rst (shot_datetime) VALUES (temp_time);
#Loop through cursor1 recordset
OPEN cursor1;
start_loop: LOOP
FETCH cursor1 INTO temp_shot_ct, temp_shot_count, temp_shot_mold, temp_shot_cavity_count;
IF done THEN
LEAVE start_loop;
END IF;
IF temp_mold <> temp_shot_mold THEN
SET temp_time = TIMESTAMPADD(MINUTE,setup_time,temp_time); #Add standard setup time whenever the mold changes
INSERT INTO rst (shot_datetime) VALUES (temp_time);
SET temp_mold = temp_shot_mold;
END IF;
SET temp_shot_counter = 1;
REPEAT
SET temp_time = TIMESTAMPADD(SECOND,temp_shot_ct,temp_time);
INSERT INTO rst (shot_datetime,shot_qty) VALUES (temp_time,temp_shot_cavity_count);
SET temp_shot_counter = temp_shot_counter + 1;
UNTIL temp_shot_counter > temp_shot_count
END REPEAT;
END LOOP;
CLOSE cursor1;
#Return records. Will be used in ORM to loop through records on the PHP side
SELECT SUM(shot_qty) AS qty, HOUR(shot_datetime) AS shot_hour FROM rst GROUP BY HOUR(shot_datetime);
DROP TEMPORARY TABLE IF EXISTS rst;
END
I'm not the greatest at using stored procedures. This one is running very slow - it takes about 6 seconds to run. The queries individually run in milliseconds, so I was expecting this to take 100 milliseconds at most. I can't figure out what I'm doing wrong that's causing this to run so slowly. Does anyone have any ideas on how to make this run quicker? I can create a PHP script to do the same thing, but I'm trying to get better at using stored procs and functions in MySQL - and this is far less code. I welcome any and all advice.
I've been recently stuck on an issue I've been having involving sending a string of comma separated values into a stored procedure. My issue is that when I execute my stored procedure in PHP it uploads the values with quotes around it like so;
CALL `rankingInformation`('145', '5', '', '37,38,39,40,41')
Failing to add the quotes would make MySQL interpret them as extra parameters.
However it's mean't to be like in the WHERE IN on the query side it's meant to be formatted like so
'37', '38', '39', '40', '41'
Here is the query below, can anyone spot anything I can do? Here is what I've got up to now.
CREATE DEFINER = `root`#` localhost` PROCEDURE` rankingInformation`(IN` surveyId` INT, IN` filterCounting` INT, IN` survey_filter_id` INT, IN` question_limit` TEXT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'Gathers all the ranking information for a given ID'
BEGIN
DECLARE sfi int(2);
DECLARE ql TEXT;
IF(survey_filter_id = '') THEN
SET sfi = (SELECT sf2.survey_filter_id FROM survey_filters AS sf2 WHERE sf2.survey_id = 145 AND sf2.survey_filter_id IS NOT NULL LIMIT 1);
ELSE
SET sfi = survey_filter_id;
END IF;
SELECT
COUNT( * ) AS total, CONCAT(su.first_name, ' ', su.last_name) as full_name, sf.survey_filter_id, sf.survey_filter_name, qa.question_id, su.temp_group_1 AS department
FROM questions_answers AS qa
INNER JOIN survey_users AS su ON su.survey_users_id = qa.survey_users_id_answer
INNER JOIN survey_filters AS sf ON sf.survey_id = surveyId
WHERE qa.survey_id = surveyId
AND qa.question_id IN (splitAndTranslate(question_limit, ','))
AND sf.survey_filter_id = sfi
GROUP BY qa.survey_users_id_answer
HAVING total > filterCounting
ORDER BY total DESC;
END
splitAndTranslate
Here is a function I found which is mean't to do the job, I am not sure I am far away.
CREATE DEFINER=`root`#`localhost` FUNCTION `splitAndTranslate`(`str` TEXT, `delim` VARCHAR(1))
RETURNS text CHARSET utf8
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'Fixes all Where IN issues'
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 ImageFileName INTO temp_val FROM custombu_roomassets_images WHERE ImageID=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
What did you do with FIND_IN_SET? Building on spencer7593's answer, it should work if you replace:
AND qa.question_id IN (splitAndTranslate(question_limit, ','))
with
AND FIND_IN_SET(qa.question_id, question_limit)>0
The commas within a string value are not interpreted as SQL text in the context of a SQL IN comparison. Your query is essentially of the form:
AND qa.question_id IN ('some,long,string,value')
And any comma characters within the string are just data; just characters that are part of the string. This is effectively the same as an equals comparison.
The MySQL FIND_IN_SET function might be a way for you to perform the comparison you want.
http://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_find-in-set
AND qa.question_id IN (splitAndTranslate(question_limit, ','))
Put these lines instead of the above line
AND (qa.question_id = question_limit
OR qa.question_id LIKE CONCAT(question_limit,',%')
OR qa.question_id LIKE CONCAT('%,',question_limit,',%')
OR qa.question_id LIKE CONCAT('%,',question_limit))
Then you don't need the splitAndTranslate Function anymore . .
Trying to convert this tax-like IRS function Calculating revenue share at different tiers to SQL. The assumption is the table would be one column (price) and the tiers will be added dynamically.
My first attempt has some case statements that didn't work out well. I have since scrapped it :) Thanks for the help!
SQL isn't really suited to iterative loops. I'd
select * from `brackets` where `tier` < $income
From there I'd... well, you linked to some sample code in another language yourself. You should really do it that way.
If you insist otherwise, SQL can do it:
DELIMITER $$
create procedure `calcTax`(IN v_param1 int)
begin
declare v_tier int;
declare v_rate decimal(3,3);
declare v_untaxed int default 0;
declare v_taxTotal float default 0;
set v_untaxed = v_param1;
set v_taxTotal = 0;
while v_untaxed > 0 do
select max(`tier`), max(`rate`) into v_tier, v_rate
from `brackets` where tier < v_untaxed order by `tier` desc limit 1;
set v_taxTotal = v_taxTotal + (v_untaxed - v_tier) * v_rate;
set v_untaxed = v_tier;
end while;
select v_taxTotal;
end;
$$
Correct datatypes are up to you. You'll need a tax brackets table that works out. I made one for testing below (with poorly advised data types).
create table brackets (tier int primary key, rate float);
insert into brackets values (0, 0), (10000, 0.1), (50000, 0.2);
You could create a MYSQL user defined function.
http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html