I've added some simple triggers (after update) to my ms-sql server 2008 database which work perfect when I manually update recordsets in "Microsoft SQL Server Management Studio".
But when I update recordsets with my PHP script (PHP 7, PDO), the recordsets get updated but the triggers won't fire.
I've already spent hours with google but didn't find a solution...
Does anyone have an idea what the problem could be?
Regards
M Badabum
The trigger create script:
CREATE TRIGGER [dbo].[TRIGGERNAME] ON [dbo].[TABLENAME]
AFTER UPDATE
AS
IF ##rowcount = 1
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
IF ( not UPDATE (thenameofonefield) )
BEGIN
UPDATE f set last_update=GETDATE(), thenameofonefield=0
FROM
dbo.TABLENAME AS f
INNER JOIN inserted
AS i
ON f.id = i.id;
END
END
GO
Related
Actually I use PHP to insert some value into SQL server database table that contain one trigger that i have already created.
any error detected while the execution but I don't find the row.
But if I delete my trigger and I run the program again the row is inserted correctly.
Please help me.
Trigger code :
CREATE TRIGGER test6
ON dbo.test
AFTER INSERT
AS
BEGIN
Select * from dbo.tabletest
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
END
GO
My sys admin is upgrading my PHP server from 5.2 to 5.5 . As a result, the mssql family of functions is gone and I have to update my code (it seems to either the odbc functions or sqlsrv functions). Unfortunately, neither seems to be working correctly for anything beyond simple queries.
I've reduced one of the problematic queries down to the following two variants (middle line added is the only change):
IF OBJECT_ID('tempdb..#i') IS NOT NULL BEGIN DROP TABLE #i END
SELECT 'value' as test
IF OBJECT_ID('tempdb..#i') IS NOT NULL BEGIN DROP TABLE #i END
CREATE TABLE #i (id INT primary key) INSERT INTO #i SELECT 405782
SELECT 'value' as test
When I try them in SQL Server Mangement Studio, both work fine and return one row. When I try the first one from PHP, it works fine and returns one row. However, when I try to execute the second query from PHP I get an unexpected result:
$SQL_query= '********'; //Second query
$serverName = '**********';
$connectionInfo = array( "Database"=>"*****","UID"=>"********","PWD"=>"*********");
$conn = sqlsrv_connect( $serverName, $connectionInfo);
$msg1=sqlsrv_errors(SQLSRV_ERR_ALL); // ""
if($conn){ //truthy
$result=sqlsrv_query($conn,$SQL_query);
if(sqlsrv_has_rows ($result)){$rows='true';}else{$rows='false';} //false
$msg2=sqlsrv_errors(SQLSRV_ERR_ALL); // ""
$row=sqlsrv_fetch_array($result,SQLSRV_FETCH_NUMERIC) //false
}
(The odbc functions were even worse, choking if the query contained a SET #var statement...)
So the result of the query is incorrect, but no errors are reported.
Can anyone explain this? You'd think if the range of queries that could be handled by these functions was somehow limited that it would be at least mentioned in passing in the PHP documentation for these functions.
For reference: Microsoft SQL Server Standard Edition 9.00.1406.00, PHP 5.5.19 x86 thread safety disabled, running on Windows.
Edit: Per Rob Farley's suggestion, I've confirmed that the ##OPTIONS are either identical or immaterial to reproducing the problem.
Depending on what driver you're using (freetds?), you could be finding that ANSI_NULLS (and other ANSI settings, such as QUOTED_IDENTIFIER) are set differently to what is expected, and this can affect things such as the creation of tables. Try passing in a query that tests those values, and you will quite probably find the problem.
The problem is that the client framework is confused by the row counts that are returned by default for every statement. When the first INSERT happens, it returns a row count of 1, making it appear as if this is a statement returning results, which it's not -- the row count may even get confused for the result itself. PHP isn't the only technology troubled by this; jTDS is another driver that has trouble (in the case of jTDS, it requires the row count). SET NOCOUNT ON suppresses this extra information:
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#i') IS NOT NULL BEGIN DROP TABLE #i END
CREATE TABLE #i (id INT primary key)
INSERT INTO #i SELECT 405782
SELECT 'value' as test
My stored procedure executes 100% fine from the Management Studio, but when running through PDO with a try catch block I get the following exception message:
SQLSTATE[IMSSP]: The active result for the query contains no fields.
I have tried the classic SET NOCOUNT ON (this caught me out previously) to stop it returning row counts, and I've done various tests by removing sections of the SP until I have found which section the error lies in. I've also tried the PHP PDO nextRowset() with no luck.
The Stored Procedure:
I declare a cursor (shock, horror I know!) and iterate over some results, which itself caused no issues - but in reality this cursor must run various stored procedures itself for each fetch in the cursor, and when I introduce these stored procedures that is when the issues appear.
I have gone through the SPs inside the cursor and SET NOCOUNT ON on them in case that might be the issue, but no luck. One or two of these SPs have OUTPUTS but these are captured in variables accordingly.
Does anyone have any ideas? I don't wish to post any code of the project but some scenarios of commands I perform in the cursor block:
SELECT #varName = columnName FROM dbo.tableName
SET #varName = (SELECT columnName FROM dbo.tableName)
EXEC dbo.storedProcedure #outputVar OUTPUT
My best guess is the top example is the problem, but I am not knowledgeable to know. I would like to locate the error without removing these one by one as the actions performed by the procedure as a whole are difficult to roll-back on my test database and each line is important to getting correct output.
Thanks in advance for any help provided!
I have now managed to solve this! Hopefully the solution will help others who run into this error or similar.
The issue was that nested cursors (cursor inside cursor) were in use for the SQL Server stored procedure. My main procedure (called from PHP by PDO) opened a cursor and then ran other stored procedures inside that cursor that opened a cursor of their own.
This method works fine when running the query in SQL Server Management Studio, but calling from PHP via PDO fails.
While I know that using cursors is considered bad practice by most SQL buffs, unfortunately I inherited these stored procedures so I'm removing all blame from myself!
The actual solution was to replace the cursor in the originating stored procedure (the one called by PHP which in turn calls the other SPs) with a while loop using code like this:
DECLARE #loopTable table (id int IDENTITY(1,1), dataColumn)
DECLARE #id int
DECLARE #rows int
DECLARE #data int -- var to hold targeted data in the loop
INSERT INTO #loopTable (dataColumn)
SELECT dataColumn FROM dataTable
SELECT #rows = COUNT(1) FROM #loopTable
WHILE (#rows > 0)
BEGIN
SELECT TOP 1 #data = dataColumn, #id = id FROM #loopTable
// Do stuff with #data variable here
DELETE FROM #loopTable where id = #id
SELECT #rows = COUNT(1) from #loopTable
END
Problem solved, nightmare to find!
This is just the strangest thing and I'm not sure why or what is happening. Everything runs fine in SQL Server but results in errors when ran in PHP.
In PHP, I build a dynamic statement that contains one to many queries or statements. It looks like this:
begin try
begin transaction
-- statement 1
UPDATE table_name SET status = 1 WHERE things='stuff';
-- dynamic: to run after inserts
exec [dbo].[SP_TEST_2];
exec [dbo].[SP_TEST_3];
exec [dbo].[SP_TEST_9];
exec [dbo].[SP_TEST_14];
commit
select 'successful' as for_php_success_message
end try
begin catch
rollback
select error_number() as for_php_error_number
,error_severity() as for_php_error_severity
,error_state() as for_php_error_state
,error_procedure() as for_php_error_procedure
,error_line() as for_php_error_line
,error_message() as for_php_error_message;
end catch
This morning, someone came to me because the front page to all of this was throwing errors at them. Warning: mssql_query() [function.mssql-query]: message: The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION. This code has been untouched for several months and hasn't had any problems. The code in the stored procedures probably has changed since then.
I copied the code over to SQL Server Management Studio to test and everything ran fine from a direct copy and paste. No errors, no warnings, just successful.
Next, I looked up transactions online and changed rollback to if ##trancount>0 rollback and this did fix the transaction error; however - I had a new error from PHP:
Array
(
[for_php_error_number] => 50000
[for_php_error_severity] => 16
[for_php_error_state] => 1
[for_php_error_procedure] => SP_TEST_Record
[for_php_error_line] => 247
[for_php_error_message] => spTEST_Record: 515: Cannot insert the value NULL into column 'TEST_DATA', table 'tempdb.dbo.#IDs________________________________________________________________________________________________________________00000001D27E'; column does not allow nulls. INS
)
(still, a reminder: no errors are returned if I run the exact code in SQL Server Mgmt Studio)
The error involves a SP that is called at the end of each of the SP_TEST_# procedures listed in the dynamic query that PHP built. I cannot copy the SP code over to here because this is work stuff and I didn't write them so I'd really prefer to not have to paraphrase them, either, because they are huge and not formatted well. I will however, show the top of SP_TEST_RECORD where the error occurs (which isn't on line 247):
ALTER PROCEDURE [dbo].[SP_TEST_Record] (
#Test_ID real=0, #debug int=1, #create_entry int=0, #autoclose bit=0, #autodelete bit=0
)
AS
BEGIN
SET NOCOUNT ON;
declare #vtest_id real; set #vtest_id=#Test_ID
declare #vdebug int; set #vdebug=#debug
declare #vcreate_entry bit; set #vcreate_entry=#create_entry
declare #vautoclose bit; set #vautoclose=#autoclose
declare #vautodelete bit; set #vautodelete=#autodelete
declare #test_date datetime; set #test_date=getutcdate()
declare #ct int;
begin try
begin tran
if object_id('tempdb..#IDs') is not null drop table #IDs
CREATE TABLE #IDs (TYC_ID int not null, TYC_TYPE_ID int not null, TYC_ENV_ID int not null, TEST_DATA nvarchar(2000) null) ON [PRIMARY]
ALTER TABLE #IDs ADD PRIMARY KEY NONCLUSTERED (TYC_ID, TYC_TYPE_ID, TYC_ENV_ID)
insert into #IDs(TYC_ID, TYC_TYPE_ID, TYC_ENV_ID, TEST_DATA)
select TYC_ID, TYC_TYPE_ID, TYC_ENV_ID, TEST_DATA from SR_TESTING where TEST_ID=#vTest_ID
So - one thing I know... some how, my PHP transaction was being ended by something in one of the stored procedures that was called by the dynamic statement and that is cause of the transaction issue. There are transactions inside of those stored procedures.
But why don't I see the null insert error in SQL Server when I run the same exact code? If there's a null insert, there's a null insert... so why would it make a difference if it was called from PHP or not?
And less importantly, how was my transaction being ended by one of the stored procedures in the beginning?
Why was thetransaction ended by the SP's when the code was ran in PHP but not when it was ran in SQL Server?
Is there some execution-concurrency-order of operations-transaction hierarchy-something going on?
I didn't want to straight delete this question because it was, at the very least, a great learning experience for me. However, it was a very localized question. I figured out the answer a while ago so I can't remember the specifics...
It was definitely caused because of something happening in one of the stored procedures that were being called down the line. I was very surprised to find out that the error bubbled up that way, especially with the PHP. I guess error scope kind of played a part in it... the PHP took the first error it saw but running the code in SQL server allowed it to fail more gracefully (so I didn't see the error there). Dunno. Troubleshooting... when in doubt - go deeper, lol...
I have a fast running query (sub 1 sec) when I execute the query in SQL Server Mgt Studio, but when I run the exact same query in PHP (on the same db instace)
using FreeTDS v8, mssql_query(), it takes much longer (70+ seconds).
The tables I'm hitting have an index on a date field that I'm using in the Where clause.
Could it be that PHP's mssql functions aren't utilizing the index?
I have also tried putting the query inside a stored procedure, then executing the SP from PHP - the same results in time difference occurs.
I have also tried adding a WITH ( INDEX( .. ) ) clause on the table where that has the date index, but no luck either.
Here's the query:
SELECT
1 History,
h.CUSTNMBR CustNmbr,
CONVERT(VARCHAR(10), h.ORDRDATE, 120 ) OrdDate,
h.SOPNUMBE OrdNmbr,
h.SUBTOTAL OrdTotal,
h.CSTPONBR PONmbr,
h.SHIPMTHD Shipper,
h.VOIDSTTS VoidStatus,
h.BACHNUMB BatchNmbr,
h.MODIFDT ModifDt
FROM SOP30200 h
WITH (INDEX (AK2SOP30200))
WHERE
h.SOPTYPE = 2 AND
h.DOCDATE >= DATEADD(dd, -61, GETDATE()) AND
h.VOIDSTTS = 0 AND
h.MODIFDT = CONVERT(VARCHAR(10), DATEADD(dd, -1*#daysAgo, GETDATE()) , 120 )
;
what settings are on, usually ARITHABORT is the culprit, it is ON in SSMS but you might be connecting with it off
Run this in SSMS while you are running your query and see what the first column is for the session that is connected from PHP
select arithabort,* from sys.dm_exec_sessions
where session_id > 50
Run the SQL Profiler, and set up a trace and see if there are any differences between the two runs.
Using the LOGIN EVENT (and EXISTING CONNECTION) in SQL Profiler with the Text column will show the connection settings of a lot of important SET commands--Arithabort, Isolation Level, Quoted Identifier, and others. Compare and contrast these between the fast and slow connections to see if anything stands out.
SET ARITHABORT ON; in your session, might improve query performance.
https://learn.microsoft.com/en-us/sql/t-sql/statements/set-arithabort-transact-sql?view=sql-server-ver16
Always set ARITHABORT to ON in your logon sessions. Setting ARITHABORT to OFF can negatively impact query optimization, leading to performance issues.