【问题标题】:Python Pandas SQL Style Left Join Two Class ListsPython Pandas SQL 样式左连接两个类列表
【发布时间】:2019-05-14 06:53:30
【问题描述】:

我有两个对象列表:listA<modelA>(), listB<modelB>() 基于以下模型。

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String

Base = declarative_base()

class modelA(Base):
    __tablename__ = "TableA"

    rowID = Column(Integer, primary_key=True)
    applicationNo = Column(String)
    accountNum = Column(String)
    sanitizedAccountNum = Column(String)

class modelB(Base):
    __tablename__ = "TableB"

    rowID = Column(Integer, primary_key=True)
    applicationNo = Column(String)
    accountNum = Column(String)
    sanitizedAccountNum = Column(String)

# create SQLAlchemy engine/connection
engine = create_engine("mysql+mysqlconnector://root:usbw@localhost:3307/testDB", echo=False)
dbSession = sessionmaker(bind=engine)
session = dbSession()

# query to pull data from DB 
listA = session.query(modelA).limit(100).all()
listB = session.query(modelB).limit(100).all()

这些列表是使用 SqlAlchemy 填充的。每个表都包含近百万条记录,因此我尝试一次对部分记录执行查询。

从数据库中获取数据后,我尝试在上述两个列表上执行 SQL 样式的左连接,如下面的 SQL 查询:

SELECT a.applicationNo, a.sanitizedAccountNum
FROM listA a
LEFT JOIN listB b on b.applicationNo=a.applicationNo and b.sanitizedAccountNum=a.sanitizedAccountNum
WHERE b.applicationNo IS NULL;

我尝试过使用 Pandas 的 DataFrame,但无法获得正确的结果。

熊猫:

dfA = pd.DataFrame(listA)
dfB = pd.DataFrame(listB)
resultPD = pd.merge(dfA, dfB, how="left"), on=["applicationNo","sanitizedAccountNum"])

这里的“on”子句不起作用给我“KeyError:'applicationNo'”。如何在上述查询中为我的模型设置“加入”列?

追溯:

Traceback (most recent call last):
  File "dbna.py", line 58, in <module>
    resultPD = pd.merge(dfA, dfB, indicator="i", how="left", on=["applicationNo","sanitizedAccountNum"])
  File "C:\Users\1833\AppData\Local\Programs\Python\Python37-32\lib\site-packages\pandas\core\reshape\merge.py", line 61, in merge validate=validate)
  File "C:\Users\1833\AppData\Local\Programs\Python\Python37-32\lib\site-packages\pandas\core\reshape\merge.py", line 551, in __init__ self.join_names) = self._get_merge_keys()
  File "C:\Users\1833\AppData\Local\Programs\Python\Python37-32\lib\site-packages\pandas\core\reshape\merge.py", line 857, in _get_merge_keys rk, stacklevel=stacklevel))
  File "C:\Users\1833\AppData\Local\Programs\Python\Python37-32\lib\site-packages\pandas\core\generic.py", line 1382, in _get_label_or_level_values raise KeyError(key)
KeyError: 'applicationNo'

此外,这是否是“左连接”listA 和 listB 的最佳方式,并且仅根据提到的两个特定列从 listA 中获取不在 listB 中的记录?

编辑(示例数据): TableA Sample

TableB Sample

更新:

正如@Philip 在下面的 cmets 中所建议的那样,诀窍是将 DB 结果直接绑定到 Pandas DataFrame,而不是绑定到类(模型)列表,然后从该列表创建 DataFrame。他在 cmets 中提供的 link 成功了。

【问题讨论】:

  • 是否有不能在 SQL 中执行(左)连接的原因?每个表中有一百万条记录应该不是问题,尽管查询可能需要一些时间。如果 listA 是 SQLAlchemy 实体对象的列表,则 dfA 将有一个对象列。请生成minimal reproducible example 并包含完整的回溯。
  • 尝试在 MySQL 中执行它需要 40 多个小时并且仍在继续,因此在 DB 端执行它不是一个选项。我的最终目标是运行几个线程,每个线程有 10 万条记录。有没有办法从 dfA 单对象列访问列?我还编辑了我的帖子并为 KeyError 添加了 Traceback
  • 你能从两张表中发布几行示例数据吗?
  • 有趣的事实:Postgresql 在大约一秒钟内完成这样的查询,两个表中都有大约百万行(使用版本 10)。我正在等待 MySQL 8 完成。
  • 可能有助于 MySQL 真正到达那里的事情:使连接中使用的列 NOT NULL 并在两个表上创建一个索引,以覆盖 ON 子句中使用的列。通过这样的修改,查询在这台机器上的 MySQL 上大约 5 秒内完成(两个表中都有百万行)。

