Python运行SQL存储过程



我对SQL世界相对来说还是个新手。我有一个有人写的存储过程,我只想通过我的Python执行它。

现在,存储的proc接受一个表作为输入,并生成两个输出表。它运行良好,我已经在SQL Studio中使用进行了尝试

exec CalcIdxRet_v4  INPUT_TABLE

而且效果非常好。然而,当我在Python中运行它时。它工作良好,没有错误,但它只生成2个输出表中的一个。由于没有错误,仍然只有一个输出表,这让我感到困惑

有人能投一些光吗。非常感谢

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()

这是存储的过程

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.')

您需要使用cursr.fetchall((来获取所有行集,并使用cursr.nextset((来获得第二个行集。

请在此处查看详细信息:https://www.easysoft.com/developer/languages/python/examples/CallSPMultiResultSet.html

相关内容

  • 没有找到相关文章

最新更新