Как обрабатывать несколько отношений один-> много? - PullRequest
0 голосов
/ 06 августа 2009

У меня есть база данных с несколькими таблицами, 5 из которых посвящены определенным типам публикаций. Каждый из этих 5 имеет отношение один-> много к таблице состояния и таблице людей. Все эти таблицы связаны между собой с помощью уникального «pubid». У меня есть представление, которое включает pubid (для всех 5 типов), а также связанные с ними ключевые слова. Когда пользователь выполняет поиск по ключевым словам и результаты охватывают более 1 из этих 5 таблиц типов публикаций, я действительно не уверен, как с этим справиться.

Если бы был только один тип публикации (то есть только одна таблица с 1-> многими), было бы очень легко выполнить вложенное соединение, что-то вроде:

SELECT * FROM articles 
  INNER JOIN status ON articles.spubid = status.spubid 
  INNER JOIN people ON articles.spubid = people.spubid 
  WHERE people.saffil = 'ABC' ORDER BY people.iorder, articles.spubid;

В этом примере «статьи» - одна из 5 упомянутых мной таблиц, которая имеет отношение 1-> много. Допустим, поиск по ключевым словам возвращает результаты, которые включают статьи, книги и документы. Как я могу достичь этой же цели с таким количеством разных таблиц? Если бы мне пришлось выяснить, как использовать JOIN в этом случае, декартово произведение было бы настолько большим, что я думаю, что затраты на его разбор в пригодный для использования формат были бы слишком высокими. Какие у меня другие варианты в этом случае?

Ответы [ 5 ]

2 голосов
/ 06 августа 2009

Почему они в отдельных таблицах? Различаются ли столбцы? И какие столбцы вы хотите вернуть (никогда не используйте select * в производстве, особенно с объединениями), различаются ли они между различными типами в вашем запросе?

Если вы можете сделать столбцы одинаковыми, я предлагаю вам использовать UNION ALL. Даже если столбцы отличаются, предоставляя все, что вам нужно в каждой части оператора объединения (давая значение null для тех столбцов, которых нет в наборе таблиц), вы все равно можете получить то, что хотите. Ниже приведен упрощенный код:

SELECT articlename, articlestatus, author, ISBN_Number 
FROM articles 
  INNER JOIN status ON articles.spubid = status.spubid   
  INNER JOIN people ON articles.spubid = people.spubid   
WHERE people.saffil = 'ABC' 
UNION ALL 
SELECT papername, paperstatus, author, null
FROM papers 
  INNER JOIN status ON papers.spubid = status.spubid   
  INNER JOIN people ON papers.spubid = people.spubid   
WHERE people.saffil = 'ABC' 
2 голосов
/ 06 августа 2009

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

CREATE VIEW AllTables AS
  SELECT Afield1, Afield2, NULL as Bfield1, NULL as Bfield2 FROM Atable
    UNION
  SELECT NULL as Afield1, NULL as Afield2, Bfield1, Bfield2 FROM Btable;

Конечно, при необходимости вы можете использовать что-то кроме NULL в качестве значения заглушки. Затем вы запускаете свой запрос против представления. Если вам нужно изменить форматирование в соответствии с типом публикации, вы можете включить исходную таблицу как часть представления (т. Е. Добавить "'magazine' AS publication_type" или что-то похожее на каждый из выбранных вами вариантов).

0 голосов
/ 27 августа 2009

В итоге мы создали очень сложное представление, которое включает в себя 1-много таблиц в виде столбцов массива в представлении. Это позволяет выполнять один запрос в одном представлении и возвращать все необходимые данные. Определение представления является ОЧЕНЬ сложным, но оно работает как лучший, настоящий трюк заключался в использовании функции ARRAY () в PostgreSQL.

0 голосов
/ 06 августа 2009

Мне действительно не нравится дизайн стола здесь. Однако я полагаю, что перепроектирование всей базы данных с нуля является слишком экстремальным.

Учитывая дизайн таблицы, я думаю, вам придется использовать UNION и указывать столбцы в каждой таблице. Я знаю, что это монстр, но это то, что происходит, когда вы разрабатываете таблицы с большим количеством столбцов, которые «почти» похожи.

И HLGEM прав. Использование «select *» в постоянно хранимом запросе действительно опасно.

0 голосов
/ 06 августа 2009

Возможно, вы могли бы создать представления для каждого из 5 типов (vwBook, vwArticle и т. Д.)

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

Измените, конечно, по своему усмотрению. Вот пример с широким штрихом:

create proc MySearch

@MySearchTerm varchar(50)

AS
    DECLARE @SearchResultsTABLE
    (
     Type      varchar(10) -- the view you found the result in.
    ,ID        int -- the primary key of the Book record. whatever you want to link it back to the original
    ,FoundText varchar(512)
    --etc
    )

    INSERT INTO @SearchResults(Type, ID, FoundText)
      SELECT 'Articles', ID, SomeKeyField
      FROM vwArticle
      WHERE SomeKeyField LIKE '%' + @MySearchTerm + '%'

    INSERT INTO @SearchResults(Type, ID, FoundText)
      SELECT 'Book', ID, SomeKeyField
      FROM vwBook
      WHERE SomeKeyField LIKE '%' + @MySearchTerm + '%'

    --repeat as needed with the 3 other views that you'd build

    SELECT * FROM @SearchResults
...