Python csv_writer如何无需to_char统一格式化Oracle日期列?
你遇到的问题其实是cx_Oracle的默认数据转换行为导致的——当你从Oracle获取DATE类型数据时,驱动会自动把它转换成Python的datetime对象,而不是遵循你设置的会话NLS_DATE_FORMAT格式。所以哪怕你执行了ALTER SESSION,csv_writer在写入时还是会用Python datetime对象默认的字符串格式(也就是你看到的YYYY-MM-DD HH:MM:SS)来输出。
不用每个列都写to_char的话,有两种比较优雅的批量处理方法:
方法1:让cx_Oracle直接返回格式化后的日期字符串
你可以在创建Oracle连接的时候,直接指定nls_date_format参数,这样驱动会把DATE类型的数据直接转换成你想要的格式字符串,而不是datetime对象。修改你的连接代码就行:
import csv import cx_Oracle file_handle = open("test.csv", "w") csv_writer = csv.writer(file_handle, dialect="excel", lineterminator='\n', delimiter=';', quoting=csv.QUOTE_NONNUMERIC) # 连接时指定NLS_DATE_FORMAT,直接返回格式化后的字符串 conn = cx_Oracle.connect(connectionstring, nls_date_format='DD.MM.YYYY') cur = conn.cursor() # 不需要再执行ALTER SESSION语句 cur.execute("select attr4 from aTable") rows = cur.fetchmany(16000) while len(rows) > 0: csv_writer.writerows(rows) rows = cur.fetchmany(16000) cur.close() conn.close() file_handle.close()
这样查询返回的attr4就是直接格式化好的DD.MM.YYYY字符串,写入CSV时就符合你的要求了。如果需要带时间的格式,把nls_date_format改成'DD.MM.YYYY HH24:MI:SS'即可。
方法2:批量转换行中的datetime对象
如果你不想修改连接参数,也可以在写入CSV之前,遍历每一行的数据,把所有datetime类型的元素转换成指定格式的字符串。比如写一个辅助函数来处理行数据:
import csv import cx_Oracle from datetime import datetime def format_datetime_rows(rows, date_format='DD.MM.YYYY'): formatted_rows = [] # 映射Oracle格式到Python的strftime格式 py_date_format = '%d.%m.%Y' if date_format == 'DD.MM.YYYY HH24:MI:SS': py_date_format = '%d.%m.%Y %H:%M:%S' for row in rows: formatted_row = [] for item in row: if isinstance(item, datetime): formatted_row.append(item.strftime(py_date_format)) else: formatted_row.append(item) formatted_rows.append(formatted_row) return formatted_rows file_handle = open("test.csv", "w") csv_writer = csv.writer(file_handle, dialect="excel", lineterminator='\n', delimiter=';', quoting=csv.QUOTE_NONNUMERIC) conn = cx_Oracle.connect(connectionstring) cur = conn.cursor() cur.execute("select attr4 from aTable") rows = cur.fetchmany(16000) while len(rows) > 0: # 先转换日期格式再写入CSV formatted_rows = format_datetime_rows(rows) csv_writer.writerows(formatted_rows) rows = cur.fetchmany(16000) cur.close() conn.close() file_handle.close()
这个方法的好处是可以灵活处理不同的日期格式,甚至同时处理DATE和TIMESTAMP类型的数据,只要是Python的datetime对象都会被统一转换。
补充说明
为什么ALTER SESSION没效果?因为cx_Oracle默认会将Oracle的DATE/TIMESTAMP类型映射为Python的datetime对象,这个转换过程是驱动内部完成的,不会受会话的NLS_DATE_FORMAT影响。只有当你配置驱动返回字符串类型时(比如方法1的设置),NLS格式才会生效。
内容的提问来源于stack exchange,提问作者DropNameHere

