MySQL- Stored Procs- Weirdness when trying to use an output parameter - mysql

To be honest, I'm feeling pretty stupid right now. But this simply isn't working.
Scenario
I have a stored procedure that includes an output parameter. I'm trying to SELECT a value INTO that parameter. This seems simple, but it continues giving me faulty results. I've checked many online sources, and I'm certain that I'm trying to do it properly.
Code
DELIMITER //
CREATE PROCEDURE `spGetId`(
IN ParamA VARCHAR(32),
OUT OutputId INT
)
BEGIN
SELECT `id` INTO OutputId
FROM `Table`
WHERE `column_a` = ParamA;
END//
CALL spGetId('foobar', #Bloop)//
SELECT #Bloop//
Results
I have two rows in this table, their IDs being '1' and '2'. The result I get back is '31', whether the SELECT statement matches anything or not.
I have tried many variations, including removing the WHERE clause entirely and having the SELECT return a COUNT(1) into the parameter (which gives me a result of '32', despite there being only 2 rows), and I have tried "declaring" the #Bloop variable before using it in the sproc call by using SET #Bloop = 0.
If you have any insight on why this is happening, and what I can do to make it return the proper value, I would be much obliged. Also, if you can show me how to achieve the same desired result using a Stored Function instead, with a return value, I'd appreciate that even more! My desired approach is using a stored function, but I had similar problems with that, then gave up and tried using a stored proc, only to find I was getting similar results.
Anything you can offer would be helpful!
Edit:
CREATE TABLE `Table` (
`id` int(11) NOT NULL auto_increment,
`column_a` varchar(32) character set utf8 NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
mysql> SELECT * FROM Table;
+------+----------+
| id | column_a |
+------+----------+
| 1 | asdf |
| 2 | foobar |
+------+----------+
When I call spGetId() with any argument, it returns the value '31' (even if the argument is 'foobar', which should return an integer value of '2' (or ascii 0x32)). If I modify spGetId() to return the total rowcount of Table, instead of returning '2', it returns '32'.

Your stored proc is working. I think it is returning the ascii value of the character '1' instead of the integer value 1.

I need to learn to vary my testing environments.
I'm still not sure exactly what the problem was, but it looks like phpMyAdmin was performing some kind of type conversion of its own, and I had been running all my tests through that particular client.
Throwing together a quick PHP script of my own and manually calling the sproc (and in further testing, calling a stored function as well) provided the desired results.
So, lesson learned: don't ever trust the client. Got to remember to switch it up a bit.

Related

MySQL bug in the DELETE clause

Has anyone experienced a situation like this?
I accidentally typed the DELETE command in the wrong syntax (MySQL version 8.0.22). The command should never have worked, but it not only worked, it also deleted all data from the table:
Syntax: DELETE FROM test WHERE 123456;
Note that neither the column name nor the conditional operator was specified, but even so the command was executed without errors and deleted all data from the table.
The code 123456 is an example but can be any code.
Test it on any version of MySQL:
CREATE TABLE `test` (
`cod` int NOT NULL AUTO_INCREMENT,
`name` varchar(50),
PRIMARY KEY (`cod`),
KEY `ix_tmp_autoinc` (`cod`)
);
INSERT INTO `test`
(`name`)
VALUES
('MySQL bug');
INSERT INTO `test`
(`name`)
VALUES
('MySQL bug 2');
DELETE FROM test WHERE 123456;
SELECT COUNT(*) FROM test;
This is a perfectly valid statement, absolutely in accordance with the syntax for the DELETE command described at DELETE statement. On the DELETE statement page it says that WHERE must be followed by a where_condition, which is described on the SELECT statement page. There we find that a where_condition can be either a Function and Operator, or it can be an Expression. Looking at the Expression page we find the following hierarchy:
expr
|
boolean_primary
|
predicate
|
bit_expr
|
simple_expr
|
literal
So a where_condition can be a literal, which is exactly what you gave it. It may not have been what you meant, and it may not have done what you intended, but from the standpoint of MySQL syntax it's perfectly legal.
What is playing out here are MySQL complex casting rules. When you ran the following query:
DELETE FROM test WHERE 123456;
MySQL expected a boolean expression following WHERE. It didn't find that, but it did instead find an integer literal. It turns out that MySQL will treat an integer literal as being "truthy," and so it will evaluate to true.

MySQL Fetch Not Returning Values

I'm attempting to use MySQL cursors for the first time and hitting a wall. Below code snippet should be returning actual (non-midnight) time values for tStart and tEnd, however all I'm getting is '00:00:00'. StartTime and EndTime fields in table are type TIME. Running just the select statement returns the expected values. Num_rows is indicating 1 row returned, which is also expected. Can anyone help with what I'm doing wrong?
DECLARE tStart TIME;
DECLARE tEnd TIME;
DECLARE num_rows INT DEFAULT 0;
DECLARE curs_1 CURSOR FOR SELECT MIN(asd.StartTime), MAX(asd.EndTime) FROM database.table1 asd WHERE (((asd.varid)='1006') AND ((asd.neededdate)='2016-06-22') AND ((asd.neededint)=3));
OPEN curs_1;
SELECT FOUND_ROWS() INTO num_rows;
FETCH curs_1 INTO tStart, tEnd;
Below code can generate test data:
CREATE TABLE `table1` (`StartTime` TIME DEFAULT NULL,`EndTime` TIME DEFAULT NULL, `varid` VARCHAR(10) DEFAULT NULL, `neededdate` DATE DEFAULT NULL, `neededint` INT(11) DEFAULT NULL) ENGINE=INNODB;
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('09:00:00','18:00:00','1006','2016-06-22',3);
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('09:00:00','18:00:00','1007','2016-06-21',3);
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('09:00:00','18:00:00','1008','2016-06-20',3);
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('08:00:00','17:00:00','1006','2016-06-21',2);
INSERT INTO `table1` (`StartTime`,`EndTime`,`varid`,`neededdate`,`neededint`) VALUES ('11:00:00','20:00:00','1006','2016-06-22',1);
Your cursor is indeed housing a single row.
You are using FOUND_ROWS() which can work fine without the use of SQL_CALC_FOUND_ROWS but typically uses it. But in your case you will only ever get back 1 row due to your aggregate (sum). So it almost makes no sense. There is nothing to "cursor" over like a tricky set of data.
I see no sense in using a CURSOR. In fact I recommend never ever using a cursor. Performance is awful for a highly tuned SQL Server that has relations and indexes and performance strategies to deliver results. Cursors as like throwing low-performance mechanisms into an otherwise deliberate stream-lines robust engine. Like opening the carburetor on a Corvette and throwing in a handful of sand. They are something new developers to SQL think of as they procedurally try to wrap their heads around their problem. I recommend you never use it.
The following is the Manual Page for Cursors. You need a LOOP construct to use them because you are row by row on your own terms moving through the data in that cursor. It takes quite a bit of patience to ever get them right. Try to use it sparingly, like maybe 1 time per app at most and with the trickiest of code until you solve it otherwise. Performance is really dreadful, trust me.
What you could do is simply the following to get those values into variables.
SELECT MIN(asd.StartTime) , MAX(asd.EndTime) into #myMin,#myMax
FROM table1 asd
WHERE (((asd.varid)='1006') AND ((asd.neededdate)='2016-06-22') AND ((asd.neededint)=3));
select #myMin,#myMax; -- display them if you want
Good luck, and did I mention yet not to use Cursors? :p

Can I specify a query as a field's default value?

I mean something like:
create table Measures (
id_user int,
date timestamp,
measure_1 double default 'select measure_1 from Measures where data = '**/**/****'',
measure_2 double default 'select measure_1 from Measures where data = '**/**/****'');
In this way I insert the value of the last measure saved in the db..
Is it possible?
Not directly:
11.7 Data Type Default Values
... the default value must be a constant; it cannot be a function or an expression.
You'll have to do this on application level, or in a trigger as suggested by #Timekiller.
You can do that via a before-insert trigger.
Check if NEW.measure_1 is null, and if it is, then perform select and store results.
UPD:
Right, I was in a bit of a hurry yesterday, and forgot to give an example later. Trigger is a good replacement for complex default value - it will work transparently, will look just like the default value from database user standpoint, and you won't have to do anything on the application level, since triggers are stored in the database itself. It will look something like this:
CREATE TRIGGER `measures_bi_trigger` BEFORE INSERT ON `Measures`
FOR EACH ROW BEGIN
if NEW.measure_1 is null then
SET NEW.measure_1 = (select measure_1 from Measures where ... limit 1);
end if;
if NEW.measure_2 is null then
SET NEW.measure_2 = (select measure_2 from Measures where ... limit 1);
end if;
END
It's not exactly clear what should be in your where condition, so you'll have to substitute ... yourself. Note that your query should return exactly one row, so either use an aggregate function like MAX or order by ... limit 1. If your query returns no rows, NULL will be inserted.

Make Laravel use MySQL default value on insert/update

I've noticed that, in Laravel, (when using $model->fill(Input::all()), not that it matters how the data comes in), empty fields (empty in a form) come through as an empty string (''). That makes sense, as that's how it's delivered from browser to HTTP server to PHP.
The problem is that if the column is numeric and has a DEFAULT value, or is NULLable, the generated query from Eloquent has '' for the column's value and so MySQL interprets that and enters the value as 0 rather than the default column value or NULL. Is it something I'm doing wrong here, or will I need to put extra work in (e.g. a mutator) to detect this empty string and convert to null to achieve what I actually want?
Of course I understand that from a technical point of view, Laravel, without knowing how your columns work, can't just assume that empty string means pass NULL to the INSERT query, because sometimes you actually want to set a field (specifically a character-based one) to an empty string rather than NULL.
That said, I'd rather not have to define mutators for all my models just because I'm using $model->fill(), but is there anything I don't know about that I can do?
For the MySQL people reading this - is it correct behaviour to set a numeric field to 0 if passed ''? Seems like it should be seen as NULL as it's not explicitly 0, but I guess it's maybe weak typing equating '' to 0 rather than the more distant NULL.
There is a very simple way to do this, and that is by using an array_filter.
$input = array_filter(Input::all(), 'strlen');
$model->fill($input);
The array_filter will return all of the keys that have something assigned to them.
There are some caveats with this solution:
strlen has been used, and not empty. This is because empty will cause other items (such as the number 0) to also be unset.
this means that edits that are made with an update, such as a text box being completely emptied, will not be fulfilled by your application, so use wisely!
EDIT: As for the MySQL question, yes, this is normal.
mysql> SELECT CAST("tim?" AS SIGNED);
+------------------------+
| CAST("tim?" AS SIGNED) |
+------------------------+
| 0 |
+------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1292 | Truncated incorrect INTEGER value: 'tim?' |
+---------+------+-------------------------------------------+
For the default values to be inserted on some fields then do not set values for those fields explicitly.
When you receive form data, process each of the fields and decide for which of them you want to insert the default values. Then remove them from insert into ... statement.
Example:
Form fields: field1_1, field_2.
If valid input is given for both of them, then you can write sql statement like:
insert into my_table values( field_1_value, field_2_value );
If one of the fields, say field_2 does not have valid input and you still want the record go into the database table. Then write the sql statement like:
insert into my_table( field_1 ) values( field_1_value );
When you do this, SQL engine will use the default valued defined on the omitted fields in the insert statement.
Note: This only works when you have set default someValue on the columns at the time of creating or altering them in a database table.

Why does this table name require back-ticks?

I am seeing a curious name dependency in the following MySQL table definition. When I code the table as shown, it seems to break MySQL. When I perform "select * from dictionary_pair_join" from MySQLQueryBrowser, the status bar says "No resultset returned" -- no column names and no errors. When I insert a row into the table, the status bar says "1 row affected by the last command, no resultset returned", and subsequent selects give the same "No resultset returned" response.
When I enclose the tablename in backticks in the "select" statement, all works fine. Surely there are no mysql entities named "dictionary_pair_join"!
Here is the table definition:
DROP TABLE IF EXISTS dictionary_pair_join;
CREATE TABLE dictionary_pair_join (
version_id int(11) UNSIGNED NOT NULL default '0',
pair_id int(11) UNSIGNED default NULL,
KEY (version_id),
KEY (pair_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Here is the broken select statement:
select * from dictionary_pair_join;
Here is its working counterpart:
select * from `dictionary_pair_join`;
Why are backticks required in the select statement?
Update: This also fails in the Python mysqldb interface, which is why I started looking at it. I can put the backticks into my Python "select" generators, but I was hoping this was some stupid and easily-changed nit. I suppose I can also find a different name.
I've uprated the comments from Quassnoi and Software Guy, together they've persuaded me that it's just a bug in mysql/mysqldb/mysqlquerybrowser.
I changed the table name (to "dictionary_pair_cons") and the problem went away, in both the query browser and mysqldb.