zxjdbc调用存储过程的正确用法是啥?
Posted
技术标签:
【中文标题】zxjdbc调用存储过程的正确用法是啥?【英文标题】:What is the correct usage of zxjdbc to call stored procedures?zxjdbc调用存储过程的正确用法是什么? 【发布时间】:2013-08-06 00:46:12 【问题描述】:我正在尝试使用 zxJDBC 连接到在 SQL Server 2008 R2 (Express) 上运行的数据库并调用存储过程,向其传递单个参数。我正在使用 jython-standalone 2.5.3,理想情况下不想安装额外的模块。
我的测试代码如下所示。
数据库名称是CSM
存储过程:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE dbo.DUMMY
-- Add the parameters for the stored procedure here
@carrierId VARCHAR(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO dbo.carrier (carrierId, test)
VALUES (@carrierId, 'Success')
END
GO
Jython 脚本:
from com.ziclix.python.sql import zxJDBC
conn = None
try :
conn = zxJDBC.connect('jdbc:sqlserver://localhost\SQLEXPRESS', 'sa', 'password', 'com.microsoft.sqlserver.jdbc.SQLServerDriver')
cur = conn.cursor()
cur.callproc(('CSM','dbo','DUMMY'), ['carrier1'])
conn.commit()
except Exception, err :
print err
if conn:
conn.rollback()
finally :
if conn :
conn.close()
通过使用cur.execute()
,我已经能够验证上述是否成功连接到数据库,并且我可以对其进行查询。但是,到目前为止,我一直无法成功调用带参数的存储过程。
文档here(可能已过期?)表明callproc()
可以使用字符串或元组调用来标识过程。给出的例子 -
c.callproc(("northwind", "dbo", "SalesByCategory"), ["Seafood", "1998"], maxrows=2)
当我尝试使用此方法时,我收到以下错误
Error("Could not find stored procedure 'CSM.DUMMY'. [SQLCode: 2812], [SQLState: S00062]",)
zxJDBC 似乎忽略了包含过程标识符的dbo
部分。
如果我改为使用“CSM.dbo.DUMMY”作为第一个参数调用callproc
,那么我会收到此错误
Error('An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as "" or [] are not allowed. Change the alias to a valid name. [SQLCode: 1038], [SQLState: S0004]',)
在运行我的脚本时对数据库使用分析器表明,在第二种情况下,执行了以下 SQL:
use []
go
所以看起来当使用单个字符串来识别过程时,数据库名称没有被正确解析出来。
解决此问题的尝试和错误尝试之一是调用 callproc,如下所示:
cur.callproc(('CSM', '', 'dbo.DUMMY'), ['carrier1'])
这让我只有这么远
Error("Procedure or function 'DUMMY' expects parameter '@carrierId', which was not supplied. [SQLCode: 201], [SQLState: S0004]",)
在这种情况下,我认为正在发生的是 zxJDBC 尝试调用系统存储过程 (sp_proc_columns
) 以确定我要调用的存储过程所需的参数。我的猜测是,由于上述格式不正确的过程标识符,zxJDBC 没有得到有效/正确的返回,并且假定不需要任何参数。
所以基本上我对如何获得它的想法并不感到困惑
使用正确的数据库名称 使用 sp_proc_columns 正确确定所需参数 使用正确的名称调用我的存储过程同时进行。
我确实有一个解决方法,就是使用类似的东西
cur.execute('EXEC CSM.dbo.DUMMY ?', ['carrier1'])
但是我觉得callproc()
是正确的解决方案,并且在我调用具有大量参数的存储过程时可能会产生更清晰的代码。
如果有人能发现我正在犯的错误,或者知道这不会像我想的那样奏效,那么任何意见都将不胜感激。
谢谢
编辑
按照 i-one 的建议,我尝试在调用存储过程之前添加 cur.execute('USE CSM')
(同时从过程调用中删除数据库名称)。不幸的是,这会产生与上述相同的对象或列缺失错误。分析器显示正在执行 USE CSM
,然后是 USE []
,因此似乎 callproc() 总是在过程本身之前触发 USE
语句。
我也尝试过打开/关闭自动提交,但无济于事。
编辑 2
以下 cmets/建议解决方案的更多信息:
我的连接字符串中的“SQLEXPRESS”是数据库实例名称。 使用双引号代替单引号无效。 在连接字符串中包含数据库名称(通过;databaseName=CSM;
指定 here)并在 callproc() 调用中省略它会导致原始错误,并触发 USE []
语句。
使用callproc(('CSM', 'dbo', 'dbo.DUMMY'), ['carrier1'])
给了我一些进步,但导致了错误
错误(“程序或函数'DUMMY'需要参数'@carrierId',但未提供。[SQLCode:201],[SQLState:S0004]”,)
我将尝试进一步调查此问题
编辑 3
根据我可以看到 zxJDBC 触发的查询,我对我的数据库手动执行了以下操作:
use CSM
go
exec sp_sproc_columns_100 N'dbo.DUMMY',N'dbo',N'CSM',NULL,N'3'
go
这给了我一个空的结果集,这似乎可以解释为什么 zxJDBC 没有将任何参数传递给存储过程——它认为它不需要。不过,我还没有弄清楚为什么会这样。
编辑 4
要更新上面,空结果集是因为调用应该是
exec sp_sproc_columns_100 N'DUMMY',N'dbo',N'CSM',NULL,N'3'
不幸的是,我无法在 callproc() 调用中从存储过程名称中删除 dbo
所有者,否则根本找不到该过程。
编辑 5
按要求定义表
CREATE TABLE [dbo].[carrier](
[carrierId] [varchar](50) NOT NULL,
[test] [varchar](50) NULL
) ON [PRIMARY]
【问题讨论】:
其实我没有经验回答,我只是看到赏金时间已经过去了,你一个人解决你的问题。我在 zxjdbc 文档中注意到的一件事。你可以试试跟随吗?在您启动游标之后(在cur = conn.cursor()
之后)尝试c.execute("use CSM")
(这应该将上下文从master
数据库切换到CSM
并允许引用没有db 名称前缀的db 对象)然后尝试cur.callproc("dbo.DUMMY",["carrier1"])
甚至cur.callproc("DUMMY",["carrier1"])
。如果你这样做会发生什么?
@i-one 感谢您的建议。恐怕我没有运气,但已经用结果描述编辑了问题
可能是cur.callproc(('CSM', '', 'dbo.DUMMY ?'), ['carrier1'])
或cur.callproc(('CSM', '', 'dbo.DUMMY @carrierId'), ['carrier1'])
?
那些似乎混淆了对sp_proc_columns
的调用,因为它认为参数是存储过程名称的一部分
哦,可惜了。但至少你有可行的解决方法。
【参考方案1】:
虽然完全不知道这里使用的技术(除非对 SQL Server 有一些小的了解),但我会尝试回答(如果我的 jython 语法不正确,请原谅我。我试图在这里概述可能性而不是确切的代码)
我的第一个方法(在this post 找到)是尝试:
cur.execute("use CSM")
cur.callproc(("CSM","dbo","dbo.DUMMY"), ["carrier1"])
这一定与sa
用户始终将dbo
作为默认架构这一事实有关(在this SO post 中描述)
如果上述方法不起作用,我也会尝试在 JDBC url 中使用 CSM
数据库名称(这在将 JDBC 用于其他数据库时很常见),然后只需调用以下两者之一。
cur.callproc("DUMMY", ["carrier1"])
cur.callproc("dbo.DUMMY", ["carrier1"])
希望对你有帮助
更新:我引用了您无法查看的链接的相关部分
>> Program calls a Stored Procedure - master.dbo.xp_fixeddrives on MS SQL Server
from com.ziclix.python.sql import zxJDBC
def getConnection():
url = "$DBServer.Url"
user= "$DBServer.User"
password = "$DBServer.Password"
driver = "$DBServer.Driver"
con = zxJDBC.connect(url, user, password, driver)
return con
try:
conn = getConnection()
print 'Connection successful'
cur = conn.cursor()
cur.execute("use master")
cur.callproc(("master", "dbo", "dbo.xp_fixeddrives"))
print cur.description
for a in cur.fetchall():
print a
finally:
cur.close()
conn.close()
print 'Connection closed'
您在指定上述调用函数时遇到的错误表明参数未正确传递。因此,请修改您的存储过程以采用默认值并尝试通过传递params = [None]
进行调用。如果您看到调用成功,就指定数据库而言,我们一定做对了一些事情。
顺便说一句:most recent documentation 建议您应该能够使用您的语法访问它。
【讨论】:
我已根据尝试这些建议的结果更新了问题。第一种方法给了我一些进步,但不幸的是,由于工作中的过滤器,我无法查看相关链接 如果您看到我上面的编辑 3 和 4,我很确定问题出在“dbo”方面。连接数据库没问题,但是当使用元组指定存储过程名称时,中间元素(dbo)似乎被忽略了。将此添加到最后一个元素仅适用于存储过程没有参数的情况,因为 dbo.procName 不是正确的过程名称,因此所需的参数不是由 sp_sproc_columns 确定的 @Vindicare 是的,我认为你是正确的。这意味着您的驱动程序可能存在缺陷。 Another example 看起来像你的,用户最后决定使用cursor.execute()
。
我将在这里奖励赏金,但不接受答案,因为虽然问题没有解决,但建议很有帮助并表明了对问题的理解。【参考方案2】:
如 cmets 中所述,callproc 仅适用于 SELECT。试试这种方法:
cur.execute("exec CSM.dbo.DUMMY @Param1='" + str(Param1) + "', @carrierId=" + str(carrierID))
详情请见this link。
【讨论】:
我偶然发现了该链接,这是导致我找到问题中提到的解决方法的部分原因。然而,它没有说明 SELECT 与 INSERT 的问题,我相信我的调查表明这不是问题。此外,文档here 建议不要使用字符串连接来构造查询,而是使用准备好的语句(根据我的示例解决方法)以上是关于zxjdbc调用存储过程的正确用法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
请问,我用报表调用的oracle数据库的数据存储过程乱码是啥原因啊?