通过os.system在Python中命令行执行MySQL查询
我正在尝试把一个循环中的“天”这个值传递到一个SQL语句里,然后再通过MySQL命令行执行这个语句,使用-e选项。
我不能使用数据库模块或者其他Python库来访问MySQL,必须通过命令行来完成。而且,看起来我可能需要把“天”转换成字符串,然后再拼接到SQL语句里?
#!/usr/bin/python
import datetime
a = datetime.date(2009, 1, 1)
b = datetime.date(2009, 7, 1)
one_day = datetime.timedelta(1)
day = a
while day <= b:
print day
sql="SELECT Date,SUM(CostUsd) FROM Stats d WHERE d.Date = " + day + " GROUP BY Date"
print "SELECT Date,SUM(CostUsd) FROM Stats d WHERE d.Date = " + day + " GROUP BY Date"
os.system('mysql -h -sN -u -p -e " + sql + " > /home/output/DateLoop-" + day + ".txt db')
day += one_day
有没有可能把SQL语句设置成一个输入文件,然后把“天”作为字符串传递给它?这个查询可能会变得更复杂,甚至需要多个查询,这样传递字符串可能会变得麻烦。
我对任何想法都持开放态度,只要这个查询能接受日期作为输入,输出文件的名字也能用同样的日期,并且能通过命令行的MySQL客户端来完成。
3 个回答
0
你可以把mysql的查询模板保存在一个配置文件里,然后用ConfigParser来读取它。
这个配置文件的样子大概是这样的:
[mysql query configuration]
dbhost =
db =
username = guest
password =
[query template]
template = SELECT Date, SUM(CostUsd).......
或者你也可以把它存到一个单独的文件里,然后用标准的open(filename).read等方法来读取。如果你觉得将来的查询会变得更复杂,使用配置文件的方法可能会更容易管理和理解,不过其实差别不大。
如果你想把日期作为参数传入,可以用sys.argv,或者使用像optparse这样的库。
1
试着对生成的字符串进行明确的格式化和引用:
sql = "....WHERE d.Date = '" + date.isoformat() + "' GROUP BY ..."
在os.system调用中的引号看起来很乱,重定向的样子也很奇怪(如果这不是打错字的话)
os.system("mysql db -h -sN -u -p -e '" + sql + "' > /home/output/DateLoop-" + day + ".txt")
3
下面的代码可能对你有帮助。它并不是特别复杂,故意写得很简单。这并不是很多程序员处理这个问题的方式,但在没有更多信息的情况下,它似乎能满足你的需求。
我还假设你是刚接触python的,如果我错了,可以忽略这段话。
- 允许在命令行中传递数据库的登录信息、输出目录和日期(开始和结束)。
- 使用subprocess代替os.system。subprocess提供了更好的方法来从python调用外部程序。这个代码使用了最简单的调用方式;call(),因为它和os.system()很相似。
- 使用optparse来处理命令行参数。虽然代码会长一些,内容也会多一些,但这样你将来添加和修改参数处理会更容易。而且代码的逻辑也比较清晰(代码通常被阅读的次数远多于被写的次数)。
- 命令行设置只有在脚本执行时才会运行,因为它在
__main__
块内。由于脚本的“逻辑”在main()方法中,你也可以从其他地方导入它,并提供选项对象(和参数列表)。
如果你能去掉每个日期输出到单独文件的需求,你可以让数据库引擎计算SUM()并按日期分组。这样你可以在一次数据库调用中得到所有的总和,这样会更快,代码也会更简单。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import datetime
import os
import subprocess
from optparse import OptionParser
SQL = """SELECT d.Date, SUM(d.CostUsd) FROM Stats d WHERE d.Date = '%s' GROUP BY d.Date"""
def get_stats(options, dateobj):
"""Return statistics for the date of `dateobj`"""
_datestr = dateobj.strftime('%Y-%m-%d')
sql = SQL % _datestr
filepath = os.path.join(options.outdir, 'DateLoop-%s.txt' % _datestr)
return subprocess.call('mysql -h %s -u %s -p -sN -e "%s" db > %s' % (options.dbhost, options.dbuser, sql, filepath), shell=True)
def main(options, args):
""""""
_date = options.startdate
while _date <= options.enddate:
rs = get_stats(options, _date)
_date += datetime.timedelta(days=1)
if __name__ == '__main__':
parser = OptionParser(version="%prog 1.0")
parser.add_option('-s', '--startdate', type='string', dest='startdate',
help='the start date (format: yyyymmdd)')
parser.add_option('-e', '--enddate', type='string', dest='enddate',
help='the end date (format: yyyymmdd)')
parser.add_option('--output', type='string', dest='outdir', default='/home/output/',
help='target directory for output files')
parser.add_option('--dbhost', type='string', dest='dbhost', default='myhost',
help='SQL server address')
parser.add_option('--dbuser', type='string', dest='dbuser', default='dbuser',
help='SQL server user')
options, args = parser.parse_args()
## Process the date args
if not options.startdate:
options.startdate = datetime.datetime.today()
else:
try:
options.startdate = datetime.datetime.strptime('%Y%m%d', options.startdate)
except ValueError:
parser.error("Invalid value for startdate (%s)" % options.startdate)
if not options.enddate:
options.enddate = options.startdate + datetime.timedelta(days=7)
else:
try:
options.enddate = datetime.datetime.strptime('%Y%m%d', options.enddate)
except ValueError:
parser.error("Invalid value for enddate (%s)" % options.enddate)
main(options, args)