I'm utilizing PHP 5.5 and connecting to a MS SQL database. Connection works as expected. I'm attempting to do a mass insert (around 2000 rows). Rather than using PHP to run 2000 INSERT statements, I'm sending a query like so:
DECLARE #count INT
SET #count = 0
WHILE(#count < 2000)
BEGIN
INSERT INTO Table (Field1, Field2)
VALUES ('data1', 'data2')
SET #count = (#count + 1)
END
When I plug this query directly into SQL Server Mangement Studio, it inserts 2000 rows as expected. When I run the query via PHP like so:
$connectInfo = array ("UID"=>'user',"PWD"=>'#pass',"Database"=>"database", "ReturnDatesAsStrings"=>true);
$link = sqlsrv_connect("dataserver",$connectInfo);
$query = "DECLARE #count INT
SET #count = 0
WHILE(#count < 2000)
BEGIN
INSERT INTO License (Field1, Field2)
VALUES('data1','data2')
SET #count = (#count + 1)
END";
if(!$foo = sqlsrv_query($link,$query)) {
die(print_r(sqlsrv_errors(), true));
}else{
die("hooray beer time!");
}
This only inserts 524 rows in the table, not the expected 2000 (and some times it's 309 rows, X rows, its erratic). It doesn't error out, and I get "hooray beer time!" after every script run. By default, sqlsrv_query() function has no time limits, I thought. Has anyone run into anything like this before? It has me baffled.
Ahh, it seems that after each insert in the While loop, SQL returns a little row of Text that says (1) row affected (or something to that tune). Turning this off via:
SET NOCOUNT ON
has remedied this issue. Thanks all!
Related
I have spent hours trying to get to the bottom of this and it is driving me la la.
I have a simple stored procedure that just inputs 10,000 rows into a table. This is done because it takes long enough to prove the point (about 12 seconds).
create table lala (id int not null identity(1,1), txt varchar(100))
go
CREATE PROCEDURE lalatest
AS
BEGIN
DECLARE #x int;
SET #x = 10000;
WHILE #x > 0
BEGIN
INSERT INTO lala (txt) VALUES ('blah ' + CAST(#x AS varchar(10)));
SET #x = #x - 1;
END;
END;
When I run the stored procedure from within SQL Server Management Studio, 10,000 rows are inserted into the table.
When I run it from php it just aborts after about 150 rows (the number of rows varies, suggesting a timeout issue) with no error messages or any indication that it has finished early
The remote query timeout setting is set to the default of 600 seconds, so its not that.
The php:
$sql = "exec lalatest";
sqlsrv_query($cn, $sql);
I have tried specifying a timeout value (which should be indefinite by default anyway) on the sqlsrv_query line and it made no difference.
I'm using php 5.6.7
and Microsoft SQL Server 2012 (SP3) (KB3072779) - 11.0.6020.0 (X64)
Oct 20 2015 15:36:27
Standard Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor)
I have all errors and warnings turned on
Anyone got any ideas as to what I need to do to make this work?
Would you believe it, I have made it work. I added SET NOCOUNT ON as the first line in the stored procedure and now it works from php as well.
Looks like having the rows affected info throws the php sqlsrv driver a wobbly. Strange how it only does it after a certain time (about a second), perhaps something to do with when the message buffer is flushed or something. Yes, I'm guessing, but this has solved the problem, so I'm happy.
CREATE PROCEDURE lalatest
AS
BEGIN
SET NOCOUNT ON; -- must be added so that it will run from php
DECLARE #x int;
SET #x = 10000;
WHILE #x > 0
BEGIN
INSERT INTO lala (txt) VALUES ('blah ' + CAST(#x AS varchar(10)));
SET #x = #x - 1;
END;
END;
I have the following stored procedure:
CREATE PROCEDURE `Get_Events_And_Deadlines_By_User`(IN `intervalStart` INT, IN `intervalEnd` INT)
BEGIN
IF (intervalStart != 0 AND intervalEnd != 0) THEN
SELECT 1 AS `test`;
ELSEIF (intervalStart != 0 AND intervalEnd = 0) THEN
BEGIN
SELECT 2 AS `test`;
IF ((SELECT FOUND_ROWS())=1) THEN
SELECT 3 AS `test`;
END IF;
END;
ELSE
SELECT 4 AS `test`;
END IF;
END
When I run call Get_Events_And_Deadlines_By_User(1,0) I only get the select query with 2 as the result, the select inside the if statement is never returned. It seems like only the first select query that is encountered is being executed before the stored procedure is returned. Why is this? What can I do to fix this? I want the select query INSIDE the if statement to be the ONLY result when the if holds.
Your procedure returns multiple resulsets.
PDO:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly', array(
PDO::ATTR_EMULATE_PREPARES=>false,
PDO::MYSQL_ATTR_DIRECT_QUERY=>false,
PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION
));
$result = $pdo->query('Call Get_Events_And_Deadlines_By_User(1,0)');
do {
foreach( $result as $row ) {
echo join(', ', $row), "\r\n";
}
echo "-----\r\n";
} while($result->nextRowset());
My test:
mysql> DELIMITER $$
mysql> DROP PROCEDURE IF EXISTS `Get_Events_And_Deadlines_By_User`$$
Query OK, 0 rows affected, 1 warning (0,00 sec)
mysql> CREATE PROCEDURE `Get_Events_And_Deadlines_By_User`(
-> IN `intervalStart` INT,
-> IN `intervalEnd` INT
-> )
-> BEGIN
-> IF (intervalStart != 0 AND intervalEnd != 0) THEN
-> SELECT 1 AS `test`;
-> ELSEIF (intervalStart != 0 AND intervalEnd = 0) THEN
-> SELECT 2 AS `test`;
-> IF ((SELECT FOUND_ROWS())=1) THEN
-> SELECT 3 AS `test`;
-> END IF;
-> ELSE
-> SELECT 4 AS `test`;
-> END IF;
-> END$$
Query OK, 0 rows affected (0,00 sec)
mysql> DELIMITER ;
mysql> CALL `Get_Events_And_Deadlines_By_User`(1, 0);
+------+
| test |
+------+
| 2 |
+------+
1 row in set (0,00 sec)
+------+
| test |
+------+
| 3 |
+------+
1 row in set (0,00 sec)
Query OK, 0 rows affected (0,00 sec)
After: Retrieving Multiple Result sets with stored procedure in php/mysqli.
Q: I only get the select query with 2 as the result, the select inside the if statement is never returned. It seems like only the first select query that is encountered is being executed before the stored procedure is returned. Why is this?
A: That second resultset is being returned by the procedure. The client is responsible for requesting the second (and subsequent) resultsets. (In PHP, that would require calling the mysqli mysqli_next_result, or the PDO nextRowset function, depending on which interface library you are using.
But that doesn't seem to be your real question.
Q: What can I do to fix this?
A: That really depends on what behavior you want to achieve. It's possible for a procedure to return multiple resultsets, and for the client to process them.
Q: I want the select query INSIDE the if statement to be the ONLY result when the if holds.
A: You want to a run a query (the SELECT 2 query in the example procedure) but you don't want the procedure to return that as a resultset. You just want to know how many rows that query returns, and to conditionally control the flow in your stored program based on how many rows are returned.
There are several constructs you can use to achieve that. It is possible to run a query within a procedure without having the result from that query returned as a resultset.
test for "more than zero" rows with SELECT EXISTS (subquery)
I suspect you don't really want to use the FOUND_ROWS function; and I suspect that you don't want to test whether the number of rows found is exactly equal to 1.
If what you are attempting to achieve is determining whether a particular query will return one or more rows, you could use a pattern like this:
IF ( SELECT EXISTS ( SELECT 2 AS `test` ) ) THEN
SELECT 2 AS `test`;
ELSE
SELECT 3 AS `test`;
END IF;
Given that the queries in the example code are guaranteed to return exactly one row, I'm just guessing at what you are trying to achieve.
If you have query, and you want to see if that query returns a row, you can wrap it in an EXISTS() condition, which will return a boolean.
EXISTS(subquery) returns a value of 1 if the subquery returns at least one row; and returns 0 if the subquery doesn't return a row.
That construct can be used within an IF for controlling the logic flow within a stored program.
IF ( SELECT EXISTS(subquery) ) THEN
-- subquery returned at least one row, so do something
ELSE
-- subquery didn't return any rows, so do something else
END IF;
get exact count of rows with SELECT COUNT(1) INTO FROM (subquery) q
If testing the "existence" of rows from a subquery isn't sufficient; if there's a reason you need to get an exact number of rows returned by a subquery, you could use a COUNT() aggregate. For example:
SELECT COUNT(1) FROM (subquery) q
To avoid returning the result from that query as a resultset from the procedure, you can assign the value returned by that into procedure variable or a user-defined variable. Assuming you've declared a procedure variable at the top of the procedure, something like this:
DECLARE myrowcount INT;
You could do something like this:
SELECT COUNT(1) INTO myrowcount FROM (subquery) q
IF ( myrowcount = 1 ) THEN
-- subquery returned exactly one row
ELSE
-- subquery either returned zero rows, or returned more than one row
END IF;
A user-defined variable could be used in place of a procedure variable. My preference is to use a procedure variable, if there's no need to persist that count of rows beyond the execution of the procedure.
(The biggest downside to using a user-defined variable is that it introduces unnecessary ambiguity. Someone who is later reading the code is left wondering whether the value stored in the user-defined variable is actually needed after the procedure ends. They don't know whether that's a purposeful, intentional side effect of the procedure, that something else is dependent on. To my mind, avoiding that ambiguity is sufficient reason to use a procedure variable.)
Based on what you mentioned in comments so far, I think you are looking for something below. All I did was to add a new parameter as an output to your procedure and instead of using SELECT to fill the value of test, I have used a variable sending from outside to your function and receive the updated value from outside and use SELECT #test to get your value and it will be only one value at a time not two.
If you have questions regarding the comment I left in procedure, I suggest you read more about FOUND_ROWS() usage.
DELIMITER $$
DROP PROCEDURE IF EXISTS `Get_Events_And_Deadlines_By_User` $$
CREATE PROCEDURE `Get_Events_And_Deadlines_By_User`(
IN `intervalStart` INT,
IN `intervalEnd` INT,
OUT `test` INT
)
BEGIN
IF (intervalStart != 0 AND intervalEnd != 0) THEN
SET test = 1;
ELSEIF (intervalStart != 0 AND intervalEnd = 0) THEN
BEGIN
SET test = 2;
# include your select query here,
# as you need if for FOUND_ROWS(), or
# it will always return 1 cause there is no successful
# SELECT query before it and it will be always 1.
SELECT * FROM someTable;
IF ((SELECT FOUND_ROWS())=1) THEN
SET test = 3;
END IF;
END;
ELSE
SET test = 4;
END IF;
END$$
DELIMITER ;
CALL Get_Events_And_Deadlines_By_User(1,0,#test);
SELECT #test;
I hope it helps.
I'm new. Most of the times I find solutions here just by reading but now I have to ask you directly because of the weirdness of my subject.
I have an application in php and I used to use the mssql libraries to connect to MS Server 2008 but now I migrated part of my code to connect through ADODB http://adodb.sourceforge.net/
I have a store procedure which I use to validate/insert/update/delete rows depending of parameters I send, so at the very bottom I have a line of code like this
Select #Result Result
This variable just tell me everytime if the proccess went correctly or if I'm missing something so the row doesn't get inserted/deleted/updated.
Here the code of my store procedure
create procedure sp_MyTable #id int, #name varchar(100), #type varchar(10)
as
declare #Results varchar(100)
set #Result=#type
--validations!
if exists(select * from MyTable where name=#name)begin
set #Result='No insert:('
end
if #Result='insert'
insert into MyTable (name)values(#name)
select #Result Result
Here and example of code to create my connection in php
$pQry='exec sp_MyTable #id="0",#name="Hello",#type="insert"';
require ("php/ExtClases/adodb5/adodb.inc.php");
$db = ADONewConnection('mssqlnative');
$db->debug = false;
$db->Connect($datCon['sServer'], $datCon['UID'], $datCon['PWD'], $datCon['Database']);
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
$rs = $db->Execute($pQry);
$rows=$rs->GetRows();
So, it is supposed to return and Array like this
print_r($rows);
//Array ([0]=>Array( [Result] => insert )) )
But It prints just nothing.
Using Management Studio I already ran this procedure directly on my own computer and at the server online and both cases retrieved data, so the store procedure is fine.
If I remove the insert statement:
create procedure sp_MyTable #id int, #name varchar(100), #type varchar(10)
as
declare #Results varchar(100)
set #Result=#type
--validations!
if exists(select * from MyTable where name=#name)begin
set #Result='No insert:('
end
/*
No insert!
*/
select #Result Result
It works!.
print_r($rows);
//Array ([0]=>Array( [Result] => insert)) )
_UPDATE: even if I print something (print 'something') in the store procedure ADODB ignores the select statement, so the select must be totally alone.
I think I will consider searching for another way.
_.
PD: Sorry for my bad english.
Regards.
Well, it took a few hours of my appreciated life but at last now I know what is going on.
MSSQL driver use to ignore all but the last statement (as I remember...). So, if you have something like this
$query="select * from table1; select some, data from table2; select yadda yadda from table3";
$conn=mysql_connect($sServer,$UID,$PWD);
$baseDatos=mysql_select_db($Database,$conn);
$res=mysql_query($query);
While($row=mssql_fetch_array($res)){
print($row);
}
You will have printed the results only of table3.
( ACTUALLY Here it is written that you can have multiple recorset and navigate through them with mssql http://www.php.net/manual/es/function.mssql-next-result.php but at the moment I don't know why it takes just the last one as the first one. Maybe it doesn't recognize prints or inserts as recorsets at all)
ADODB library actually uses SQLSRV to handle connections and queries to any sql server (mssqlnative). SQLSRV is the official driver released for Microsoft so is slightly different. You need to use the function sqlsrv_next_result or it will be return just the first result whatever it is (print,insert,select,etc)
here the link
http://www.php.net/manual/en/function.sqlsrv-next-result.php
and another guy who had the same problem
SQLSRV and multiple selects in Stored Procedure
what I did is not pretty but with patience I think I can fix it in the future, for now I just need the last recorset (table3) and not the first, so ...
/*here my temporary fix*/
do {
$result = array();
while ($row = sqlsrv_fetch_array($this->_queryID)) {
$result[] = $row;
}
} while (!is_null(sqlsrv_next_result($this->_queryID)));
return $result;
/**/
I overwrite the variable $result with every step into the results :). Funny code.
Cheers
I got a pretty large DB-Table that I need to split into smaller tables for different reasons.
The handling happens via php close to this example:
// Note: It's an example and not working code - the actual function is much larger
function split_db()
{
$results = "
SELECT *
FROM big_table
";
foreach ( $results as $result )
{
// Here I split the big_tables contents and ...
$some_val = $result->SomeVal;
// ...
$another_val = $result->AnotherVal;
// ... here I insert the contents in the different tables
$sql = "
INSERT
INTO first_small_table
// ...
VALUES
// ...
";
}
}
Problem: The query inserts 255 rows, no matter if I'm in the local environment or on the test server.
Question: Why? What am I doing wrong or am I missing something? And how would I avoid this?
Info about MySQL-Client-Version:
Dev-Server: 5.0.32,
Local Dev-Env.: 5.1.41
I'm no MySQL-Hero, so any help and explanation is appreciated, as Google brought nothing meaningful (to me) up. Thanks!
I bet you have your primary key of unsigned tinyint type, that has limit of 255 for the maximum value.
So change it to just int
ALTER TABLE first_small_table MODIFY id INT;
I can't say why you're limited to 255 rows, but what I can say is that you can do a single query to add your rows from your big table into your small table with a INSERT INTO ... SELECT query :
INSERT INTO first_small_table (col1, col2, col3)
SELECT col1, col2, col3
FROM big_table;
If you don't need to use PHP, then by all mean don't use it. It's faster to only use SQL.
I have a stored procedure in MySQL that should update a column in a table. When I run
CALL recalculate_city_ids();
from a MySQL prompt, the correct number of rows are updated (a few hundred). When I run the command from PHP, only a single row is updated and I get no error.
Here's the PHP:
$con = mysqli_connect('localhost', 'user', 'pass', 'dbname' );
$result = $con->query( 'call recalculate_city_ids()' );
mysql_close($con);
And the SQL for the sproc:
DROP PROCEDURE IF EXISTS recalculate_city_ids;
DELIMITER $$
CREATE PROCEDURE recalculate_city_ids()
READS SQL DATA
BEGIN
DECLARE o_id INT DEFAULT 0;
DECLARE o_latitude FLOAT;
DECLARE o_longitude FLOAT;
DECLARE done INT DEFAULT 0;
DECLARE cur_users CURSOR FOR SELECT id, latitude, longitude FROM user WHERE latitude IS NOT NULL ORDER BY fname;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN cur_users;
users: LOOP
FETCH cur_users INTO o_id, o_latitude, o_longitude;
IF done=1 THEN
LEAVE users;
END IF;
SELECT #closest_city_distance:=fn_distance_cosine(o_latitude, o_longitude, latitude, longitude) AS distance, #closest_city_id:=id AS id FROM category WHERE zone="city" AND active=1 ORDER BY distance LIMIT 1;
UPDATE user SET city_id = IF(#closest_city_distance<=30, #closest_city_id, 0) WHERE id=o_id;
END LOOP users;
CLOSE cur_users;
END
$$
I can run other queries from PHP using mysqli (also tried the mysql object). I'm also unable to create stored procedures from PHP (no error) and had to do that part from a MySQL prompt as well.
PHP and my MySQL prompt are using the same username.
I think that PHP doesn't like it when a query returns multiple result sets. I don't really need to return anything since this is just a glorified UPDATE statement, so I changed my
SELECT #closest_city_distance:=fn_distance_cosine... query to
SELECT fn_distance_cosine(o_latitude, o_longitude, latitude, longitude) as distance,id into closest_city_distance, closest_city_id FROM category WHERE zone="city" AND active=1 ORDER BY distance LIMIT 1;
Since that was the only place a result set was getting returned, eliminating the returned sets fixed the problem.