0

任务规格:

使用 Python(但以参数化方式)并使用 SQL Server 代理作业将 Excel 文件导入 MSSQL 数据库。增加了设置参数值和/或从 SQL(查询或 SP)运行作业步骤的要求。并且不使用 Access 数据库引擎和/或任何使用此类驱动程序的代码(在任何包装中)。

4

1 回答 1

0

第一的。让我们先做一些准备工作。我们需要设置一些 PowerShell 设置。以管理员身份运行 windows PowerShell 并执行以下操作:

Set-ExecutionPolicy -ExecutionPolicy Unrestricted

第二。为清楚起见的一些假设。这些是:1a。您的机器上至少安装并运行了一个 SQL2017 或更高版本(开发者/企业/标准版)的实例。1b。您尚未引导安装此 SQL 实例以排除集成服务 (SSIS)。1c。存在正在运行的 SQL Server 代理,绑定到此 SQL 实例。1d。您安装了一些 SSMS。2a。至少有一个数据库附加到这个实例(如果没有创建一个 - 请不要在这个练习中使用内存中的文件组,我没有测试过这些)。2b。没有将所有数据更改记录在指定表中的数据库级 DML 触发器。3. 这个数据库没有有效的服务器审计规范记录我们所做的一切。4. 未启用复制(我的意思是正确的 MSSQL 复制功能不像 3rd 方应用程序的脚本)。对于 2b 和 3,这只是因为我没有在这些设备上进行测试,但对于 4 号,它 defo 将无法使用它。5.您是通过 Windows 身份验证进入所选 SQL 实例的,并且您的实例登录和数据库映射和权限至少足以创建表和基本内容。

第三。我们将需要某种 Python 脚本来执行此操作,对吗?好吧,让我们做一个。

import pandas as pd
import sqlalchemy as sa
import urllib
import sys
import warnings
import os
import re
import time

#COMMAND LINE PARAMETERS

server = sys.argv[1]
database = sys.argv[2]
ExcelFileHolder = sys.argv[3]
SQLTableName = sys.argv[4]

#END OF COMMAND LINE PARAMETERS

excel_sheet_number_left_to_right = 0

warnings.filterwarnings('ignore')

driver = "SQL Server Native Client 11.0"
params = "DRIVER={%s};SERVER=%s;DATABASE=%s;Trusted_Connection=yes;QuotedID=Yes;" % (driver, server, database) #added the explicit "QuotedID=Yes;" to ensure no issues with column names
params = urllib.parse.quote_plus(params) #urllib.parse.quote_plus for Python 3
engine = sa.create_engine("mssql+pyodbc:///?odbc_connect=%s?charset=utf8" % params) #charset is cool to have here
conn = engine.connect()

def execute_sql_trans(sql_string, log_entry):
    with conn.begin() as trans:
        result = conn.execute(sql_string)
    if len(log_entry) >= 1:
        log.write(log_entry + "\n")
    return result

excelfilesCursor = {}

def process_excel_file(excelfile, excel_sheet_name, tableName, withPyIndexOrSQLIndex, orderByCandidateFields):
    withPyIndexOrSQLIndex = 0

    excelfilesCursor.update({tableName: withPyIndexOrSQLIndex})

    df = pd.read_excel(open(excelfile,'rb'), sheet_name=excel_sheet_name)
    now = time.time()
    mlsec = repr(now).split('.')[1][:3]
    log_string = "Reading file \"" + excelfile + "\" to memory: " + str(time.strftime("%Y-%m-%d %H:%M:%S.{} %Z".format(mlsec), time.localtime(now))) + "\n"
    print(log_string)
    
    df.to_sql(tableName, engine, if_exists='replace', index_label='index.py')

    now = time.time()
    mlsec = repr(now).split('.')[1][:3]
    log_string = "Writing file \"" + excelfile + "\", sheet " +str(excel_sheet_name)+ " to SQL instance " +server+ ", into ["+database+"].[dbo].["+tableName+"]: " + str(time.strftime("%Y-%m-%d %H:%M:%S.{} %Z".format(mlsec), time.localtime(now))) + "\n"
    print(log_string)

def convert_datetimes_to_dates(tableNameParam):
    sql_string = "exec [convert_datetimes_to_dates] '"+tableNameParam+"';"
    execute_sql_trans(sql_string, "")

process_excel_file(ExcelFileHolder, excel_sheet_number_left_to_right, SQLTableName, 0, None)

sys.exit(0)

