【问题标题】:How can I query a database with a list of values?如何使用值列表查询数据库?
【发布时间】:2023-03-31 03:05:02
【问题描述】:

我有一个世界各地主要机场经纬度坐标的数据库。我只需要在单独的 .csv 文件中列出的其中一部分(特别是在美国)。

这个 csv 文件有两列,我从两个列表中提取数据:始发机场代码(IATA 代码)和目的地机场代码(也是 IATA)。

我的数据库有一个 IATA 列,基本上我正在尝试查询该数据库以获取我拥有的两个列表中每个机场的相应纬度/经度坐标。

这是我的代码:

import pandas as pd
from sqlalchemy import create_engine

engine = create_engine('sqlite:///airport_coordinates.db')

# The dataframe that contains the IATA codes for the airports I need
airport_relpath = "data/processed/%s_%s_combined.csv" % (file, airline)
script_dir = os.path.dirname(os.getcwd())
temp_file = os.path.join(script_dir, airport_relpath)
fields = ["Origin_Airport_Code", "Destination_Airport_Code"]
df_airports = pd.read_csv(temp_file, usecols=fields)

# the origin/destination IATA codes for the airports I need
origin = df_airports.Origin_Airport_Code.values
dest = df_airports.Destination_Airport_Code.values

# query the database for the lat/long coords of the airports I need
sql = ('SELECT lat, long FROM airportCoords WHERE iata IN %s' %(origin))
indexcols = ['lat', 'long']

df_origin = pd.read_sql(sql, engine)
# testing the origin coordinates
print(df_origin)

这是我得到的错误:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such 
table: 'JFK' 'JFK' 'JFK' ... 'MIA' 'JFK' 'MIA' [SQL: "SELECT lat, long 
FROM airportCoords WHERE iata IN ['JFK' 'JFK' 'JFK' ... 'MIA' 'JFK' 
'MIA']"] (Background on this error at: http://sqlalche.me/e/e3q8)

这绝对是因为我没有正确查询它(因为它认为我的查询应该是表)。

我尝试循环遍历列表以单独查询每个元素,但该列表包含超过 604,885 个元素,并且我的计算机无法提供任何输出。

【问题讨论】:

  • 你也不需要自己创建SQLAlchemy引擎,你可以将sqlite:///... URL直接传入pandas.read_sql()
  • @MartijnPieters 你是对的,我错误地留下了从另一个函数中粘贴的内容,我在该函数中创建/输入了数据库中的数据。
  • 我已经从问题中删除了 sqlite3 连接,无需为此分散注意力。
  • 省点麻烦......如果你使用 sqlalchemy,你应该使用 ORM......它比手动构建 SQL 命令容易得多

标签: python database pandas sqlite sqlalchemy


【解决方案1】:

您的错误在于使用字符串插值:

sql = ('SELECT lat, long FROM airportCoords WHERE iata IN %s' %(origin))

因为origin 是一个Numpy 数组,所以查询中会产生[....] SQL 标识符 语法;见SQLite documentation:

如果您想使用关键字作为名称,您需要引用它。 SQLite中有四种引用关键字的方式:

[...]
[keyword] 括在方括号中的关键字是标识符。 [...]

您要求 SQLite 检查 iata 是否在名为 ['JFK' 'JFK' 'JFK' ... 'MIA' 'JFK' 'MIA'] 的表中,因为那是 Numpy 数组的字符串表示形式。

您已经在使用 SQLAlchemy,如果您使用该库为您生成所有 SQL 会更容易,包括 IN (....) 成员资格测试:

from sqlalchemy import *

filter = literal_column('iata', String).in_(origin)
sql = select([
    literal_column('lat', Float),
    literal_column('long', Float),
]).select_from(table('airportCoords')).where(filter)

然后将sql 作为查询传入。

我在这里使用literal_column()table() 对象来直接找到对象的名称,但您也可以直接从您已经创建的engine 对象中询问SQLAlchemy 到reflect your database table,然后使用结果表生成查询的定义:

metadata = MetaData()
airport_coords = Table('airportCoords', metadata, autoload=True, autoload_with=engine)

此时查询将被定义为:

filter = airport_coords.c.iata.in_(origin)
sql = select([airport_coords.c.lat, airport_coords.c.long]).where(filter)

我还会在输出中包含 iata 代码,否则您将无法返回将 IATA 代码连接到匹配坐标的路径:

sql = select([airport_coords.c.lat, airport_coords.c.long, airport_coords.c.iata]).where(filter)

接下来,正如您所说,列表中有 604,885 个元素,因此您可能希望将该 CSV 数据加载到临时表中以保持查询效率:

engine = create_engine('sqlite:///airport_coordinates.db')

# code to read CSV file
# ...
df_airports = pd.read_csv(temp_file, usecols=fields)

# SQLAlchemy table wrangling
metadata = MetaData()
airport_coords = Table('airportCoords', metadata, autoload=True, autoload_with=engine)
temp = Table(
    "airports_temp",
    metadata,
    *(Column(field, String) for field in fields),
    prefixes=['TEMPORARY']
)
with engine.begin() as conn:
    # insert CSV values into a temporary table in SQLite
    temp.create(conn, checkfirst=True)
    df_airports.to_sql(temp.name), engine, if_exists='append')

# Join the airport coords against the temporary table
joined = airport_coords.join(temp, airport_coords.c.iata==temp.c.Origin_Airport_Code)

# select coordinates per airport, include the iata code
sql = select([airport_coords.c.lat, airport_coords.c.long, airport_coords.c.iata]).select_from(joined)
df_origin = pd.read_sql(sql, engine)

【讨论】:

  • 作为一个注释,以防万一,从 1.2 开始,可以使用 bindparam.expanding 功能将列表作为文本 SQL 的参数传递。
  • 如果我想避免重复(因为 csv 有许多相同的机场重复),我将如何更改 'joined' 参数?
  • @TimKrash:可以在sql查询对象中添加.distinct()
  • @TimKrash:例如尝试df_origin = pd.read_sql(sql.distinct(), engine)
  • @MartijnPieters 不幸的是这没有用。不过没关系,在我尝试做的这一步中,这可能并不重要。不过非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-20
  • 1970-01-01
  • 1970-01-01
  • 2019-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多