Как я могу получить общее количество во многих отношениях? - PullRequest
0 голосов
/ 08 апреля 2020

Я использую wx python и sqlite3 для создания простого организатора песен.

Существуют следующие таблицы

  • песни
  • файлы
  • теги
  • списки воспроизведения

Песни и файлы имеют отношение «один к одному».

Песни и теги имеют отношение «многие ко многим».

Песни и списки воспроизведения имеют отношение «многие ко многим».

Ниже вы можете увидеть запросы к таблицам, которые я использую:

create_songs_table_query = """ CREATE TABLE IF NOT EXISTS songs (
                                song_id integer PRIMARY KEY AUTOINCREMENT,
                                title text NOT NULL,
                                artist text NOT NULL,
                                added_timestamp integer NOT NULL,
                                file_id INTEGER NULL,
                                    FOREIGN KEY (file_id)
                                    REFERENCES files (file_id)
                                    ON DELETE CASCADE
                    ); """

create_files_table_query = """ CREATE TABLE IF NOT EXISTS files (
                                        file_id integer PRIMARY KEY AUTOINCREMENT,
                                        filename text NULL,
                                        size integer NULL,
                                        song_id INTEGER NOT NULL,
                                            FOREIGN KEY (song_id)
                                            REFERENCES songs (song_id)
                                            ON DELETE CASCADE                                
                                ); """

create_tags_table_query = """CREATE TABLE IF NOT EXISTS tags (
                                 tag_id integer PRIMARY KEY AUTOINCREMENT,
                                 tag_text  text NOT NULL,
                                 tag_timestamp integer NULL,

                                ); """

create_songs_tags_table_query = """CREATE TABLE IF NOT EXISTS songs_tags (
                                    song_tag_id  integer PRIMARY KEY AUTOINCREMENT,
                                    song_id INTEGER NOT NULL,
                                            FOREIGN KEY (song_id)
                                            REFERENCES songs (song_id)
                                            ON DELETE CASCADE,  
                                    tag_id INTEGER NOT NULL,
                                            FOREIGN KEY (tag_id)
                                            REFERENCES tags (tag_id)
                                            ON DELETE CASCADE  
                                    ); """

create_playlists_table_query = """CREATE TABLE IF NOT EXISTS playlists (
                                      playlist_id  integer PRIMARY KEY AUTOINCREMENT,
                                      playlist_title text NOT NULL,
                                      created_timestamp  INTEGER NOT NULL,
                                      updated_timestamp  INTEGER NULL,
                                    ); """

create_songs_playlists__table_query = """CREATE TABLE IF NOT EXISTS songs_playlists (
                                            song_playlist_id integer PRIMARY KEY AUTOINCREMENT,
                                            song_id INTEGER NOT NULL,
                                                    FOREIGN KEY (song_id)
                                                    REFERENCES songs (song_id)
                                                    ON DELETE CASCADE,
                                            playlist_id INTEGER NOT NULL,
                                                        FOREIGN KEY (playlist_id)
                                                        REFERENCES playlists (playlist_id)
                                                        ON DELETE CASCADE  
                                        ); """

Я пытаюсь узнать, сколько песен содержит каждый плейлист. всего, включая, если в каком-либо плейлисте 0 песен.

Я использую следующий запрос, который, кажется, возвращает желаемые результаты:

SELECT playlists.playlist_id, playlists.playlist_title, COUNT(songs.song_id) as total
FROM playlists 
LEFT OUTER JOIN songs_playlists 
ON playlists.playlist_id = songs_playlists.playlist_id 
LEFT OUTER JOIN songs
ON songs_playlists.song_id = songs.song_id
GROUP BY (songs.song_id)
ORDER BY total DESC

Хотя я не уверен, что он полностью правильный или может быть, есть более простой или эффективный способ сделать это.

Ответы [ 2 ]

0 голосов
/ 08 апреля 2020

SQLite требует, чтобы при использовании синтаксиса FOREIGN KEY (columnname).... для определения внешних ключей все эти определения помещались после определений столбцов (в конце оператора CREATE). Также у вас есть запятые в 2 из операторов CREATE прямо перед закрывающей скобкой, которые должны быть удалены. Это неправильные операторы:

CREATE TABLE IF NOT EXISTS tags (
   tag_id integer PRIMARY KEY AUTOINCREMENT,
   tag_text  text NOT NULL,
   tag_timestamp integer NULL, -- remove the last comma
);

