Как мы можем построить такой чрезвычайно сложный оператор SQL? - PullRequest
0 голосов
/ 06 октября 2009

[Книга] isbn (PK), название, category_id, subcategory_id, цена

[Автор] isbn (FK), author_id (PK), имя

[Категория] category_id (PK), имя

[Подкатегория] sub_category_id (PK), category_id (FK), имя

У меня есть база данных (не спроектированная мной), которая содержит четыре таблицы выше.

Я хочу, чтобы список книг имел следующий формат:

isbn, название, имя автора (авторов), название категории, название подкатегории (может не иметь), цена

Но есть некоторая сложность, как вы можете видеть, в каждой книге может быть несколько авторов, столбец с именем автора должен иметь имена авторов, разделенные запятыми.

А для категории, которая является более сложной, есть некоторые категории, у которых нет подкатегорий, и, таким образом, в некоторых записях книги subcategory_id имеет значение 0, поскольку его category_id относится к категории, которая не имеет подкатегорий, в данном случае Столбец с именем подкатегории в списке книг отображать не нужно.

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

Большое спасибо всем вам.

Ответы [ 5 ]

2 голосов
/ 06 октября 2009

Когда вы обнаруживаете, что строите «чрезвычайно сложный оператор SQL», обычно лучше сделать шаг назад и переосмыслить.

Помните об этом - подавляющее большинство операций, выполняемых над таблицей базы данных, - это операции выбора, а не вставки или обновления (хотя, конечно, существуют исключения для каждого правила).

Правильное время, чтобы "тратить" циклы ЦП на вычисление таких вещей, как списки авторов, - это когда список меняется, , а не , когда вы просто хотите извлечь информацию.

Добавьте еще один столбец в таблицу книги с именем author_list, а затем создайте триггер вставки / обновления для авторов, чтобы этот столбец перестраивался при каждом изменении автора для определенного номера ISBN.

Это помещает стоимость туда, где она должна быть, и сделает ваш запрос намного проще. Триггер гарантирует, что данные остаются непротиворечивыми, и если вы знаете, что делаете, то можно прервать 3NF.

Что касается подкатегории, оператор case может быть вашим другом, но функции для отдельных строк при выборе никогда не масштабируются хорошо.

Я бы просто создал набор строк в подкатегориях с идентификатором 0 (по одному для каждой категории) и оставил бы его имя пустым. Тогда это можно сделать простым соединением, не заботясь о производительности. Это также может быть сделано с триггером на категорию, поэтому каждая категория всегда будет иметь подкатегорию 0.

С этими двумя изменениями запрос становится намного менее сложным, что-то вроде:

select b.isbn, b.title, b.author_list, c.name, sc.name, b.price
from Book b, Category c, SubCategory sc
where b.category_id = c.category_id
and   b.category_id = sc.category_id
and   b.subcategory_id = sc.subcategory_id
order by ...

Этот запрос должен развиваться, так как он использует только базовые уровни реляционной алгебры (то есть, нет функций для каждой строки (включая операторы case), нет подзапросов). И это запрос «старой школы», вы можете получить еще большую производительность, используя явные, а не неявные соединения.

И последнее замечание: в правильной схеме 3NF ISBN не будет указан в таблице авторов. Лучше было бы иметь отдельную таблицу BookAuthor, содержащую ISBN и author_id, для правильного моделирования отношения «многие ко многим». Но вы, возможно, уже изменили это для производительности (я не знаю).

1 голос
/ 06 октября 2009

Это странная схема, а не то, как я ее разработал. Будучи денормализованным, он, вероятно, будет иметь много дубликатов в таблице авторов.

В любом случае, поскольку у вас может быть один или несколько авторов, объединения на самом деле не помогут вам в этой информации. Честно говоря, некоторые вещи лучше выполнять вне SQL, и это одна из них. Вы можете просто создать цикл, который создает информацию и выдает данные при изменении номера ISBN, при условии, что вы правильно выполнили заказ.

Что касается категорий и подкатегорий, используйте левое соединение, и оно вернет NULL для информации подкатегории, для которой вы можете проверить. Если для книги возможно несколько подкатегорий (или категорий в этом отношении), то вы действительно DOA с SQL здесь.

0 голосов
/ 06 октября 2009

Ну, бизнес подкатегории - плохое проектирование базы данных. Даже если вы предполагаете, что книга может относиться только к одной категории, это плохой дизайн, потому что (в этом случае) категория всегда может быть получена из подкатегории, поэтому вы ввели избыточность, имея в книге атрибуты для обеих.

Насколько вы хотите запрос, это просто вопрос объединения и проекции оператора select. Поскольку вы не знаете достаточно SQL, чтобы сделать это, вам, вероятно, не следует пытаться писать запросы (или вы должны спрашивать об основных объединениях и проекциях).

Относительно того, как вы превращаете несколько строк в одну (это то, что вы хотите делать с авторами), это зависит от вашей СУБД (которую вы не указываете) и / или вашего внешнего интерфейса.

0 голосов
/ 06 октября 2009

См. Ответ @ Pax для лучшего способа обработки нулевых / нулевых значений для sub_category_id

select isbn, a.name as author_name, c.name as category_name, sc.name as subcategory_name, price
from Book 
join Author a on isbn = a.isbn
join Category c on category_id = c.category_id
join SubCategory sc on category_id = sc.category_id and subcategory_id = sc.subcategory_id
where subcategory_id != 0
union
select isbn, a.name as author_name, c.name as category_name, '' as subcategory_name, price
from Book
join Author a on isbn = a.isbn
join Category c on category_id = c.category_id
join SubCategory sc on category_id = sc.category_id and subcategory_id = sc.subcategory_id
where subcategory_id = 0
0 голосов
/ 06 октября 2009

Что-то вроде этого должно быть близко.

select
   Book.ISBN,
   Book.Title,
   Author.Name,
   Category.Name as Category_Name,
   SubCategory.Name as SubCategory_Name,
   Book.Price
from
   Book join Author
      on Book.ISBN = Author.ISBN
   join Category
      on Book.Category_ID = Category.Category_ID
   join SubCategory
      on Book.Category_ID = SubCategory.Category_ID
         and Book.SubCategory_ID = SubCategory.Sub_Category_ID
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...