Casper-SC
@Casper-SC
Программист (.NET)

Эффективно ли составлен запрос на получение первых нескольких записей из БД с использованием JOIN?

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

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

IF OBJECT_ID('Users') IS NOT NULL 
  DROP TABLE Users;

IF OBJECT_ID('Groups') IS NOT NULL 
  DROP TABLE Groups;

CREATE TABLE Groups(
  GroupID INT NOT NULL,
  Name NVARCHAR(40) NOT NULL,
  Replicated TINYINT NOT NULL DEFAULT(0),
  CONSTRAINT PK_Groups_GroupID PRIMARY KEY(GroupID)
  );
GO

CREATE TABLE Users(
  UserID INT NOT NULL IDENTITY(1, 1),
  Name NVARCHAR(40) NOT NULL,
  GroupID INT NOT NULL,
  CONSTRAINT PK_Users_UserID PRIMARY KEY(UserID),
  CONSTRAINT FK_Users_GroupID FOREIGN KEY(GroupID) REFERENCES Groups(GroupID)
  );
GO

INSERT INTO Groups (GroupID, Name)
  VALUES 
    (1, N'Группа 1'),
    (2, N'Группа 2'),
    (3, N'Группа 3'),
    (4, N'Группа 4'),
    (5, N'Группа 5'),
    (6, N'Группа 6'),
    (7, N'Группа 7'),
    (8, N'Группа 8'),
    (9, N'Группа 9'),
    (10, N'Группа 10'),
    (11, N'Группа 11'),
    (12, N'Группа 12'),
    (13, N'Группа 13'),
    (14, N'Группа 14'),
    (15, N'Группа 15'),
    (16, N'Группа 16'),
    (17, N'Группа 17');
GO

INSERT INTO Users (Name, GroupID)
  VALUES 
    (N'1 Владимир Сенцов', 1),
    (N'1 Дмитрий Логинов', 1),
    (N'2 Адрей Астапов', 2),
    (N'2 Владимир Астапов', 2),
    (N'3 Александр Астапов', 3),
    (N'3 Адрей Серков', 3),
    (N'4 Адрей Астапов', 4),
    (N'4 Борис Хорошилов', 4),
    (N'5 Адрей Серков', 5),
    (N'5 Адрей Животное', 5),
    (N'6 Антон Миллер', 6),
    (N'6 Владимир Животное', 6),
    (N'7 Дмитрий Логинов', 7),
    (N'7 Адрей Астапов', 7),
    (N'8 Адрей Серков', 8),
    (N'8 Александр Астапов', 8),
    (N'9 Адрей Серков', 9),
    (N'9 Адрей Синдеев', 9),
    (N'10 Борис Хорошилов', 10),
    (N'10 Адрей Серков', 10),
    (N'11 Анатолий Сенцов', 11),
    (N'11 Антон Миллер', 11),
    (N'12 Борис Серков', 12),
    (N'12 Анатолий Астапов', 12),
    (N'13 Борис Хорошилов', 13),
    (N'13 Адрей Серков', 13),
    (N'14 Адрей Животное', 14),
    (N'14 Борис Миллер', 14),
    (N'15 Адрей Серков', 15),
    (N'15 Александр Астапов', 15),
    (N'16 Александр Серков', 16),
    (N'16 Адрей Синдеев', 16),
    (N'17 Борис Хорошилов', 17),
    (N'17 Адрей Миллер', 17);
GO


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

Кое-какое пояснение, к сути вопроса мало относится:
Replicated = 2 означает, что данные отправлены, но не было подтверждено, что их сохранили на принимающей стороне. Если на отправляющей стороне данные обновятся, то в Replicated будет выставлен 1 и при получении ответа от принимающей стороны я просто перевожу в Replicated = 1 только те данные, которые были Replicated = 2. То есть если данные, которые я только что отправил обновили ещё до подтверждения, то хоть на принимающей стороне данные и сохранили, но я снова их же прочитаю (уже обновленные) и снова отправлю. Это к вопросу не особо относится, но чтобы вы понимали, что к чему.

Теперь сам запрос на выборку не реплицированных данных:
DECLARE @GroupIDs TABLE(ID INT)
INSERT INTO @GroupIDs(ID) SELECT TOP(3) GroupID FROM Groups WHERE Replicated <> 1;

SELECT 
  g.GroupID,
  g.Name AS GroupName,
  g.Replicated,
  u.UserID,
  u.Name AS UserName
FROM
Groups g
INNER JOIN @GroupIDs ids ON g.GroupID = ids.ID
LEFT OUTER JOIN Users u ON u.GroupID = ids.ID;

UPDATE Groups SET Replicated = 2 WHERE GroupID IN(SELECT ID FROM @GroupIDs);


Суть вопроса:
Мне нужно получать первые несколько записей из таблицы Groups и всех кто в этой группе. Эффективно ли составлен запрос? Как и можно ли вообще его оптимизировать?

Так я подтвержаю, что данные были сохранены на принимающей стороне, после получения ответа-подтверждения успешного сохранения данных от принимающей стороны:
UPDATE Groups SET Replicated = 1 WHERE ID = @ID AND Replicated > 1;


Вспомогательный код:
Выполнив запрос выше помечаем ранее полученные данные, как реплицированные и при следующем вызове запроса выше эти данные уже не будут выбраны из БД. Просто для удоства тестирования запросов в текущем примере.
UPDATE Groups SET Replicated = 1 WHERE Replicated = 2;


Возвращаем всё в исходное состояние.
UPDATE Groups SET Replicated = 0 WHERE Replicated = 1; 

SELECT COUNT(GroupID) AS Replicated_1 FROM Groups WHERE Replicated = 1;
SELECT COUNT(GroupID) AS Replicated_2 FROM Groups WHERE Replicated = 2;
  • Вопрос задан
  • 476 просмотров
Пригласить эксперта
Ответы на вопрос 2
@res2001
Developer, ex-admin
1.На мой взгляд временная таблица не нужна. Перепишите update без временной таблицы.
2.Для выборки первых 3 записей я бы добавил какую-нибудь сортировку. Хотя это и не обязательно.
3.Вместо primary key, я бы сделал уникальный кластерный индекс.
Вообще запросы не сложные, особо оптимизировать не чего.
Ответ написан
@art_karetnikov
Лучший мой проект: Мобильный банк Сбербанка РФ.
Доброго. На мой взгляд - изврат. Вот это вот создание временной таблицы из groups, потом джойн с тем же groups её...

SELECT TOP(3) GroupID FROM Groups g
LEFT JOIN Users u ON u.GroupID = g.ID
WHERE Replicated <> 1;

Все. Вот ваши три записи, любые несинхронизированные. Поля нужные из обеих таблиц вставьте самостоятельно. И второе - откройте для себя таинство команды select ... output ...
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы