[英]Python Running SQL Stored Procedure
I am relatively very new to the world of SQL. 我对SQL领域来说还很陌生。 I have a stored proc that somebody wrote and I simply want to execute it thru my Python. 我有一个有人写的存储过程,我只是想通过我的Python执行它。
Now the stored proc accepts a table as an input and generates two output tables. 现在,存储的proc接受一个表作为输入并生成两个输出表。 It works fine and I have tried it in SQL Studio using 它工作正常,我已经在SQL Studio中使用
exec CalcIdxRet_v4 INPUT_TABLE
and it works absolutely fine. 它的工作原理绝对不错。 However when i run it in Python. 但是,当我在Python中运行它时。 It works fine and no errors but it ONLY produces one of the 2 output tables. 它工作正常且没有错误,但仅生成2个输出表之一。 Since there are no errors and still there is one output table only, it is puzzling to me. 由于没有错误,但仍然只有一个输出表,这令我感到困惑。
Can someone throw some light. 有人可以给我一些光。 Many many thanks 非常感谢
Python code: Python代码:
import pyodbc as db
(username, password, driver, database, server) = ('xxx', 'xxx', '{ODBC Driver 13 for SQL Server}', 'DB', 'ZZZZZ')
conn = db.connect('DRIVER='+driver+'; PORT=1433; SERVER='+server+'; PORT=1443; DATABASE='+database+'; UID='+username+'; PWD='+ password)
cursr = conn.cursor()
sql_btest_1 = ('exec CalcIdxRet_v4 INPUT_TABLE')
cursr.execute(sql_btest_1)
conn.commit()
Here is the stored proc 这是存储的过程
USE [DevDB]
GO
/****** Object: StoredProcedure [dbo].[CalcIdxRet_v4] Script Date: 12/12/2018 6:46:13 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- drop table temp
-- create table temp(date date, fs_perm_sec_id varchar(10), weight float)
-- insert into temp values('20160101','aaa',1)
-- [CalcIdxRet_v2b] 'temp'
ALTER procedure [dbo].[CalcIdxRet_v4]
@weightTable varchar(max),
@retType VARCHAR(25) = null, -- USD or local
@constTable varchar(max) = null,
@indexTable varchar(max) = null,
@finalDate date = '21000101'
/* Table definitions
- @weightTable: user supplied date, fs_perm_sec_id, weight
- @constTable: the constituent data when all is done
- TempReturns: mirrors @constTable this is where I work
*/
as
-- catch table names with a period
if charindex('.',@weightTable) > 0 GOTO error_one
if charindex('.',@constTable) > 0 GOTO error_one
if charindex('.',@indexTable) > 0 GOTO error_one
-- auto generate output table names
if @constTable is null set @constTable = @weightTable + 'ConstituentReturns'
if @indexTable is null set @indexTable = @weightTable + 'IndexReturns'
/*
alter view vw_HistPrice_monthly_USD as select date, startDate, endDate, terminationFlag, fs_perm_sec_id, priceRet, divRet, totalRet from HistPrice_monthly
alter view vw_HistPrice_monthly_local as select date, startDate, endDate, terminationFlag,fs_perm_sec_id, priceRet_local as priceRet, divRet, totalRet_local as totalRet from HistPrice_monthly
alter view vw_HistPrice_daily_USD as select date, startDate, endDate, terminationFlag,fs_perm_sec_id, priceRet, divRet, totalRet from HistPrice_daily
alter view vw_HistPrice_daily_local as select date, startDate, endDate, terminationFlag,fs_perm_sec_id, priceRet_local as priceRet, divRet, totalRet_local as totalRet from HistPrice_daily
alter view vw_HistPriceUS_monthly_USD as select date, startDate, endDate, terminationFlag,fs_perm_sec_id, priceRet, divRet, totalRet from HistPriceUS_monthly
alter view vw_HistPriceUS_daily_USD as select date, startDate, endDate, terminationFlag,fs_perm_sec_id, priceRet, divRet, totalRet from HistPriceUS_daily
*/
declare @DynamicSQL nvarchar(max)
declare @startDate date = '19000101'
declare @startDatePrev date = '19000101'
declare @endDate date
declare @interimDate date
declare @interimDatePrev date
declare @schema varchar(20) = (select SCHEMA_NAME())
declare @temp varchar(25) = (select @schema + '.temp_returns')
declare @error int = 0
set @weightTable = (select @schema + '.' + @weightTable)
set @constTable = (select @schema + '.' + @constTable)
set @indexTable = (select @schema + '.' + @indexTable)
if charindex('#',@weightTable) > 0 -- if temp table used for table weights, schema is wrong
begin
set @weightTable = (select 'tempdb.' + @schema + '.' +
substring(@weightTable,(select charindex('#',@weightTable)),(select DATALENGTH(@weightTable))))
end
IF OBJECT_ID(@temp, 'U') IS NOT NULL
begin
set @DynamicSQL = 'DROP TABLE ' + @temp;
exec (@DynamicSQL)
end
------------------------------------
-- Ensure proper inputs
------------------------------------
-- ill-specified weighting table
set @DynamicSQL =
'set @error0 = (select case when OBJECT_ID ('''+@weightTable+''', ''U'') IS NULL then 1 else 0 end)'
EXECUTE sp_executesql @DynamicSQL, N'@error0 bit OUTPUT', @error0=@error OUTPUT
if @error = 1 goto error_three
-- wrong columns
CREATE TABLE #cols(column_name varchar(500), IsDate int, IsNum int, IsChar Int)
SET @DynamicSQL =
'INSERT INTO #cols
SELECT column_name
, case when datetime_precision is not null then 1 else 0 end as IsDate
, case when numeric_precision is not null then 1 else 0 end as IsNum
, case when character_maximum_length is not null then 1 else 0 end as IsChar
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA+''.''+TABLE_NAME = '''+@weightTable+''''
EXEC(@DynamicSQL)
set @error = (
select 3-sum(case when column_name = 'date' and IsDate > 0 then 1 else 0 end)
- sum(case when column_name = 'fs_perm_sec_id' and IsChar > 0 then 1 else 0 end)
- sum(case when column_name = 'weight' and IsNum > 0 then 1 else 0 end) as error
from #cols
)
if @error > 0 goto error_five
-- non-unique date / fs_perm_sec_id
set @DynamicSQL =
'select @error0 = count(*)
from (
SELECT date, fs_perm_sec_id, COUNT(*) as ct
FROM ' + @weightTable +
' GROUP BY date, fs_perm_sec_id
HAVING COUNT(*) > 1
) as a'
EXECUTE sp_executesql @DynamicSQL, N'@error0 int OUTPUT', @error0=@error OUTPUT
if @error > 0 goto error_four
------------------------------------
-- Copy table structure from Returns
------------------------------------
-- what type of returns are calculating?
SET @DynamicSQL = 'select top 0 date, startDate, endDate, terminationFlag, fs_perm_sec_id, priceRet_local, totalRet_local, fxRet, priceRet as priceRet_USD, totalRet as totalRet_USD INTO ' + @temp + ' from HistPrice_daily';
exec (@DynamicSQL)
SET @DynamicSQL = 'alter table ' + @temp + ' add weightOpen float, weightClose float'
exec (@DynamicSQL)
SET @DynamicSQL = 'alter table ' + @temp + ' alter column enddate date'
exec (@DynamicSQL)
SET @DynamicSQL = 'SELECT top 0 * INTO ' + @constTable + ' FROM ' + @temp;
exec (@DynamicSQL)
------------------------------------
-- Pre-Allocate Returns for Speed
------------------------------------
CREATE TABLE #RetTable
( date DATE
, startDate DATE
, endDate DATE
, terminationFlag BIT
, fs_perm_sec_id VARCHAR(50)
, priceRet_local FLOAT
, totalRet_local FLOAT
, fxRet FLOAT
, priceRet_usd FLOAT
, totalRet_usd FLOAT
)
set @DynamicSQL =
'insert into #RetTable
select date, startDate, endDate, terminationFlag, fs_perm_sec_id, priceRet_local, totalRet_local, case when currency = ''USD'' then 1 else fxRet end as fxRet, priceRet, totalRet
from HistPrice_daily as a
where fs_perm_sec_id in (select fs_perm_sec_id from ' + @weightTable + ')
and priceRet is not null'
exec (@DynamicSQL)
set @DynamicSQL =
'insert into #RetTable
select date, startDate, endDate, terminationFlag, fs_perm_sec_id, priceRet_local, totalRet_local, case when currency = ''USD'' then 1 else fxRet end as fxRet, priceRet_local as priceRet, totalRet_local as totalRet
from HistPriceUS_daily as a
where fs_perm_sec_id in (select fs_perm_sec_id from ' + @weightTable + ')
and priceRet is not null'
exec (@DynamicSQL)
CREATE INDEX idx ON #RetTable(fs_perm_sec_id,startDate)
------------------------------------
-- Initial start and end date
------------------------------------
set @DynamicSQL =
'select @date = min(date) from ' + @weightTable +
' where date > (select coalesce(max(date),''19000101'') from ' + @constTable + ')'
EXECUTE sp_executesql @DynamicSQL, N'@date date OUTPUT', @date=@startDate OUTPUT
set @DynamicSQL =
'select @date = coalesce(min(date),''21000101'') from ' + @weightTable +
' where date > ''' + convert(varchar(8),@startDate,112) +''''
EXECUTE sp_executesql @DynamicSQL, N'@date date OUTPUT', @date=@endDate OUTPUT
------------------------------------
-- Loop on rebalance dates
------------------------------------
declare @run bit = 1
if @startDate = '21000101' begin set @run = 0 end
while (@run = 1)
begin
set @DynamicSQL = 'truncate table ' + @temp;
exec (@DynamicSQL)
-- source the dreturns data
set @DynamicSQL =
'insert into ' + @temp +
' select date, startDate, endDate, terminationFlag, fs_perm_sec_id, priceRet_local, totalRet_local, fxRet, priceRet_usd, totalRet_usd
, null as weightOpen, null as weightClose
from #RetTable as a
where startDate between dateadd(d,1,''' + convert(varchar(8),@startDate,112) + ''') and ''' + convert(varchar(8),@endDate,112) +'''
and fs_perm_sec_id in (select fs_perm_sec_id from ' + @weightTable + ' where date = '''+convert(varchar(8),@startDate,112)+''')
and priceRet_local is not null'
exec (@DynamicSQL)
-- This prevents corp actions prior to being in portfolio
set @DynamicSQL =
'delete from ' + @temp + ' where startDate < (select max(endDate) from ' + @constTable + ')'
exec (@DynamicSQL)
------------------------------------
-- Fill missing companies
------------------------------------
-- This is really slick, it allows for errant days where a single
-- company shows up in the pricing table. On these days, we dummy
-- a return of 1, so the rest of the portfolio just carries forward!
set @DynamicSQL = '
insert into ' + @temp + ' (date, fs_perm_sec_id, priceRet_local, totalRet_local, priceRet_usd, totalRet_usd)
select *, 1 as priceRet_local, 1 as totalRet_local, 1 as priceRet_usd, 1 as totalRet_usd
from (
select *
from ( select distinct date from ' + @temp + ' ) as a
join (select distinct fs_perm_sec_id from ' + @weightTable + ' where date = '''+convert(varchar(8),@startDate,112)+''') as b
on 1=1
except
select date, fs_perm_sec_id
from ' + @temp + '
) as aa
-- delete past termination
delete from ' + @temp + '
where fs_perm_sec_id + convert(varchar(8),date,112)
in (
select a.fs_perm_sec_id + convert(varchar(8),a.date,112)
from ' + @temp + ' as a
join ' + @temp + ' as b
on a.date > b.date and a.fs_perm_sec_id = b.fs_perm_sec_id
where b.terminationFlag = 1
)
-- update enddate
;WITH cte AS (SELECT *, ROW_NUMBER() OVER(PARTITION BY date ORDER BY ct DESC) AS rn
FROM (SELECT date, enddate, COUNT(*) AS ct
FROM ' + @temp + '
GROUP BY date, enddate) AS a
)
UPDATE ' + @temp + '
SET enddate = bb.enddate
FROM ' + @temp + ' AS aa
JOIN cte AS bb
ON aa.date = bb.date
where aa.enddate IS NULL AND bb.rn = 1'
exec (@DynamicSQL)
------------------------------------
-- Initial weights
------------------------------------
set @DynamicSQL =
'update ' + @temp +
' set weightOpen = b.weight
from ' + @temp + ' as a
join ' + @weightTable + ' as b
on a.fs_perm_sec_id = b.fs_perm_sec_id
where a.date = (select min(date) from ' + @temp + ')
and b.date = ''' + convert(varchar(8),@startDate,112) + '''';
exec (@DynamicSQL)
set @DynamicSQL =
'select @date = (select min(date) from ' + @temp + ')'
EXECUTE sp_executesql @DynamicSQL, N'@date date OUTPUT', @date=@interimDate OUTPUT
-- normalize weight
set @DynamicSQL =
'update ' + @temp +
' set weightOpen = weightOpen / b.wgt
from ' + @temp + ' as a
join (
select date, sum(weightOpen) as wgt
from ' + @temp + '
where date = ''' + convert(varchar(8),@interimDate,112) + '''
group by date
) as b
on a.date = b.date
-- update close
update ' + @temp + '
set weightClose = priceRet_usd*weightOpen / div
from ' + @temp + ' as a
join (
select date, sum(priceRet_usd*weightOpen) as div
from ' + @temp + '
where date = ''' + convert(varchar(8),@interimDate,112) + '''
group by date
) as b
on a.date = b.date'
exec (@DynamicSQL)
------------------------------------
-- Loop on intirim dates
------------------------------------
declare @ct int
set @DynamicSQL =
'select @ct2 = (select count(*) from ' + @temp + ' where date > ''' + convert(varchar(8),@interimDate,112) + ''')'
EXECUTE sp_executesql @DynamicSQL, N'@ct2 int OUTPUT', @ct2=@ct OUTPUT
while (@ct <> 0)
begin
select @interimDatePrev = @interimDate
--select @interimDate = min(date) from TempReturns where date > @interimDatePrev
set @DynamicSQL =
'select @date = (select min(date) from ' + @temp + ' where date > ''' + convert(varchar(8),@interimDate,112) + ''')'
EXECUTE sp_executesql @DynamicSQL, N'@date date OUTPUT', @date=@interimDate OUTPUT
-- push forward weights
set @DynamicSQL =
'update a
set weightOpen = b.weightClose
from ' + @temp + ' as a
join ' + @temp + ' as b
on a.fs_perm_sec_id = b.fs_perm_sec_id
where a.date = ''' + convert(varchar(8),@interimDate,112) + '''
and b.date = ''' + convert(varchar(8),@interimDatePrev,112) + ''''
exec(@DynamicSQL)
-- update in case something was terminated
set @DynamicSQL =
'update a
set weightOpen = weightOpen / b.wgt
from ' + @temp + ' as a
join (
select date, sum(weightOpen) as wgt
from ' + @temp + '
where date = ''' + convert(varchar(8),@interimDate,112) + '''
group by date
) as b
on a.date = b.date'
exec(@DynamicSQL)
-- update close
set @DynamicSQL =
'update a
set weightClose = priceRet_usd*weightOpen / div
from ' + @temp + ' as a
join (
select date, sum(priceRet_usd*weightOpen) as div
from ' + @temp + '
where date = ''' + convert(varchar(8),@interimDate,112) + '''
group by date
) as b
on a.date = b.date'
exec(@DynamicSQL)
set @DynamicSQL =
'select @ct2 = (select count(*) from ' + @temp + ' where date > ''' + convert(varchar(8),@interimDate,112) + ''')'
EXECUTE sp_executesql @DynamicSQL, N'@ct2 int OUTPUT', @ct2=@ct OUTPUT
end
-------------
-- END WHILE
------------
set @DynamicSQL = 'insert into ' + @constTable + ' select * from ' + @temp
exec(@DynamicSQL)
-------------------
-- update dates
-------------------
-- If there is no end date, we set end date to 2100-01-01 and the portfolio gets
-- computed all of the way out. If there is rebalance date more recent than the
-- max date in constituent table, then start date to 2100-01-01. This is our
-- flag to end loop
set @startDatePrev = @startDate
set @DynamicSQL =
'select @date = coalesce(min(date),''21000101'') from ' + @weightTable +
' where date >= (select max(Date) from ' + @constTable + ')'
EXECUTE sp_executesql @DynamicSQL, N'@date date OUTPUT', @date=@startDate OUTPUT
set @DynamicSQL =
'select @date = coalesce(min(date),''21000101'') from ' + @weightTable +
' where date > ''' + convert(varchar(8),@startDate,112) +''''
EXECUTE sp_executesql @DynamicSQL, N'@date date OUTPUT', @date=@endDate OUTPUT
if @endDate >= @finalDate set @endDate = @finalDate
if @startDate >= @finalDate set @run = 0
if @startDate in('21000101',@startDatePrev) set @run = 0
end
-------------
-- END WHILE
------------
set @DynamicSQL = 'drop table ' + @temp;
exec(@DynamicSQL)
print('constituents done')
-------------
-- Aggregate
-------------
declare @datePrev date
declare @date date
set @DynamicSQL =
'select min(endDate) as date
, count(*) as constituents
, sum(weightOpen) as weightOpen
, sum(weightClose) as weightClose
, sum(weightOpen * priceRet_local) as priceRet_local
, sum(weightOpen * totalRet_local) as totalRet_local
, sum(weightOpen * fxRet) as fxRet
, sum(weightOpen * priceRet_usd) as priceRet_usd
, sum(weightOpen * totalRet_usd) as totalRet_usd
into ' + @indexTable +
' from ' + @constTable +
' group by date'
exec (@DynamicSQL)
set @DynamicSQL =
'delete from ' + @indexTable + ' where date is null'
exec(@DynamicSQL)
set @DynamicSQL =
'alter table ' + @indexTable +
' add priceIdxLevel_local numeric(16,8)
, trIdxLevel_local numeric(16,8)
, priceIdxLevel_usd numeric(16,8)
, trIdxLevel_usd numeric(16,8)'
exec (@DynamicSQL)
set @DynamicSQL =
'insert into ' + @indexTable + ' (date, priceIdxLevel_local,trIdxLevel_local,priceIdxLevel_usd, trIdxLevel_usd)
select (select min(startdate) from ' + @constTable + '),100,100,100,100'
exec (@DynamicSQL)
set @DynamicSQL =
'select @date = (select max(date) from ' + @indexTable + ' where priceIdxLevel_local is not null)'
EXECUTE sp_executesql @DynamicSQL, N'@date date OUTPUT', @date=@datePrev OUTPUT
set @DynamicSQL =
'select @date2 = (select min(date) from ' + @indexTable + ' where date > ''' + convert(varchar(8),@datePrev,112) + ''')'
EXECUTE sp_executesql @DynamicSQL, N'@date2 date OUTPUT', @date2=@date OUTPUT
set @run = 1
while (@run = 1)
begin
set @DynamicSQL =
'update a
set priceIdxLevel_local = b.priceIdxLevel_local*a.priceRet_local
, trIdxLevel_local = b.trIdxLevel_local*a.totalRet_local
, priceIdxLevel_usd = b.priceIdxLevel_usd*a.priceRet_usd
, trIdxLevel_usd = b.trIdxLevel_usd*a.totalRet_usd
from ' + @indexTable + ' as a
join ' + @indexTable + ' as b
on a.date = ''' + convert(varchar(8),@date,112) + ''' and
b.date = ''' + convert(varchar(8),@datePrev,112) + ''''
exec(@DynamicSQL)
set @datePrev = @date
set @DynamicSQL =
'select @date2 = (select coalesce(min(date),''21000101'') from ' + @indexTable +
' where date > ''' + convert(varchar(8),@datePrev,112) + ''')'
EXECUTE sp_executesql @DynamicSQL, N'@date2 date OUTPUT', @date2=@date OUTPUT
if @date = '21000101' begin set @run = 0 end
end
------------------------
-- Backwards Compatability
------------------------
if @retType = 'USD'
begin
set @DynamicSQL = 'alter table ' + @constTable +' drop column priceRet_local'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @constTable +' drop column totalRet_local'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @constTable +' drop column fxRet'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column priceRet_local'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column totalRet_local'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column fxRet'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column priceIdxLevel_local'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column trIdxLevel_local'
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @constTable +'.priceRet_USD'',''priceRet'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @constTable +'.totalRet_USD'',''totalRet'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @indexTable +'.priceRet_usd'',''priceRet'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @indexTable +'.totalRet_usd'',''totalRet'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @indexTable +'.priceIdxLevel_USD'',''priceIdxLevel'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @indexTable +'.trIdxLevel_USD'',''trIdxLevel'',''COLUMN'''
exec(@DynamicSQL)
end
if @retType = 'local'
begin
set @DynamicSQL = 'alter table ' + @constTable +' drop column priceRet_USD'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @constTable +' drop column totalRet_USD'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @constTable +' drop column fxRet'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column priceRet_USD'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column totalRet_USD'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column fxRet'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column priceIdxLevel_USD'
exec(@DynamicSQL)
set @DynamicSQL = 'alter table ' + @indexTable +' drop column trIdxLevel_USD'
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @constTable +'.priceRet_local'',''priceRet'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @constTable +'.totalRet_local'',''totalRet'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @indexTable +'.priceRet_local'',''priceRet'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @indexTable +'.totalRet_local'',''totalRet'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @indexTable +'.priceIdxLevel_local'',''priceIdxLevel'',''COLUMN'''
exec(@DynamicSQL)
set @DynamicSQL = 'sp_rename ''' + @indexTable +'.trIdxLevel_local'',''trIdxLevel'',''COLUMN'''
exec(@DynamicSQL)
end
GOTO clean_exit
------------------------
-- Error codes
------------------------
error_one:
print('Input or output table names should not include schema!')
return
error_two:
print('Input frequency or return type is unknown!')
return
error_three:
print('The weighting table does not exist!')
return
error_four:
print('The security and date combinations in the weighting table are not unique.')
return
error_five:
print('The weighting table is not correctly specified.
It needs a date column called date,
a char column called fs_perm_sec_id,
and a numeric column called weight.')
return
clean_exit:
print('Returns procedure is done.')
You will need to use cursr.fetchall() to get all the rowsets, and use cursr.nextset() to get the second one. 您将需要使用cursr.fetchall()获取所有行集,并使用cursr.nextset()获取第二个行集。
See details here: https://www.easysoft.com/developer/languages/python/examples/CallSPMultiResultSet.html 在此处查看详细信息: https : //www.easysoft.com/developer/languages/python/examples/CallSPMultiResultSet.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.