SQL查询对记录进行分组,并以多对一关系将它们作为列表返回

时间:2018-06-24 19:06:55

标签: python mysql sqlalchemy flask-sqlalchemy

在使用Flask在Python应用程序中充分利用SQLAlchemy的潜力时,我遇到了麻烦,并且了解了多对一关系的工作方式以及如何利用它们。

我正在使用一个具有以下格式的歌曲和歌手数据库:

CREATE TABLE song (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(64),
artist_id INTEGER
);

CREATE TABLE artist (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(64)
);

或者,通过Python SQLAlchemy,它看起来像这样:

class Song(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    kws_id = db.Column(db.String(32))
    name = db.Column(db.String(255))
    artist_id = db.Column(db.Integer, db.ForeignKey('artist.id'))
    artist = db.relationship('Artist', backref='songs', foreign_keys=[artist_id])

class Artist(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))

song.artist_id是指列artist.id;这是一种多对一的关系,许多歌曲可以引用一位歌手。

我希望我可以将结果显示为:

artist:
  song1
  song2
  song3
artist2:
  song4
  song5
...

或者,在JSON中,大致为:{artist: [song1, song2, song3], artist2: [song4, song5]}。 (最终目标是通过Flask在AJAX分页响应中返回此值)。

基本上,我希望我能有一个艺术家的字典,其中包含一些歌曲列表;并且如果我要搜索特定的歌曲,它将返回一个结构相同的字典,只包含匹配的歌曲。

如果我想运行查询并选择所有艺术家的所有歌曲,我可以简单地查询所有艺术家,然后使用backref属性来获取所有歌曲。

artists = Artist.query.all()
for artist in artists:
  songs = artist.songs
  # construct json: artist => song

但是,我正在努力寻找一种过滤歌曲的解决方案,同时保持其返回的数据结构完整。

到目前为止,我已经尝试过一些解决方案:

我可以在Python中手动完成此操作,只需让查询返回原始列表即可:

artists = Artist.query.all()
for artist in artists:
  for song in artist.songs
    if song == "search": # or starts with, or something else
       # this is a song we want to match

我可以从中建立一个json响应,但是感觉就像我在使用SQL像一个列表而不是一种查询语言一样-没有充分利用它并牺牲了速度。


我可以查询艺术家表并按歌曲进行过滤:

q = Artist.query
q = q.join(Artist.songs)
q = q.filter(Song.name.like("%" + search + "%"))
# q is a query which will return a list of artists
# each artist has the aforementioned backref for a list of song

此搜索,但返回每个结果的整个艺术家。例如,在此数据集中:

{artist1: [song1, song2, song3], artist2: [song4, song5, song6]}

搜索"song1"将返回artist1,而backref将包含artist1的所有歌曲。我构造的json将是{artist1: [song1, song2, song3]}而不是{artist1: song1},这是理想的选择。

我不确定是否有办法改进此功能以过滤掉我不想要的歌曲,或者查询歌手表是否是错误的方法。


我探索的另一种方法是在歌曲列表中进行查询:

q = Song.query
q = q.join(Artist)
q = q.filter(Song.name.like("%" + i + "%"))

但是,在这种情况下,q.all()是歌曲列表,尽管它们包含歌手姓名,但它们并未按歌手姓名“分组”(我必须做手工才能制成一个列表) (对于每位歌手,具有相同歌手名称的所有歌曲)。即我能做的最好的事情是:

[song1, song2, song3, song4]

或者,

artists = {}
for song in songs:
    artists[song.artist.name].append(song.name)
    # or initialize artists[song.artist.name] = [song.name,]

我尝试过的所有这些解决方案似乎在Python程序的一部分上都涉及某种手动任务,我觉得应该有一种方法可以改善我的解决方案以更加依赖SQL。 是否有一种查询表并返回按多对一关系的“一个”部分分组/列出的组的方法,只匹配符合特定条件的“许多”?

0 个答案:

没有答案