Присоединение операции дублирования - PullRequest
1 голос
/ 17 апреля 2011

Давайте представим, что у нас есть две таблицы: Users (UserId, UserName, UserPhoto) и Articles (ArticleId, UserId, ArticleText).Теперь мы выполняем запрос внутреннего объединения, чтобы получить пользователей со статьями:

SELECT UserId, UserName, UserPhoto, ArticleId, ArticleText 
FROM Users as u INNER JOIN Articles as a ON u.UserId = a.UserId

Структура результата запроса будет следующей:

UserId1 UserName1 UserPhoto1 ArticleId1 ArticleText1
UserId1 UserName1 UserPhoto1 ArticleId2 ArticleText2

Итак, для первого пользователя у нас есть две статьии UserName1 и UserPhoto1 дублируются.А что, если UserPhoto хранит несколько гигабайтных двоичных объектов?

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

Ответы [ 4 ]

2 голосов
/ 17 апреля 2011

Вы можете выполнить два запроса, один для получения данных пользователя (поэтому каждая фотография будет перемещаться один раз):

SELECT u.UserId
     , u.UserName
     , u.UserPhoto
FROM Users as u

и еще один, чтобы получить остальные (статьи) данные:

SELECT a.UserId               <--- only UserId this time
     , a.ArticleId
     , a.ArticleText 
FROM Users as u
  INNER JOIN Articles as a
    ON u.UserId = a.UserId

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

2 голосов
/ 20 апреля 2011

Вы можете избежать выборки фотографий несколько раз, например так:

SELECT * FROM (
  SELECT UserId, UserName, UserPhoto, ArticleId, ArticleText 
    FROM Users as u INNER JOIN Articles as a ON u.UserId = a.UserId
    WHERE ArticleId IN (SELECT MIN(ArticleId) FROM Articles GROUP BY UserId)
  UNION ALL
  SELECT UserId, UserName, NULL, ArticleId, ArticleText
  FROM Users as u INNER JOIN Articles as a ON u.UserId = a.UserId
  WHERE ArticleId NOT IN (SELECT MIN(ArticleId) FROM Articles GROUP BY UserId)
) base
ORDER BY ArticleId;  // UserId,ArticleId will also work if you want it sorted by users.

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

2 голосов
/ 17 апреля 2011

Сначала создайте третью таблицу для фотографий и свяжите идентификатор пользователя с фотографией. Во-вторых, вам нужно выполнить два отдельных запроса, чтобы получить:

  1. Каждая фотография предоставлена ​​пользователем
  2. Каждая статья, связанная с конкретным пользователем / фото

Вы будете перебирать все пары пользователь / фото и запрашивать статьи в вашем цикле.

1 голос
/ 21 апреля 2011

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

2) Однако его можно переносить (с сервера на клиент) несколько раз, для этого нет встроенной оптимизации.

3) Лучшим решением было бы обернуть это как хранимую процедуру, которая возвращает 2 записиустанавливает, и вы выполняете объединение в коде клинета, этот подход отличается от запуска 2 запросов, для которых требуется два приема в оба конца.

4) если вы не хотите этого делать, вы можете получить все идентификаторы статей изПользователь в формате CSV, а затем вы можете легко разделить CSV на отдельные строки в коде клиента.

Вот пример вывода

UserId  UserName  UserPhoto   CSV_ArticleId               CSV_ArticleText
------- --------- ----------  ------------------------    ----------------------------
UserId1 UserName1 UserPhoto1  ",ArticleId1,ArticleId2"    ",ArticleText1,ArticleText2"
UserId2 UserName2 UserPhoto2  ",ArticleId3"               ",ArticleText3"

Вот как вы можете это сделать.Запустите дословно код в тестовой базе данных, и вы увидите результат

CREATE TABLE Users(UserId int , UserName nvarchar(256), UserPhoto nvarchar(256))

CREATE TABLE Articles (ArticleId int , UserId int , ArticleText nvarchar(256))

INSERT INTO Users(UserId,UserName,UserPhoto)
VALUES (2,'2a','2pa')
INSERT INTO Users(UserId,UserName,UserPhoto)
VALUES (1,'a','pa')

INSERt INTO Articles (ArticleId, UserId, ArticleText)
VALUES (2,2,'text2')
INSERt INTO Articles (ArticleId, UserId, ArticleText)
VALUES (1,2,'text1')

;WITH tArticles AS (SELECT ArticleId, UserId, ArticleText FROM Articles)
SELECT 
    UserId, 
    UserName, 
    UserPhoto,
    (SELECT TOP 1 LTRIM(
                        (SELECT ',' + CONVERT(nvarchar(256),A.ArticleId) FROM Articles A WHERE U.UserId = A.UserId ORDER BY A.ArticleId FOR XML PATH(''))
                        )) as CSV_ArticleId,
    (SELECT TOP 1 LTRIM(
                        (SELECT ',' + CONVERT(nvarchar(256),A.ArticleText) FROM Articles A WHERE U.UserId = A.UserId ORDER BY A.ArticleId FOR XML PATH(''))
                        )) as CSV_ArticleText                   

FROM Users U
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...