【问题标题】:Using SQL Alchemy to Import Data and Replace Given a Condition使用 SQL Alchemy 导入数据并替换给定条件
【发布时间】:2018-11-28 23:55:09
【问题描述】:

下面是我的 selenium 网络爬虫的最后一部分,它循环遍历此 website page 的不同选项卡,选择“导出数据”按钮,下载数据,添加“yearid”列,然后将数据加载到MySQL 表。

    df = pd.read_csv(desired_filepath)
    df["yearid"] = datetime.today().year
    df[df.columns[df.columns.str.contains('%')]] = \
    (df.filter(regex='%')
     .apply(lambda x: pd.to_numeric(x.str.replace(r'[\s%]', ''),
                                    errors='coerce')))
    df.to_csv(desired_filepath)

    engine = create_engine("mysql+pymysql://{user}:{pw}@localhost/{db}"
                           .format(user="walker",
                                   pw="password",
                                   db="data"))
    df.to_sql(con=engine, name='fg_test_hitting_{}'.format(button_text), if_exists='replace')

time.sleep(10)
driver.quit()

一切都很好,但我想将数据导入 MySQL 表并仅在 yearid=2018 时替换。有谁知道在特定条件下是否可以加载数据和替换?提前致谢!

【问题讨论】:

  • 你能在加载到mysql之前过滤数据子集吗? df[df['yearid']==2018].to_sql(...) 之类的东西?
  • 很遗憾,您不能这样做,因为当前表有多年的数据。刮刀一次只提取一年的数据,所以当它替换时,它会删除其他年份的数据,而我只会被 2018 年的数据卡住。
  • 试试if_exists='append'
  • 感谢推荐!不幸的是,我也不能这样做,因为我提取的数据是棒球赛季的总数。如果我附加,我将拥有一个数据库,其中包含每个球员的多个赛季总数。我需要替换具有 yearid=2018 的每一行,以便每个玩家在该特定年份都有一个赛季总数。
  • 一种方法可能是首先输入所有前几年的数据。然后让您的更新过程执行以下操作:1) 删除所有 2018 年数据,2) 重新创建更新的 2018 年数据,以及 3) 使用 if_exists='append' 放回更新的 2018 年数据。

标签: python mysql pandas selenium sqlalchemy


【解决方案1】:

我认为与其从您的表中删除,不如让 MySQL 处理替换。您可以通过使用新数据创建一个临时表,替换为永久表,然后删除临时表来做到这一点。这里最大的警告是您需要在表中设置键(理想情况下只需一次)。我不知道你的关键领域是什么,所以很难在这方面提供帮助。

将注释行替换为:

# df.to_sql(con=engine, name='fg_test_hitting_{}'.format(button_text), if_exists='replace')
conn = engine.connect()

# should fail if temporary table already exists (we want it to fail in this case)
df.to_sql('fg_test_hitting_{}_tmp'.format(button_text), conn)

# Will create the permanent table if it does not already exist (will only matter in the first run)
# note that you may have to create keys here so that mysql knows what constitutes a replacement
conn.execute('CREATE TABLE IF NOT EXISTS fg_test_hitting_{} LIKE fg_test_hitting_{}_tmp;'.format(button_text, button_text))

# updating the permanent table and dropping the temporary table
conn.execute('REPLACE INTO fg_test_hitting_{} (SELECT * FROM fg_test_hitting_{}_tmp);'.format(button_text, button_text))
conn.execute('DROP TABLE IF EXISTS fg_test_hitting_{}_tmp;'.format(button_text))

【讨论】:

  • 让我看看我是否理解你的逻辑。您使用所有更新的 2018 年数据创建一个临时表,然后将临时表中的所有数据插入到我的永久表中,其中包含多年的数据(即 2015、2016、2017、2018),然后删除临时表桌子。上面的代码怎么会知道删除旧的 2018 年数据,然后用永久表中的新 2018 年数据替换它?再次感谢您的详细回复!非常感谢您的洞察力!
  • 它会根据您分配的键知道用什么替换什么。所以说已经有一行包含 Bob-2018 的一些数据。如果 Bob-2018 的新数据行是 REPLACE INTO 表,它将覆盖现有行而不是创建新行。它可能超出了这个问题的范围来解释键的工作原理 - 要点是每个键只能有一行。因此,该表中 Bob-2018 的行数永远不会超过一行。最佳实践是控制数据库中的那些结构,而不是输入代码。
  • 哦,这很有道理。感谢您的澄清!每个玩家都有自己的“playerid”,我可以将其指定为主键,这样覆盖应该可以工作。再次感谢!
  • 不客气。如果您发现这解决了您的问题,请随时接受此答案。
【解决方案2】:

正如@Leo 在 cmets 中所述,首先删除您要更新的那部分数据(从 MySQL 表中),然后将其保存到 MySQL 表中:

conn = engine.connect()
cur = conn.cursor()
...

cur.execute('delete from fg_test_hitting_{} where yearid=?'.format(button_text), 
            (pd.datetime.today().year,))

df.to_sql(con=engine, name='fg_test_hitting_{}'.format(button_text), if_exists='replace')

【讨论】:

  • 感谢 MaxU!这很好用。由于某种原因,游标无法连接到 MySQL,但我将cur.execute 更改为conn.execute 并删除了pd.datetime.today().year,这似乎有效。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多