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调用存储过程的正确用法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

SQL的存储过程 语法格式是啥?

在SQL中存储过程的一般语法是啥?

请问,我用报表调用的oracle数据库的数据存储过程乱码是啥原因啊?

在通用 DAO 中使用 Hibernate 调用存储过程的最佳方法是啥?

Oracle 存储过程中OUT参数的用法

如何在sqlserver存储过程中输出参数,语句是啥,我不用输出参数,我只是在体内输出语句,请问是啥??