CREATE TABLE IF NOT EXISTS songs_tags (
  song_tag_id  integer PRIMARY KEY AUTOINCREMENT,
  song_id INTEGER NOT NULL,
  FOREIGN KEY (song_id) REFERENCES songs (song_id) ON DELETE CASCADE, -- move to the end 
  tag_id INTEGER NOT NULL,
  FOREIGN KEY (tag_id) REFERENCES tags (tag_id) ON DELETE CASCADE  
);

CREATE TABLE IF NOT EXISTS playlists (
  playlist_id  integer PRIMARY KEY AUTOINCREMENT,
  playlist_title text NOT NULL,
  created_timestamp  INTEGER NOT NULL,
  updated_timestamp  INTEGER NULL, -- remove the last comma
); 

CREATE TABLE IF NOT EXISTS songs_playlists (
  song_playlist_id integer PRIMARY KEY AUTOINCREMENT,
  song_id INTEGER NOT NULL,
  FOREIGN KEY (song_id) REFERENCES songs (song_id) ON DELETE CASCADE, -- move to the end 
  playlist_id INTEGER NOT NULL,
  FOREIGN KEY (playlist_id) REFERENCES playlists (playlist_id) ON DELETE CASCADE  
);

Все операторы исправлены:

CREATE TABLE IF NOT EXISTS songs (
  song_id integer PRIMARY KEY AUTOINCREMENT,
  title text NOT NULL,
  artist text NOT NULL,
  added_timestamp integer NOT NULL,
  file_id INTEGER NULL,
  FOREIGN KEY (file_id) REFERENCES files (file_id) ON DELETE CASCADE
); 

CREATE TABLE IF NOT EXISTS files (
  file_id integer PRIMARY KEY AUTOINCREMENT,
  filename text NULL,
  size integer NULL,
  song_id INTEGER NOT NULL,
  FOREIGN KEY (song_id) REFERENCES songs (song_id)  ON DELETE CASCADE
); 

CREATE TABLE IF NOT EXISTS tags (
  tag_id integer PRIMARY KEY AUTOINCREMENT,
  tag_text  text NOT NULL,
  tag_timestamp integer NULL
);

CREATE TABLE IF NOT EXISTS songs_tags (
  song_tag_id  integer PRIMARY KEY AUTOINCREMENT,
  song_id INTEGER NOT NULL,  
  tag_id INTEGER NOT NULL,
  FOREIGN KEY (song_id) REFERENCES songs (song_id) ON DELETE CASCADE,
  FOREIGN KEY (tag_id) REFERENCES tags (tag_id) ON DELETE CASCADE  
); 

CREATE TABLE IF NOT EXISTS playlists (
  playlist_id  integer PRIMARY KEY AUTOINCREMENT,
  playlist_title text NOT NULL,
  created_timestamp  INTEGER NOT NULL,
  updated_timestamp  INTEGER NULL
); 

CREATE TABLE IF NOT EXISTS songs_playlists (
  song_playlist_id integer PRIMARY KEY AUTOINCREMENT,
  song_id INTEGER NOT NULL,
  playlist_id INTEGER NOT NULL,
  FOREIGN KEY (song_id) REFERENCES songs (song_id) ON DELETE CASCADE,
  FOREIGN KEY (playlist_id)  REFERENCES playlists (playlist_id) ON DELETE CASCADE  
);

Ваш запрос должен работать, хотя вам не нужно последнее присоединение к songs.

0 голосов
/ 08 апреля 2020

Я пытаюсь узнать, сколько песен в каждом плейлисте в общей сложности, в том числе, если в каком-либо плейлисте есть 0 песен.

Вы действительно можете начать с таблицы playlists, а затем привести songs_playlists с left join, затем агрегировать. Однако вам не нужна таблица songs, чтобы получить желаемый результат, и, что более важно, вам нужно group by список воспроизведения, а не по песне:

select p.playlist_id, p.playlist_title, count(sp.playlist_id) no_songs
from playlists p
left join songs_playlists sp on sp.playlist_id = p.playlist_id
group by p.playlist_id, p.playlist_title

Коррелированный подзапрос может также будет приемлемым подходом, поскольку он устраняет необходимость во внешней агрегации:

select
    p.*,
    (select count(*) from songs_playlists sp where sp.playlist_id = p.playlist_id) no_songs
from playlists p
...