好的,您可能会也可能不会注意到我的脚本包含一些额外的定义,我有时会为了方便而使用它们,您不妨忽略它们。将 python 脚本保存在好地方,比如 C:\PythonWorkspace\ExcelPythonToSQL.py 另外,不用说你的 venv 中需要一些 py 模块。那些你还没有的,你显然需要 pip 安装它们。

第四。连接到您的数据库、SSMS 等并创建一个新的代理作业。我们称之为“ExcelPythonToSQL”。新步骤,我们称之为“PowerShell 参数化脚本”。将类型设置为 PowerShell。并将此代码放入其中:

$pyFile="C:\PythonWorkspace\ExcelPythonToSQL.py"
$SQLInstance="SomeMachineName\SomeNamedSQLInstance"
#or . or just the computername or localhost if your SQL instance is a default instance i.e. not a named one.
$dbName="SomeDatabase"
$ExcelFileFullPath="C:\Temp\ExampleExcelFile.xlsx"
$targetTableName="somenewtable"

C:\ProgramData\Miniconda3\envs\YOURVENVNAMEHERE\python $pyFile $SQLInstance $dbName $ExcelFileFullPath $targetTableName

保存步骤和作业。

现在让我们把它包裹在更容易处理的东西上。因为请记住,此作业和步骤不像 SSIS 步骤,您可能会在其配置选项卡中更改参数值。您不希望每次都对作业和步骤进行属性化并指定不同的 Excel 文件或目标表。所以。

啊也,做我一个坚实的,做这个小动作。对代码做一些小改动,然后做一个新查询窗口的脚本而不是 OK。这样我们就可以捕获工作的指导,而无需查询它。

所以现在。像这样创建一个 SP:

use [YourDatabase];
GO

create proc [ExcelPythonToSQL_amend_job_step_params](   @pyFile nvarchar(max),
                                                        @SQLInstance nvarchar(max),
                                                        @dbName nvarchar(max),
                                                        @ExcelFileFullPath nvarchar(max),
                                                        @targetTableName nvarchar(max)='somenewtable'
)
as
begin

declare @sql nvarchar(max);

set @sql = '
exec msdb.dbo.sp_update_jobstep @job_id=N''7f6ff378-56cd-4a8d-ba40-e9057439a5bc'', @step_id=1,
        @command=N''
$pyFile="'+@pyFile+'"
$SQLInstance="'+@SQLInstance+'"
$dbName="'+@dbName+'"
$ExcelFileFullPath="'+@ExcelFileFullPath+'"
$targetTableName="'+@targetTableName+'"

C:\ProgramData\Miniconda3\envs\YOURVENVGOESHERE\python $pyFile $SQLInstance $dbName $ExcelFileFullPath $targetTableName''
';
--print @sql;
exec sp_executesql @sql;

end

但在里面你必须更换两件东西。一,您通过执行我之前描述的技巧找到的代理作业的全局唯一标识符,是的,带有脚本到新查询窗口的那个。第二,您必须填写您的 Python venv 的名称,替换代码中的 YOURVENVGOESHERE 一词。凉爽的。

现在,我们可以通过一个简单的脚本进行游戏测试。让我们在一个新的查询窗口中有这样的东西:

use [YourDatabase];
GO

--to set parameters
exec [ExcelPythonToSQL_amend_job_step_params]   @pyFile='C:\PythonWorkspace\ExcelPythonToSQL.py',
                                                @SQLInstance='.',
                                                @dbName='YourDatabase',
                                                @ExcelFileFullPath='C:\Temp\ExampleExcelFile.xlsx',
                                                @targetTableName='somenewtable';

--to execute the job
exec msdb.dbo.sp_start_job N'ExcelPythonToSQL', @step_name = N'PowerShell parametrized script';

--let's test that the table is there and drop it.
if object_id('YourDatabase..somenewtable') is not null
begin
    select 'Table was here!' [test: table exists?];
    drop table [somenewtable];
end
else select 'NADA!' [test: table exists?];

您可以运行设置参数部分,然后执行,小心然后等待几秒钟,像在这个脚本中调用 sp_start_job 是异步的。然后运行测试脚本进行清理并确保它已经进入。

而已。显然,许多变化是可能的。就像在作业步骤中一样,我们可以改为调用批处理文件,我们可以调用 powershell .ps1 文件并在其中包含参数,还有很多其他方法。我只是在这篇文章中描述了一个。

于 2021-02-19T23:04:54.047 回答