标签: python mysql pandas sqlalchemy


【解决方案1】:

一个建议可能是您在 MySql 中或作为查询创建一个视图,然后将该视图与记录限制一起使用或在 pandas 中指定 chunksize。

在数据库中创建视图:

CREATE VIEW AB_joined AS
    SELECT a.applicationNo
        ,a.sanitizedAccountNum
    FROM listA a
    LEFT JOIN listB b ON b.applicationNo = a.applicationNo
        AND b.sanitizedAccountNum = a.sanitizedAccountNum
    WHERE b.applicationNo IS NULL

并在 pandas 中使用 query1:

query1 = "SELECT * FROM AB_joined"

或者直接在 pandas 中使用 query2:

query2 = """
SELECT a.applicationNo
        ,a.sanitizedAccountNum
    FROM listA a
    LEFT JOIN listB b ON b.applicationNo = a.applicationNo
        AND b.sanitizedAccountNum = a.sanitizedAccountNum
    WHERE b.applicationNo IS NULL"""

然后用pandas读取chunksize,做你的事情并将不同的chunksize合并到一起。

result = pd.read_sql_query(query, engine, chunksize=100000)

你可以找到更多关于pandas.read_sql_query here

另一个建议是直接使用 sqlalchemy 创建视图并执行您在上面所做的操作。在我看来,选择取决于项目的目的。你可能会找到灵感create views in sqlalchemy here

您的第一个问题。我认为查询应该是这样的:

resultPD = dfA.merge(dfB, left_on="applicationNo", right_on="sanitizedLoanAccount", how="left")

您的第二个问题。左连接是只从 listA 中获取不在 listB 中的记录的方法。您还使用了 where 子句,该子句添加了应选择哪些行的附加规则。

更新我

我刚刚意识到您的数据是以字符串形式存储的。在字符串值上连接数据不是一个好习惯。如果可能的话,我建议将存储为字符串的数字转换为整数。这可以帮助避免很多问题。

更新 II - 添加数据

我已尝试使用您制作屏幕截图的数据。只需使用两行。

dfA = pd.DataFrame({
    'RowID' : [1,2],
    'ApplicationNo': ['L0008065026','L000969215'],
    'AccountNum': ['34204731277', '006737107100039'],
    'SanatizedAccountNum': ['34204731277', '6737107100039']
    }) 

dfB = pd.DataFrame({
    'RowID' : [1,2],
    'ApplicationNo': ['L43907','L52006'],
    'AccountNum': ['3265470064', '073176310000477'],
    'SanatizedAccountNum': ['3265470064', '73176310000477']
    }) 

resultPD = dfA.merge(dfB, left_on="ApplicationNo", right_on="SanatizedAccountNum", how="left")

有了以上内容,我得到一个 resultPD 没有问题。

【讨论】:

  • 在尝试您的这个建议时:resultPD = dfA.merge(dfB, left_on="applicationNo", right_on="sanitizedAccountNum", how="left"),我收到了KeyError: 'sanitizedAccountNum'。无论我使用什么列名,它都会在 right_on 上给我一个 KeyError。
  • 我需要从每个表中查看几行数据。您尝试第一个建议了吗?
  • 我添加了一个编辑主帖子,其中包含表的示例数据的快照。无法在评论中以表格格式正确格式化。
  • 我正在尝试处理第一个建议;正在研究如何迭代它的结果,它是一个“SQLDatabase._query_iterator”对象。
  • 看看这里,我想这回答了你的问题:link
猜你喜欢
  • 2020-03-27
  • 1970-01-01
  • 2018-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多