i am running a MERGE statement against an SQL Server 2008 R2 database (which is in 2008 compatibility mode).
The exact merge statement SQL is irrelevant, but here is an example of a MERGE statement:
MERGE Users
USING (VALUES
('{77410DC5-7A3E-4F1A-82C6-8EFB3068DE66}')
) AS rows(UserGUID)
ON Users.UserName = rows.UserName
WHEN NOT MATCHED BY SOURCE THEN
DELETE; --always end MERGE with semi-colon
When executing this statement from SSMS, or from a client PC running Windows 7, it executes correctly.
But when my software is running on a client PC that is Windows XP or Windows Server 2003 R2, the sql CommandText is being altered before reaching the server. In the SQL Server Profiler, i can see the SQL being executed is:
exec MERGE Users
USING (VALUES
('{77410DC5-7A3E-4F1A-82C6-8EFB3068DE66}')
) AS rows(UserGUID)
ON Users.UserName = rows.UserName
WHEN NOT MATCHED BY SOURCE THEN
DELETE; --always end MERGE with semi-colon
Which is not valid SQL, and SQL Server throws the error:
Incorrect syntax near the keyword 'MERGE'
You can confirm that it is invalid SQL by trying to execute it against your SQL Server 2008 R2 database.
Peter Boulton reported the same issue on the Microsoft forums:
SQL MERGE syntax works with Win7 SP1 client but fails with earlier platforms
I believe ADO parses the SQL before it sends it to the server. The data access components were updated for Win7 SP1 and also WinServer 2008 R2. However, I believe XP SP3's data access components predate SQL Server 2008.
That's why the SQL works from Win7 SP1 but not from XP.
My 'solution' was to wrap the SQL in an EXEC so that ADO allows it through, as in:
EXEC('MERGE....etc.')
His hack certainly works, change:
MERGE Users ...
into
EXEC('MERGE Users' ...)
but i'd like to come up with the real solution. I don't know who in the chain is responsible for altering my command text:
ADO -> OLEDB -> SQLOLEDB -> SQL Server
but i want them to stop.
How do i, through ADO, specify my command text, and have SQLOLEDB not modify it?
Right now my code is1:
String sqlCommandText = "MERGE Users" //snip;
int recordsAffected;
connection.Execute(
sqlCommandText,
out recordsAffected,
adCmdText | adExecuteNoRecords);
i do not see any ExecuteOptionEnum, or CommandTypeEnum to tell ADO, and underlying providers, to treat the text as raw.
For now the fixup hack is:
sql = "MERGE Users" ...
ExecuteNoRecords(connection, sql);
with the modified helper:
int ExecuteNoRecords(Connection connection, String sql)
{
//20130611: Fix bug in ADO that mangles/breaks SQL it doesn't understand (e.g. MERGE on Windows XP)
String obfuscatedCommandText = 'EXEC(' + QuotedStr(sql)+ ')';
int recordsAffected;
connection.Execute(obfuscatedCommandText, out recordsAffected, adCmdText | adExecuteNoRecords);
return recordsAffected;
}
Update: A better workaround hack
Rather than wrapping the entire statement in EXEC(...), i found a safer trick to defeating ADO is to precede the statement with a comment. Even an empty comment will do:
--
MERGE Users
USING (VALUES ...
In reality you will want to have some text explaining that the empty comment line is critical to making the query work:
--Leading comment to thwart ADO from mangling MERGE on Windows XP/2003R2
MERGE Users
USING (VALUES ...
Since there is no solution, and ADO (while not dead) is done, the hack is the answer.
Never issue a MERGE statement without a leading comment line:
--Dummy leading comment line to thwart ADO from mangling MERGE on Windows XP/2003R2
MERGE Users
USING (VALUES ...
Related
Here is my simple query:
my $SQLp = "SELECT MAX([PawnPayments].[CreationTimeDate]) as MaxTransDate
FROM [PawnSafeDBCE].[dbo].[PawnPayments]
INNER JOIN [PawnSafeDBCE].[dbo].[PawnPaymentDetails]
ON [PawnPayments[.[PaymentID] = [PawnPaymentDetails].[PaymentID]
WHERE [PawnPaymentDetails].[TicketID[ = '$TicketID'
AND [PawnPaymentDetails].[StoreID] ='$StoreID'
Note that query is written on Perl engine. I keep receiving an error that says:
"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '[PawnPayments].[CreationTimeDate]) as MaxTransDate:"
I believe the error has to do with the bracket notation, but unfortunately, I am having to use this style due to a poorly constructed 3rd party table. Any help? Am I missing something obvious?
Huge EDIT: The table I am querying is actually on a SQL server, not a MySQL server! My database runs on the MySQL server, but this 3rd party database runs on an older version of Microsoft SQL.
I don't know why you have all those square brackets around your table and column names, but they aren't necessary and they aren't standard SQL. That's what is causing your syntax error.
my $SQLp = "SELECT MAX(PawnPayments.CreationTimeDate) as MaxTransDate
FROM PawnSafeDBCE.dbo.PawnPayments
INNER JOIN PawnSafeDBCE.dbo.PawnPaymentDetails
ON PawnPayments.PaymentID = PawnPaymentDetails.PaymentID
WHERE PawnPaymentDetails.TicketID = '$TicketID'
AND PawnPaymentDetails.StoreID ='$StoreID'";
I'll also add that having variables interpolated in your SQL statement like that is potentially leaving you open to SQL injection attacks. Far better to use bind points in your SQL and use extra arguments to execute to fill in the values (assuming you're using DBI).
my $SQLp = "SELECT MAX(PawnPayments.CreationTimeDate) as MaxTransDate
FROM PawnSafeDBCE.dbo.PawnPayments
INNER JOIN PawnSafeDBCE.dbo.PawnPaymentDetails
ON PawnPayments.PaymentID = PawnPaymentDetails.PaymentID
WHERE PawnPaymentDetails.TicketID = ?
AND PawnPaymentDetails.StoreID = ?";
my $sth = $dbh->prepare($SQLp);
$sth->execute($TicketID, $StoreID);
Update: As Bill Karwin points out in a comment, the database.schema.table syntax makes no sense in a MySQL database. So I think you're a little confused. The error message you are getting definitely mentions MySQL, so you're connecting to a MySQL server, using DBD::MySQL - but perhaps you should be connecting to an MSSQL server instead.
It might be useful if you showed us your database connection code - the call that sets up your $dbh (or equivalent) variable.
You say you are querying a MS SQL database, but the error message clearly says you are using a MySQL database or a MySQL database driver.
If you are querying a MS SQL database, fix your connection string.
If you are querying a MySQL database, use a MySQL-compatible query. MySQL uses backticks to quote identifiers (not square brackets like MS SQL).
[PawnPayments].[CreationTimeDate]
should be
`PawnPayments`.`CreationTimeDate`
Note that your code suffers from injection bugs due to incorrect quoting of value inserted into the SQL query. (It's not good enough just to put quotes around the values!) These can cause your code to fail, and they could make you vulnerable to injection attacks. Fix the quoting, or use replaceable parameters.
Strange situation with my ODBC code ( called from a C library ). Basically, I have the following sequence of events:
Create insert statement ( just a string )
Call SQLPrepare with that insert statement string
Bind the various parameters ( column values ), using
SQLBindParameter
Call SQLExecute to insert the row ( this works, by the way, as I can
see the row in the MySQL DB )
Create "select last_insert_id()" statement string
NOTE: if in SQL Server mode, we would create a "select ##identity"
statement
Bind column using SQLBindCol - this is where I get the "Invalid
descriptor index" error
NOTE: if in SQL Server mode, this works fine, with no error
Call SQLExecDirect to get the last insert id - this never happens
because of SQLBindCol error
Does the standard MySQL ODBC connector require something special in this situation? Does anyone have an ODBC example of this type of "insert" then "get last insert id" behavior? Maybe I need to call "SQLPrepare" before step 6 ( where I bind the column )? Another way to ask this: Should there be an SQLPrepare call for each SQLExecute or SQLExecDirect call?
I know it works directly in SQL, so the problem is my C ODBC code.
Thanks.
For those who are interested, I ended up changing the above steps by adding an SQLPrepare call between creating the "select last_insert_id()" ( step 5 ) and calling SQLBindCol ( step 6 ). Not sure if that would work for others, but it seems to be working rather well for me.
As for research, I looked all over the place online and never found a really good or clear answer. Most comments were about the SQL involved, not ODBC. And the references to ODBC were vague and did not seem to apply to my situation, from what I could see.
My assumption is that the SqlServer ODBC driver I am using handles the missing prepare statement differently ( maybe even better, but that is debatable ) than my MySql ODBC driver.
SQL Server ODBC driver was the one provided by Easysoft
MySql ODBC driver was the one provided with the standard CentOS install of MySql
Hopefully this will help people. Obviously, if people have a better idea, please tell us.
ERROR MSG: The requested operation could not be performed because OLE DB provider "Microsoft.Jet.OLEDB.4.0" for linked server "(null)" does not support the required transaction interface.
I am stucked finding solutions for this error (see ERROR MSG).
Here is part of my code:
.....
INSERT INTO OPENROWSET('Microsoft.Jet.OLEDB.4.0', 'Excel 8.0;
Database=C:\Folder\file_output.xls',
'SELECT * FROM [Sheet1$]')
SELECT FROM FIELD1, FIELD2, FIELD3 dbo.MY_TABLE
WHERE TRANS_ID = 1
ORDER BY CTRL_NO ASC
.....
The above code is contained inside an SP... This is the last SP that is run in a series of SP calls.
Here is the chain/flow of execution: SP1->SP2->SP3->SP4 (assuming SP4 contains the above code)
But an error occurs as what the ERROR MSG suggests during the entire execution flow.
Here are the 2 scenarios that I've tried to test/trace the error.
- Run the the chain of SPs ----> result: error occurs
- Comment/Remove SP4, then run SP1->SP2->SP3 only. Then separately execute SP4. ----> result: okay
PLS. NOTE:I need to execute the SPs in flow.
There is no web/any front-end except SP1 is executed through SQL runner
I now wonder if this is a bug in MS SQL Server 2008 or I'm missing something.
If my question is still vague, kindly ask me for clarification. Thanks.
Try to check "Non transacted updates" option in "Server objects - Linked servers - Providers - Microsoft.Jet.OLEDB.4.0" properties. Maybe you will need to restart your sql server.
I'm having a bit of a problem with moving specific data from one server running SQL Server 2000 (A) and another running SQL Server 2008 (B). I'm writing a script according to customer specifications which is to be executed on A, populating tables in B with data. However, I can't seem to get the server link to work.
-- Bunch of declarations here
EXEC sp_addlinkedserver #server = #ServerName,
#srvproduct = #ServerProduct,
#provider = #ProviderString,
#datasrc = #RemoteHost
-- Data migration stuff here
EXEC sp_dropserver #ServerName
Now if I run the script in its entirety I get an error saying:
Msg 7202, Level 11, State 2, Line 55
Could not find server 'remoteServer'
in sysservers. Execute
sp_addlinkedserver to add the server
to sysservers.
However, if I highlight only the sp_addlinkedserver part and execute that, there is no error and I can highlight the rest of the script and run it. What am I missing here?
Please help!
PS. If backup-restore was an option, I would have done that already.
The problem is that SQL Server is trying to validate the code before it runs it. To show that, try putting one of your commands in EXEC(). Try putting a GO after the sp_addlinkedserver command.
I'm having issues when trying to call a MySQL (5.0.77) stored procedure with parameters, via a linked server (SQL Server 2005) using the OPENQUERY syntax.
The MySQL stored procedure returns a result set, and when I use the 'EXEC ... AT ...' syntax the call works fine, e.g...
EXEC('CALL my_stored_proc(''2009-10-07'',''2009-10-07'');') AT MySQLSERVER;
The limitation of using 'EXEC ... AT ...' means I can't insert the result set into a temporary table in SQL Server, which is ultimately what I want to do. Which led me to trying the OPENQUERY syntax...
SELECT * FROM OPENQUERY(MySQLSERVER,'CALL my_stored_proc(''2009-10-07'',''2009-10-07'');')
...But this fails, and returns...
Msg 7357, Level 16, State 2, Line 1
Cannot process the object "CALL my_stored_proc(''2009-10-07'',''2009-10-07'');". The OLE DB provider "MSDASQL" for linked server "MySQLSERVER" indicates that either the object has no columns or the current user does not have permissions on that object.
Which is strange, given that the 'EXEC ... AT ...' call didn't complain about permissions. The following calls all work fine...
EXEC('SHOW TABLES;') AT MySQLSERVER;
SELECT * FROM OPENQUERY(MySQLSERVER,'SHOW TABLES;');
CREATE TABLE #tmpTest (
[table] varchar(255) null
);
INSERT INTO #tmpTest ([table])
SELECT * FROM OPENQUERY(MySQLSERVER,'SHOW TABLES;');
SELECT * FROM #tmpTest;
DROP TABLE #tmpTest;
So my question is, how can I make a call to a MySQL stored procedure, via a linked server, and store the result set in a temporary table in SQL Server? Either by using the 'EXEC ... AT ...' syntax, or by solving the object/permissions error when using the OPENQUERY syntax.
Any help would be greatly appreciated!
You need to enable "Ad Hoc Distributed Queries" on the SQL Server. This is not enabled by default, for security reasons. Most of the time, the "do not have permission" errors are related to this one.
Execute this on the SQL Server, and then try again your code:
EXEC sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
EXEC sp_configure 'Ad Hoc Distributed Queries', 1;
GO
RECONFIGURE;
GO
I hope it helps.
I tried the fix to configure "Ad Hoc Distributed Queries" but was still getting this same error:
"The OLE DB provider "MSDASQL" for linked server "MyMySQL" indicates
that either the object has no columns or the current user does not
have permissions on that object."
I am attempting a simple remote SELECT query against a linked MySQL database.
In my case, the query I was attempting looked like this. Pay attention to the number of lines used:
SELECT * FROM OPENQUERY
([MyMySQL],
'
SELECT
ID as ISSUE_ID, etc..
The fix was to remove the newline after the opening single-quote. Simple, huh? But not obvious.
The working code shown below:
SELECT * FROM OPENQUERY
([MyMySQL],
'SELECT
ID as ISSUE_ID, etc..
Hope it helps!
Rob
I realize this a pretty old post, but my problem was similar to Rob's in that the SQL within the OPENQUERY doesn't take line breaks as whitespace the same way SQL server does.
i.e. Laying out my query as follows:
SELECT
*
FROM
OPENQUERY(LINKEDSERVER, '
SELECT
SomeField
FROM
SomeTable
')
I needed to have spaces as if it were written on one line (using _ as a visible space for demonstration):
SELECT
*
FROM
OPENQUERY(LINKEDSERVER, '
SELECT_
SomeField_
FROM_
SomeTable
')