MYSQL LOAD DATA INFILE 语句适用于 workbench,但不适用于 python

MYSQL LOAD DATA INFILE statement works in workbench, but not python

我正在试验 MySQL 并使用 Fannie Mae 抵押数据进行数据分析。为此,我创建了两个 table(“perf”和“acq”)和几个 python 函数。我首先删除集合和 tables(如果它们存在),然后创建集合 (mortgage_analysis) 和两个 tables。然后我构建了一个文件列表,这些文件对应于我要执行的分析年数。所有这些都很好。

然后我使用以下函数将 Fannie Mae 的 perf 和 acq 文本文件中的数据加载到 tables。相同的函数用于加载两个 tables。它每次都与“perf”table 一起工作,永远不会与“acq”table 一起工作。如果我采用 SQL 语句并在 mySQL workbench 中执行它们,这些语句每次都有效。我很困惑,需要一些帮助。

在 workbench 中有效但在 Python 中无效的 SQL 语句是:

LOAD DATA  INFILE '/Users/<my user name>/github/mortgage-analysis-example/data/text/acq/Acquisition_2000Q1.txt' 
INTO TABLE acq 
FIELDS TERMINATED BY '|' 
LINES TERMINATED BY '\n' 
(loan_id, orig_channel, seller_name, orig_interest_rate, orig_upb, orig_loan_term, 
 orig_date, first_pay_date, orig_ltv, orig_cltv, num_borrowers, dti, 
 borrower_credit_score, first_home_buyer, loan_purpose, property_type, num_units, 
 occupancy_status, property_state, zip, mortgage_insurance_percent, product_type, 
 coborrow_credit_score, mortgage_insurance_type, relocation_mortgage_ind);

加载这个的python函数是:

def loadTable(env_in, template, file_list):
    # env_in: (i know, uck global variable, holds info for this notebook common to all functions
    # template: SQL template file
    # file_list: python list element with fully qualified file names to use with SQL statement 
    env = env_in # environment info
    from mysql.connector import Error
    _file = open(env["base_path"]+env["path_separator"]+template, "r")
    _template = _file.readlines()
    try:
        conn = mysql.connector.connect(host=env["mySQL"]["host"],user=env["mySQL"]["user"], passwd=env['pw'])
        if conn.is_connected():
            print('Connected to MySQL database')
    except Error as e:
            print(e)
    cursor = conn.cursor()
    cursor.execute("USE mortgage_analysis;")
    cursor.execute("SET SESSION sql_mode = '';")
    print("starting table load")
    t0 = time.time()
    res = []
    for _file in file_list:
        _sql = _template[0].format(_file)
        print(f"\n{_sql}\n")
        try:
            res = cursor.execute(_sql)
            warn = cursor.fetchwarnings()
            #print(f"warn: {warn}")
        except Error as e:
            print(f"{_sql} \n{e}")

    t1 = time.time()
    print(f"Years: {env['years']} Table load time: {t1-t0}") 
    conn.close
    return env

没有发现错误(尝试总是有效)并且没有生成警告(fetchwarnings 总是空的)。

用于创建两个 table 的 SQL 语句是:

DROP TABLE IF EXISTS acq;
CREATE TABLE acq (id DOUBLE AUTO_INCREMENT, loan_id DOUBLE, orig_channel VARCHAR(255), seller_name VARCHAR(255), orig_interest_rate DOUBLE, orig_upb DOUBLE, orig_loan_term DOUBLE, orig_date VARCHAR(255), first_pay_date VARCHAR(255), orig_ltv DOUBLE, orig_cltv DOUBLE, num_borrowers DOUBLE, dti DOUBLE, borrower_credit_score DOUBLE, first_home_buyer VARCHAR(255), loan_purpose VARCHAR(255), property_type VARCHAR(255), num_units DOUBLE, occupancy_status VARCHAR(255), property_state VARCHAR(255), zip DOUBLE, mortgage_insurance_percent DOUBLE, product_type VARCHAR(255), coborrow_credit_score DOUBLE, mortgage_insurance_type DOUBLE, relocation_mortgage_ind VARCHAR(255), PRIMARY KEY (id));
DROP TABLE IF EXISTS perf;
CREATE TABLE perf (id DOUBLE AUTO_INCREMENT, loan_id DOUBLE, monthly_reporting_period VARCHAR(255), servicer VARCHAR(255), interest_rate DECIMAL(6,3), current_actual_upb DECIMAL(12,2), loan_age DOUBLE, remaining_months_to_legal_maturity DOUBLE, adj_remaining_months_to_maturity DOUBLE, maturity_date VARCHAR(255), msa DOUBLE, current_loan_delinquency_status DOUBLE, mod_flag VARCHAR(255), zero_balance_code VARCHAR(255), zero_balance_effective_date VARCHAR(255), last_paid_installment_date VARCHAR(255), foreclosed_after VARCHAR(255), disposition_date VARCHAR(255), foreclosure_costs DOUBLE, prop_preservation_and_reair_costs DOUBLE, asset_recovery_costs DOUBLE, misc_holding_expenses DOUBLE, holding_taxes DOUBLE, net_sale_proceeds DOUBLE, credit_enhancement_proceeds DOUBLE, repurchase_make_whole_proceeds DOUBLE, other_foreclosure_proceeds DOUBLE, non_interest_bearing_upb DOUBLE, principal_forgiveness_upb VARCHAR(255), repurchase_make_whole_proceeds_flag VARCHAR(255), foreclosure_principal_write_off_amount VARCHAR(255), servicing_activity_indicator VARCHAR(255), PRIMARY KEY (id));

我测试了代码,我必须做一些更改才能让它工作。

不要更改 sql_mode。我没有收到任何错误,我能够在不影响 sql_mode.

的情况下加载数据

我使用的测试数据:

1|2|name1|3|4|4|date|date|5|6|7|8|9|buyer|purpose|type|10|status|state|11|12|type|13|14|ind
1|2|name2|3|4|4|date|date|5|6|7|8|9|buyer|purpose|type|10|status|state|11|12|type|13|14|ind

我劝你选择更合适的数据类型。您应该几乎从不在 MySQL 中使用 FLOAT 或 DOUBLE,除非您要存储科学测量值或其他内容。绝对不是货币单位或整数。

我不会使用 VARCHAR 来存储日期。 MySQL 具有 DATE 和 DATETIME,这些确保日期是 well-formed,因此您可以进行比较、排序、日期算术等

如果您有错误建议您放宽 sql_mode 并允许无效查询或无效数据,我建议您改为修复数据。即使你加载了数据,如果你允许 non-strict SQL 模式,它也有变成垃圾的风险。

代码更改:

我没有使用 format() 来尝试将文件名插入查询模板,而是使用了一个查询参数。删除带有 _template[0].format(_file) 的行,改为使用:

res = cursor.execute(_template, [_file])

但模板必须将占位符放在不带引号的位置:

正确:

LOAD DATA INFILE %s INTO TABLE...

不正确:

LOAD DATA INFILE '%s' INTO TABLE...

最后,Python中的数据更改默认不提交。也就是说,您可以插入数据,然后当您使用 conn.close 时,未提交的更改将被丢弃。所以我加了一行:

conn.commit()

我在执行 SQL.

后将它放在 try/catch 块中

加载数据成功。请注意,我必须对您的输入数据做出假设,因为您没有共享样本。我不知道您的文件是否真的 well-formed 具有正确的字段分隔符和行分隔符。但我假设它是,因为你说它在 MySQL Workbench.

中有效