I have a problem where it seems that when running the same stored procedure at the same time from two different instances of the same application it is timing out, and wondered if there was anything I could do to resolve it?
I believe the problem is in the way SQL Server 2008 handles itself, locking rows and executing the SPs...things I don't really know a whole lot about. The app uses ADODB.Command to execute the SP.
I have one VB6 exe (App.exe), running on the one server multiple times. This app calls a stored proc on the database which returns the next sequence number for that app. This sequence number field is unique for the instance of the application - there is 1 row in a table (tbl_SequenceNos) for each instance of the running application.
So for example, say we have running: App[a].exe and App[b].exe
tblSequenceNos looks like:
iAppNo| iNextSequenceNo
a | 1234
b | 4567
The stored procedure to get the next sequence number is relatively simple:
CREATE PROEDURE GetNextSequenceNo (#AppNo varchar(1), #NextSequenceNo int output)
AS
BEGIN
DECLARE #TempSequenceNo int
SELECT #NextSequenceNo = iNextSequenceNo
FROM tblSequenceNos
WHERE iAppNo = #AppNo
#NextSequenceNo = #NextSequenceNo + 1
UPDATE tblSequenceNos
SET iNextSequenceNo = #NextSequenceNo
WHERE iAppNo = #AppNo
END
When both App[a].exe and App[b].exe try to run this procedure to get their NextSequenceNo value, they are hanging for about 30Secs (ADO timeout?).
Because Each app never looks at the each others row, I thought that this would work concurrently without specifing an special Locking. Is there something I am missing? I thought perhaps I need to specify to lock the row only, not the whole table or Page? - I do not know what sql2008 does by default.
Any help is greatly appreciated!
Thank you in advance
Andrew
Your procedure is not thread safe and will produce incorrect results because between the select and the update multiple threads might get the same sequence nr.
CREATE PROCEDURE GetNextSequenceNo (#AppNo varchar(1), #NextSequenceNo int output)
AS
DECLARE #var table(seq int);
UPDATE tblSequenceNos
SET iNextSequenceNo = #NextSequenceNo + 1
OUTPUT inserted.iNextSequenceNo INTO #var;
WHERE iAppNo = #AppNo
select #NextSequenceNo = seq from #var
GO
Also make sure your iAppNo column is indexed. (This means an index on this column only or an index where this field is the first field in your index)
Related
I have a recursive mysql stored procedures for which I have set the max_sp_recursion_depth=10.
Now, without setting a local variable, i would like to know what recursion's level is during single execution.
I think that surely there is a session variable that stores the depth (how else would you know when you reach the maximum level) but I could not find it. I would avoid using a variable to do this incrementally. How could i know this (if any) system variable?
I know you specifically asked how to do this without a user-created variable - but for others coming across this thought it would be worth posting how to do it with one as it's fairly simple:
CREATE PROCEDURE sp_recursive
BEGIN
// ... DECLAREs here
-- Set maximum recursion depth (max is 255)
SET ##SESSION.max_sp_recursion_depth = 10;
-- Increment current recursion depth
SET #recursion_depth = IFNULL(#recursion_depth + 1, 1);
-- ... More stored procedure code
-- Decrement current recursion depth. Note: Care must be taken to ensure this line
-- is *always* executed at the end of the stored procedure.
SET #recursion_depth = #recursion_depth - 1;
END
Explanation
The #recursion_depth session-scoped variable is incremented by the above SET statement each time the stored procedure is entered. The first time it is entered, the variable is uninitialized and so has a value of NULL - this is checked for by the IFNULL(), which reassigns it to one in this case. At the end of the stored procedure just before exiting, the depth needs to be decremented.
Further notes
Worth noting that SQL Server does provide an in-built ##NESTLEVEL variable for doing the above - but unfortunately MySQL doesn't seem to have an equivalent.
Our database has a function to generate an order number. It reads a value from a Settings table, increments it, then returns the new value. For example:
CREATE FUNCTION NextOrderNumber() RETURNS INTEGER UNSIGNED NOT DETERMINISTIC
BEGIN
DECLARE number INTEGER UNSIGNED;
UPDATE Settings SET IntegerValue=LAST_INSERT_ID(IntegerValue+1) WHERE KeyName='NextOrderNumber';
SET number=LAST_INSERT_ID();
return number;
END
Note: Don't critique this function I know it has flaws it's just for illustration.
We use this function as follows:
INSERT INTO Orders(OrderNumber, ...)
SELECT NextOrderNumber(), ...
When binary logging is enabled, CREATE FUNCTION gives this error:
This function has none of
DETERMINISTIC, NO SQL, or READS SQL
DATA in its declaration and binary
logging is enabled (you might want
to use the less safe
log_bin_trust_function_creators
variable)
Regardless of what binlog_format is set, is there really a problem with the above function? According to my reading of the relevant MySQL page I can't see any reason why this function would be incompatible with replication, with either ROW or STATEMENT level binary logging.
If the function is safe, setting the global log_bin_trust_function_creators=1 makes me uneasy. I don't want to disable this check for all functions, just this one. Could I instead just flag the function as NO SQL to suppress the warning? I tried it and it worked. Will this cause any problem?
I've googled and here I am.
I've found a way :
SET GLOBAL log_bin_trust_function_creators = 1;
But be careful, it may be unsafe for data recovery or replication...
As per my understating it cause problem when data recovery or replication
Ref: http://dev.mysql.com/doc/refman/5.0/en/stored-programs-logging.html
MySQL 5.0.6: Statements that create stored routines and CALL statements are logged. Stored function invocations are logged when they occur in statements that update data (because those statements are logged).
However, function invocations are not logged when they occur in statements such as SELECT that do not change data, even if a data change occurs within a function itself; this can cause problems.
Under some circumstances, functions and procedures can have different effects if executed at different times or on different (master and slave) machines, and thus can be unsafe for data recovery or replication.
E.g.
CREATE FUNCTION myfunc () RETURNS INT DETERMINISTIC
BEGIN
INSERT INTO t (i) VALUES(1);
RETURN 0;
END;
SELECT myfunc();
If a stored function is invoked within a statement such as SELECT that does not modify data, execution of the function is not written to the binary log, even if the function itself modifies data. This logging behavior has the potential to cause problems. Suppose that a function myfunc() is defined as above.
There are two ways to fix this:
Execute the following in the MySQL console:
SET GLOBAL log_bin_trust_function_creators = 1;
Add the following to the mysql.ini configuration file:
log_bin_trust_function_creators = 1
The setting relaxes the checking for non-deterministic functions. Non-deterministic functions are functions that modify data (i.e. have update, insert or delete statement(s)). For more info, see here.
Please note, if binary logging is NOT enabled, this setting does not apply.
Have a think about what's getting written to the binary log.
You can't ensure that an order created on a master would have the same sequence generated for it when the transaction is played on a slave - or, what would much more likely, by another master in the cluster. e.g.
0) Node 1 and Node 2 are in sync, NextOrderNumber=100
1) Node 1 receives insert statement wrt order from customer A and assigns
order number 100, changes its NextOrderNumber to 101
2) Node 1 writes the settings update to the log
3) Node 1 writes the insert statement to the log
4) Node 2 processes for customer B, asigns order number 100 and increments
5) Node 2 writes the settings update from to the log
6) Node 2 writes the insert statement to the log
7) Nodes 2 reads settings update from the log #2
- Its NextOrderNumber is now 102
8) Node 2 reads insert from log #3, tries to apply it but it fails
due to duplicate key
9) Node 1 reads the update #5 - Its nextOrderNumber is also now 102
10) Node1 reads insert from log #6 -
but this fails due to duplicate key
Now orders 100 on the 2 nodes refer to different data, and there is no order 101.
There is a reason that there has been a lot of functionality added to modify the behaviour of auto_increment variables.
If you wrap the insert in a procedure - which retrieves a value from the sequence generator then embeds it in the insert statement the immediate problem will be resolved, however you need to think about how you avoid assigning the same number twice using different database nodes.
Could I instead just flag the function as NO SQL to suppress the warning? I tried it and it worked. Will this cause any problem?
According to this Mysql doc:
Assessment of the nature of a function is based on the “honesty” of the creator: MySQL does not check that a function declared DETERMINISTIC is free of statements that produce nondeterministic results.
So it's up to you. If you are sure the method won't cause any problem...
Writing the attribute helped me. In this function, you need to write - MODIFIES SQL DATA - because the function uses UPDATE. If only SELECT is used in the function, then we would write READS SQL DATA. You can also write these two attributes if both data read and write operators are used in the function body.
CREATE FUNCTION NextOrderNumber()
RETURNS INTEGER
UNSIGNED
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN
DECLARE number INTEGER UNSIGNED;
UPDATE Settings SET IntegerValue=LAST_INSERT_ID(IntegerValue+1)
WHERE KeyName='NextOrderNumber';
SET number=LAST_INSERT_ID();
return number;
END
Execute this just before creating the function:
SET ##global.log_bin_trust_function_creators = 1;
And add MODIFIES SQL DATA to the declaration.
Also... well, you asked not to comment the function itself, but I suggest that you drop the number variable and simply do RETURN LAST_INSERT_ID().
add READS SQL DATA which declare that is a read only function :
CREATE FUNCTION NextOrderNumber() RETURNS INTEGER UNSIGNED NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE number INTEGER UNSIGNED;
UPDATE Settings SET IntegerValue=LAST_INSERT_ID(IntegerValue+1) WHERE KeyName='NextOrderNumber';
SET number=LAST_INSERT_ID();
return number;
END
We're using SqlServer 2008. In SSMS, queries on the full text catalog might take 2-5 seconds the first time, but after that, return quite quickly.
On the other hand, running a query from via Linq2Sql will timeout.
Here's what we have:
The SQL Inline Table UDF
CREATE FUNCTION dbo.SearchArchiveFTS
(
#query nvarchar(255)
)
RETURNS #ret TABLE
(
ID NVarChar(12) NOT NULL,
snapshotDate DateTime NOT NULL,
-- about 10 more
)
AS BEGIN
declare #innerQuery nvarchar(255)
set #innerQuery = #query
insert into #ret
select ID,
snapshotDate,
-- about 10 more
from dbo.Archive a
where contains(a.*, #innerQuery)
return
Query in SSMS
select * from dbo.SearchArchiveFTS('query')
//3 seconds / 3k rows
Query in Linq2Sql
db.SearchArchiveFTS("query").ToArray();
// timeout exception
Any ideas on what the problem might be?
Check that your connection is not coming in with arithabort off. In SSMS it is ON
you can easily check like this
select arithabort,*
from sys.dm_exec_sessions
where is_user_process =1
just find the SPID that is hitting the DB
You also try to see what happens when you do this in SSMS
SET ARITHABORT OFF
select * from dbo.SearchArchiveFTS('query')
Does it now take a lot longer?
It is also possible that you are getting a bad plan from LINQ
You can clean out the procedure cache and memory buffers by running the following command
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
Just be aware that it will wipe out all the plans on the server and SQL Server will have to recreate all of those and also read in all the data from disk again.......
I agree with #SQLMenace, when somehthing runs fast in SSMS but not from the application, it is usually a connection difference.
However, why use a function for something like that?
if you must use a function, why not use a table value function like this:
CREATE FUNCTION dbo.SearchArchiveFTS
(
#query nvarchar(255)
)
RETURNS TABLE
AS RETURN
(
select ID,
snapshotDate,
-- about 10 more
from dbo.Archive a
where contains(a.*, #query)
);
The issue appears to be related to a feature of SQL Server, where the FTS indices are unloaded after a period of inactivity. A background job to keep them fresh solved the problem.
Here's my configuration:
I have a re-runnable batch script that I use to update my database.
Inside of that batch script, I have code that says the following:
If Table 'A' doesn't exist, then create Table 'A' and insert rows into it.
Later on in that batch script, I create an schemabound indexed view on that table.
And if you didn't already know, indexed views require specific client settings.
Sometimes, when I re-run the script, that is after the table has been created, SQL Server Management Studio evaluates the "insert rows" code, which is protected by the 'If this table doesn't exist' code, and yields the following error:
Msg 1934, Level 16, State 1, Line 15
INSERT failed because the following SET options have incorrect settings: 'CONCAT_NULL_YIELDS_NULL, ANSI_WARNINGS, ANSI_PADDING, ARITHABORT'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
Please note: If someone were to try this INSERT statement in a vacuum, I would fully expect SSMS to generate this error.
But not when it's protected by a conditional block.
My Question:
Does the SSMS compiler evaluate all expressions, regardless of whether they will actually be executed?
Yes, it evaluates all of them,take a look at this
declare #i int
select #i =1
if #i = 1
begin
declare #i2 int
set #i2 = 5
end
else
begin
declare #i2 int
set #i2 = 5
end
Msg 134, Level 15, State 1, Line 12
The variable name '#i2' has already been declared. Variable names must be unique within a query batch or stored procedure.
Another example with temp tables is here: What is deferred name resolution and why do you need to care?
your only way out would be to wrap it inside dynamic SQL
Note that most of the settings you mention are connection-level, i.e. in case you set/change them they stay in effect unless you close the connection or explicitly change their value.
Returning to your question. The error you mention looks like runtime error, i.e. the INSERT is actually being executed. It would be better if you could show your script (omitting details, but keeping batches).
Edit: it is not SSMS compiler that evaluates SQL you try to execute - it is SQL Server. What do you meant by 'evaluate'? Is it 'execute'? When you run a batch (which is what actually is being executed by a server), SQL Server first does syntactic analysis and throws error in case it finds any syntactic error, nothing is being executed at this point of time. In case syntax is ok, the server starts executing you batch.
Again, the error you show seems to be runtime - so I guess you'd carefully watch for the conditions and track what happens (or provide us more details about 'sometimes').
I'm having a rather strange problem with MySQL. Trying to create a procedure to update some fields in the database (the code is below).
The problem is with the line that is currently commented. It seems that if no SELECT statements get executed during the procedure MySQL query browser will return an error code of "-1, error executing SQL query".
I tried the same thing in HeidiSQL and the error was "cannot return result set". So I suppose the question is do I always have to select something in the procedure, or is there some other thing I missed.
The query works fine when the comment is removed.
DELIMITER /
DROP PROCEDURE IF EXISTS updateFavourites /
CREATE PROCEDURE updateFavourites(quota INT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE artist_id,releases INT;
DECLARE c_artist Cursor FOR
SELECT Artist.id_number,COUNT(Artist.id_number) FROM Artist
JOIN CD ON CD.is_fronted_by = Artist.id_number
GROUP BY Artist.id_number;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET done=1;
IF quota > 0 THEN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
-- SELECT 'Great success';
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;
END /
DELIMITER ;
Here's the sql to create the tables and some data:
DROP TABLE IF EXISTS CD;
DROP TABLE IF EXISTS Artist;
CREATE TABLE Artist (
id_number INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
);
CREATE TABLE CD (
catalog_no INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
is_fronted_by INT UNSIGNED,
rating ENUM ('favourite','top draw','good','so-so','poor','rubbish'),
CONSTRAINT fk_CD_Artist FOREIGN KEY (is_fronted_by) REFERENCES Artist(id_number) ON UPDATE CASCADE
);
INSERT INTO Artist VALUES(11,'Artist 1');
INSERT INTO Artist VALUES(10,'Artist 2');
INSERT INTO CD VALUES (7,11, 'top draw');
INSERT INTO CD VALUES (650,11,'good');
INSERT INTO CD VALUES (651,11,'good');
INSERT INTO CD VALUES (11,10,'favourite');
Query Browser is not for running scripts, just single query.
I tried your code by moving cursor into each query (except DELIMITER) and pressing Ctrl+Enter.
It created that stored procedure without problem. (just refresh schema on the left).
If you wish creating procedure, use menu "Script"->"Create stored procedure/function".
But better forget about QueryBrowser it is not supported at all (and actunally not useful).
If you have decent hardware and plenty resources, try Workbench 5.2 otherwise use SQLyog
Googling around, there are several reports of the same error, but little information to solve the problem. There's even a bug logged at mysql.com but it appears to have been abandoned without being resolved.
There's another StackOverflow question on the same error, but it's also unresolved.
All it means is that there is no result set from the query. Looking at the source code, it appears that sometimes an error status of MYX_SQL_ERROR is set when the query has no result set. Perhaps this is not an appropriate consequence?
I notice that when I use the mysql command-line client, it yields no error for calling a proc that returns no result set.
update: I tried to revive that MySQL bug report, and provide a good test case for them. They changed the bug from "no feedback" to "verified" -- so at least they acknowledge it's a bug in Query Browser:
[11 Dec 9:18] Sveta Smirnova
Bill,
thank you for the feedback. Verified
as described.
Although most likely this only be
fixed when MySQL Query Browser
functionality is part of MySQL
workbench.
I guess the workaround is to ignore the -1 error, or to test your stored procedures in the command-line mysql client, where the error does not occur.
The comment supposes the issue will disappear as the Query Browser functionality becomes part of MySQL Workbench. This is supposed to happen in MySQL Workbench 5.2. I'll download this beta and give it a try.
MySQL Workbench 5.2 is in Beta, but I would assume MySQL engineering can't predict when the Beta will become GA. Those kinds of predictions are hard enough under standard conditions, but there's a lot of extra uncertainty of MySQL's fate due to the unresolved Oracle acquisition.
update: Okay, I have tried MySQL Workbench 5.2.10 beta. I executed a stored procedure like this:
CREATE PROCEDURE FooProc(doquery SMALLINT)
BEGIN
IF doquery THEN
SELECT * FROM Foo;
END IF;
END
When I CALL FooProc(0) the response is no result set, and the status is simply "OK".
When I CALL FooProc(1) the response is the result of SELECT * FROM Foo as expected.
However, there's another bug related to calling procedures. Procedures may have multiple result sets, so it's hard to know when to close the statement when you execute a CALL query. The consequence is that MySQL Workbench 5.2 doesn't close the statement, and if you try to do another query (either CALL or SELECT) it gives you an error:
Commands out of sync; you can't run this command now.
MySQL doesn't support multiple concurrent open queries. So the last one must be closed before you can start a new one. But it isn't closing the CALL query. This bug is also logged at the MySQL site.
The bug about commands out of sync has been resolved. They say it's fixed in MySQL Workbench 5.2.11.
Try putting BEGIN and END blocks around the multiple statements in the IF block as such:
IF quota > 0 THEN
BEGIN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
END;
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;