我管理了许多ETL工作,其中包括与Facebook图形和Google Doubleclick等API的连接,这些API使用大型数字唯一标识符。我们使用Oracle数据库来暂存这些数据并将其与我们自己的数据相结合。我一直在运行的问题是,当我通过SQLAlchemy(使用cx_Oracle)将这些大型数字ID插入数字列时,有效数字会被截断。
实施例: 1234567890726531成为1234567890726530
解决方法: 为了解决这个问题,我一直在使用VARCHAR2数据类型来保存ID的文本表示,因为它保留了所有有效数字。
我认为它与这些错误线程有关:
(我无法复制这些线程中提到的游标代码以在我的情况下测试该解决方案)
Facebook示例的一些示例代码(或从中提取的相关部分)如下:
[...code that populate the "buffer" list]
schema,table_name = 'some_schema','some_table'
engine = create_engine(enginestr)
metadata = MetaData()
table = Table(table_name,
metadata,
schema=schema,
autoload=True,
autoload_with=self.engine)
buf=[]
for i in buffer:
d={
'id':i[1]['id'],
'id_char':i[1]['id'],
'name':i[1]['name'],
'status':i[1]['status'],
'page_id':i[0]['id']
}
buf+=[d]
engine.execute(table.insert(),buf)
缓冲区条目如下所示:
[(<Page> {
"id": "FacebookPageName"
}, <LeadgenForm> {
"id": "123456789012345",
"leadgen_export_csv_url": "https://www.facebook.com/ads/lead_gen/export_csv/?id=123456789012345&type=form&source_type=graph_api",
"locale": "en_US",
"name": "Leadgen Form Name",
"status": "ACTIVE"
})]
该表基本上是这样的:
create table some_schema.some_table (
id number primary key,
name varchar2(512 char),
status varchar2(30 char),
updated timestamp with time zone default systimestamp,
id_char varchar2(64 char)
);
运行上面的代码后,这个SQL的结果应该是什么
select to_char(t.id) ,t.id_char from some_schema.some_table t where t.id<>t.id_Char;
然而,它确实返回结果(略微改变以保护隐私)
NUMERIC_ID ID_CHAR
1234567890726530 1234567890726531
1234567890585300 1234567890585296
1234567890103880 1234567890103882
1234567890185790 1234567890185794
1234567890359660 1234567890359664
1234567890793130 1234567890793131
1234567890250270 1234567890250272
1234567890467220 1234567890467223
1234567890905240 1234567890905241
1234567890369260 1234567890369255
这个DML更新它以显示预期,因为它向我展示了问题在于python方面:
update some_schema.some_table t set t.id = t.id_char where t.id<>t.id_Char;
有没有更好的方法来处理:
这样我的大数值不会被截断?现在使用ID的字符串类型容器似乎正在工作,但就每行存储空间而言不是最好的,这对于更大的数据集来说是一个问题。
补充资料:
编辑:
根据Anthony Tuininga的建议,我尝试通过cx-Oracle直接插入记录,但这并没有导致上面的舍入问题。这使我得出结论,我的问题在于我的SQLAlchemy或SQLAlchemy库本身的实现。
buf=[]
columns = ('id','id_char','name','status','page_id')
for i in buffer:
d=(
i[1]['id'],
i[1]['id'],
i[1]['name'],
i[1]['status'],
i[0]['id']
)
buf+=[d]
from ouplax.database import KEY
import cx_Oracle
config = {
'server' : 'TNSName',
'username' : 'username',
'schema' : 'some_schema',
'table_name' : 'some_table',
'columns' : ','.join(columns),
'binds' : ','.join( [':{}'.format(i) for i in range(1,len(columns)+1)] )
}
k = KEY(server=config['server'],username=config['username'],keyHeader='PYSQL') #Object for storing/retrieving credentials
connection = cx_Oracle.connect(config['username'],k.getpass(),server)
cursor = cx_Oracle.Cursor(connection)
stmt = 'truncate table {schema}.{table_name}'.format(**config)
print(stmt)
cursor.execute(stmt)
stmt = 'insert into {schema}.{table_name} ({columns}) values ({binds})'.format(**config)
print(stmt)
cursor.prepare(stmt)
cursor.executemany(None, buf)
connection.commit()
cursor.close()
connection.close()