需要从SQL查询脚本导出中间结果

时间:2012-06-19 19:15:53

标签: sql python-3.x sql-server-2008-r2 pyodbc

我们需要创建几个SQL报告,每个报告都取决于上一个报告的结果。所有查询均采用以下形式:     选择1,     报告1,     从Select 1中选择2,     报告2,     从Select 2中选择3,     报告3,     ...等等......     报告N。

现在“N”个单独的SQL查询用于生成完整的报告集。每个查询都包含所有先前查询的SQL代码 - 这导致报告1完成“N”次,报告2完成“N - 1”次,等等。导致多个报告中相同选择语句的无意义重复导致性能问题。

我们如何将中间报告(最好是.csv文件)导出为报告“N”生成的报告,从而消除了对所有其他报告的需求?


一些特定于我们案例的复杂因素:

  • 必须使用交钥匙方法,任何使用任何形式的人工干预的方法都是不可接受的。纯文本输出是必需的。
  • OBDC(特别是Python的pyobdc包)用于将SQL查询传递给SQL Server(作为游标)。 Python使用结果将报告创建为.csv文件。
  • 禁止存储SQL过程,但现有SQL代码确实使用临时表。
  • 必须将结果写入我的(客户端)计算机。我们的IM部门可能允许我们在其服务器上使用临时文件夹。
  • 似乎pyobdc只能接受一个结果集(因此需要“N”查询)。确保传回正确的结果集要求SQL查询以“SET NOCOUNT ON”开头(由pyobdc邮件列表建议)。我知道没有其他方法可以从SQL返回的多个结果集中返回/选择正确的结果集。我尝试了一个pyobdc方法(cursor.nextset)来跳过结果集,但没有返回任何内容。
  • 我考虑将报告“n - 1”的结果传递到报告“N” - 但涉及的数据量可能使这不切实际。
  • Python(3.2.2)& SQL代码是经过充分验证的生产代码。改变语言不是一个实际的选择。更改OBDC软件包是可能的,但不太可能(必须进行非常强大的案例,并且其他OBDC软件包必须能够跨平台轻松移植,并且还能够连接到Microsoft SQL Server 2008 Management Studio)。
  • 带有pydev插件的Eclipse(Helios)正用于启动启动SQL查询的Python应用程序。
  • 客户端操作系统是XP Pro Sp 3,服务器被认为是相同的。该公司计划在不久的将来将我们的机器迁移到Windows 7/8,因此可以移植到该O / S是一个因素。

1 个答案:

答案 0 :(得分:0)

以下是我自己的问题的完整解决方案(本来可以更好地称为“需要从SQL查询脚本导出多个结果”)。

另见这个使用相同通用方法的问题,除了它使用硬编码的SQL字符串 - 而fetchall()而不是fetchone():

<http://stackoverflow.com/questions/273203/access-second-result-set-of-stored-procedure-with-sql-or-other-work-around-pyth?rq=1>

以及:

<http://stackoverflow.com/questions/7263781/pyodbc-return-multiple-cursors-from-stored-procedure-with-db2>

仔细研究(继承的)SQL代码的许多行显示中间报告未被保存。只返回了最后一个SQL select / report的结果(到python)。

解决方案的SQL端包括创建用于保存结果集(报告)的新表,然后在SQL代码的最后返回所有结果集。
SQL代码现在看起来像:

SET NOCOUNT ON    -- required by pyobdc (and other OBDC packages?) at start of code
SET @year  = ?    -- get OBDC (python) parameter 1
SET @month = ?    -- get parameter 2
SET @day   = ?    -- get parameter 3

DECLARE @ReportX TABLE  -- a new table, one of these for each report

--  Repeated for each report (1 to N):

INSERT INTO @ReportX    -- NEW LINE, it preserves the report
SELECT .....        -- the original report, uses the passed-in parameters

-- At the very bottom of the SQL code, add one of these lines for each report:

Select * from @ReportX   -- where X is 1 to N 

解决方案的Python 3.x方面如下:

import pyodbc  # contains cursor.execute, the interface to SQL
import csv     # creates csv.writer, used to create the CSV file

    Textfile     = open( "FileContainingSqlCode", 'r' )  
    SQL_COMMANDS = Textfile.read();     #  Get SQL code for all reports

    cursor.execute( SQL_COMMANDS, Year, Month, Day )  # do all reports using 3 parameters 

    # Create first report from the first result set

    ReportID = 1

    filename = "D:\\Report" + str( ReportID ) + ".csv"
    OutFile  = open( filename, 'w', newline= '' ) 

    Results = csv.writer( OutFile, delimiter = ',', quotechar = '"', 
                          quoting = csv.QUOTE_MINIMAL )
    while True:            
        row = cursor.fetchone()
        if not row:
            break
        Results.writerow( row )
    OutFile.close()

    # Create the 2nd through Nth reports

    while ( cursor.nextset() ) :
        ReportID += 1

        filename = "D:\\Report" + str( ReportID ) + ".csv"
        OutFile =  open( filename, 'w', newline= '' ) 

        Results = csv.writer( OutFile, delimiter = ',', quotechar = '"', 
                          quoting   = csv.QUOTE_MINIMAL )
        while True:            
            row = cursor.fetchone()
            if not row:
                break
            Results.writerow( row )            
        OutFile.close() 

# end of Python 3.x code

通过使用第二个while循环中的while(while(?? in cursor.nextset()),可以删除第一个报告部分。