【发布时间】:2017-08-12 07:19:46
【问题描述】:
我已经实现了一个 python 脚本,以便使用以下方案将数百万个文档(由 .NET Web 应用程序生成并且所有内容都放在一个目录中)划分到子文件夹中:年/月/批次,因为所有这些文件来自的任务最初被分成批次。
我的 python 脚本对 SQL Server 2014 执行查询,其中包含每个文档所需的所有数据,特别是创建它的月份和年份。然后它使用shutil 模块来移动 pdf。因此,我首先执行第一个查询以获取给定月份和年份的批次列表:
queryBatches = '''SELECT DISTINCT IDBATCH
FROM [DBNAME].[dbo].[WORKS]
WHERE YEAR(DATETIMEWORK)={} AND MONTH(DATETIMEWORK)={}'''.format(year, month)
然后我执行:
for batch in batches:
query = '''SELECT IDWORK, IDBATCH, NAMEDOCUMENT
FROM [DBNAME].[dbo].[WORKS]
WHERE NAMEDOCUMENTI IS NOT NULL and
NAMEDOCUMENT not like '/%/%/%/%.pdf' and
YEAR(DATETIMEWORK)={} and
MONTH(DATETIMEWORK)={} and
IDBATCH={}'''.format(year,month,batch[0])
根据 PYMSSQL 使用文档,将其记录收集到游标中。所以我继续:
IDWorksUpdate = []
row = cursor.fetchone()
while row:
if moveDocument(...):
IDWorksUpdate.append(row[0])
row = cursor.fetchone()
最后,当循环结束时,在IDWorksUpdate 中,我拥有所有 WORKS 的 PK,其文档已成功正确移动到子文件夹中。所以,我关闭光标和连接并实例化新的。
最后我表演:
subquery = '('+', '.join(str(x) for x in IDWorksUpdate)+')'
query = '''UPDATE [DBNAME].[dbo].[WORKS] SET NAMEDOCUMENT = \'/{}/{}/{}/\'+NAMEDOCUMENT WHERE IDWORK IN {}'''.format(year,month,idbatch,subquery)
newConn = pymssql.connect(server='localhost', database='DBNAME')
newCursor = newConn.cursor()
try:
newCursor.execute(query)
newConn.commit()
except:
newConn.rollback()
log.write('Error on updating documents names in database of works {}/{} of batch {}'.format(year,month,idbatch))
finally:
newCursor.close()
del newCursor
newConn.close()
今天早上我看到只有几批更新查询在数据库中执行失败,即使文档已正确移动到子目录中也是如此。
该批次有超过 55000 个文档要移动,所以 IDWorksUpdate 可能溢出,它有助于创建最终的更新查询?我认为 55000 并不是一个很大的整数列表。问题在于,在 PYMSSQL 中,我们一次不能有多个连接/游标到同一个数据库,因此我无法在移动相应文件时更新记录。所以我想创建一个文档被正确移动的作品的 PK 列表,最后用新的连接/光标更新它们。可能发生了什么?我做错了吗?
更新
我刚刚编写了一个简单的脚本来重现将要执行以更新记录的查询,这是我从 SQL Server 得到的错误:
查询处理器用尽了内部资源,无法生成查询计划。这是一个罕见的事件,仅适用于极其复杂的查询或引用大量表或分区的查询。请简化查询。如果您认为自己错误地收到了此消息,请联系客户支持服务以获取更多信息。
这是查询:
UPDATE [DBNAME].[dbo].[WORKS] SET NAMEDOCUMENT = '/2016/12/1484/'+NAMEDOCUMENT WHERE IDWORK IN (list of 55157 PKs)
事实上,这张表非常大(大约有 1400 万条记录)。但我需要该 PK 列表,因为只有文档已正确处理和移动的任务才能更新。我不能简单地运行:
UPDATE [DBNAME].[dbo].[WORKS] SET NAMEDOCUMENT = '/2016/12/1484/'+NAMEDOCUMENT WHERE YEAR(DATETIMEWORK)=2016 and
MONTH(DATETIMEWORK)=12 and IDBATCH=1484
这是因为我们的服务器受到了加密锁的攻击,我必须只处理和移动仍然存在的文件,等待其他文件被释放。 我应该将这些字符串拆分为子列表吗?怎么样?
更新 2
似乎以下可能是一个解决方案:我将 PK 列表拆分为 10000 个块(一个完全实验性的数字),然后我执行与块一样多的查询,每个块都有一个块作为子查询。
def updateDB(listID, y, m, b, log):
newConn = pymssql.connect(server='localhost', database='DBNAME')
newCursor = newConn.cursor()
if len(listID) <= 10000:
subquery = '('+', '.join(str(x) for x in listID)+')'
query = '''UPDATE [DBNAME].[dbo].[WORKS] SET NAMEDOCUMENT= \'/{}/{}/{}/\'+NAMEDOCUMENT WHERE IDWORKIN {}'''.format(y,m,b,subquery)
try:
newCursor.execute(query)
newConn.commit()
except:
newConn.rollback()
log.write('...')
log.write('\n\n')
finally:
newCursor.close()
del newCursor
newConn.close()
else:
chunksPK = [listID[i:i + 10000] for i in xrange(0, len(listID), 10000)]
for sublistPK in chunksPK:
subquery = '('+', '.join(str(x) for x in sublistPK)+')'
query = '''UPDATE [DBNAME].[dbo].[WORKS] SET NAMEDOCUMENT= \'/{}/{}/{}/\'+NAMEDOCUMENT WHERE IDWORK IN {}'''.format(y,m,b,subquery)
try:
newCursor.execute(query)
newConn.commit()
except:
newConn.rollback()
log.write('Could not execute partial {}'.format(query))
log.write('\n\n')
newCursor.close()
del newCursor
newConn.close()
这可能是一个好的/安全的解决方案吗?
【问题讨论】:
标签: python sql sql-server batch-processing pymssql