SQL: вложенный выбор против двойного соединения по производительности - PullRequest
0 голосов
/ 30 июня 2019

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

В моей системе ДВЕ могут быть владельцами транзакции.

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

Итак, я вижу только два способа добавить имя пользователя:

ДЕЛО 1

SELECT 
    E.*,
    CASE WHEN (E.UserDetailsId IS NULL AND E.UserDetailsId2 IS NULL)
            THEN 'N/A'
        ELSE
        (SELECT CONCAT(FirstName, ' ', LastName) AS UserName 
            FROM UserDb.dbo.UserDetails WHERE (
            (UserDetailsId IS NOT NULL AND UserDetailsId = E.UserDetailsId)
            OR 
            (UserDetailsId2 IS NOT NULL AND UserDetailsId2 = E.UserDetailsId)
            )
    END AS UserName,
FROM 
    TransactionDetail E
WHERE 
    E.TransactionDetailTypeId = @TypeId

СЛУЧАЙ 2

SELECT 
    E.*,
    CASE WHEN (E.UserDetailsId IS NULL AND E.UserDetailsId2 IS NULL)
            THEN 'N/A'
        ELSE
    CASE WHEN (E.UserDetailsId IS NOT NULL)
        THEN CONCAT(UD.FirstName, ' ', UD.LastName)
    CASE WHEN (E.UserDetailsId2 IS NOT NULL)
        THEN CONCAT(UD2.FirstName, ' ', UD2.LastName)
    END AS UserName
FROM 
    TransactionDetail E
    JOIN UserDb.dbo.UserDetails UD ON UD.UserDetailsId = E.UserDetailsId
    JOIN UserDb.dbo.UserDetails UD2 ON UD2.UserDetailsId = E.UserDetailsId
WHERE 
    E.TransactionDetailTypeId = @TypeId

Я пытался провести тест производительности на своем локальном компьютере с 1 миллионом тестовых транзакций и 10 тысячами пользователей, но результаты были равны.

Каков наилучший способ сделать это с точки зрения производительности и почему ?

Реальная база данных содержит около 20 миллионов транзакций, разделенных на 10 групп и 100 тысяч пользователей.

Ответы [ 2 ]

0 голосов
/ 30 июня 2019

С точки зрения производительности и вашего описания, я бы ожидал, что следующие показатели будут иметь лучшую производительность:

SELECT E.*,
       (CASE WHEN E.UserDetailsId IS NOT NULL
             THEN CONCAT(UD.FirstName, ' ', UD.LastName)
             WHEN E.UserDetailsId2 IS NOT NULL
             THEN CONCAT(UD2.FirstName, ' ', UD2.LastName),
             ELSE 'N/A'
        END) AS UserName
FROM TransactionDetail E LEFT JOIN
     UserDb.dbo.UserDetails UD
     ON UD.UserDetailsId = E.UserDetailsId LEFT JOIN
     UserDb.dbo.UserDetails UD2
     ON UD2.UserDetailsId = E.UserDetailsId2
WHERE E.TransactionDetailTypeId = @TypeId;

Затем вам понадобятся индексы для TransactionDetail(TransactionDetailTypeId) и UserDetails(UserDetailsId).

Использование OR в условии ON может иметь два отрицательных эффекта.Первое - это производительность.OR может ухудшить производительность.Это смягчено для этого примера, потому что вы, вероятно, сопоставляете только одну строку из-за предложения WHERE.

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

0 голосов
/ 30 июня 2019

Можете ли вы попробовать этот скрипт?Это должно вернуть тот же результат, что и ваш запрос.

Пожалуйста, проверьте вывод такой же, как ваш запрос или нет.В случае несоответствия просто игнорируйте это решение.

SELECT E.*,
CASE 
    WHEN (E.UserDetailsId IS NULL AND E.UserDetailsId2 IS NULL) THEN 'N/A'
    ELSE  CONCAT(FirstName, '  ', LastName) 
END AS UserName 
FROM TransactionDetail E
LEFT JOIN UserDetails UD  
    ON (E.UserDetailsId = UD.UserDetailsId) 
    OR (E.UserDetailsId2 = UD.UserDetailsId)
WHERE E.TransactionDetailTypeId = @TypeId
...