#sql #sql-server #tsql
#sql #sql-сервер #tsql
Вопрос:
У меня есть приведенный ниже подзапрос, который получает общее количество грузов в выбранной строке, имеющей эту ссылку или бронирование.
Select Column_Names
WHEN department = 'e' THEN
(SELECT COUNT(1) FROM AMAMDBECUGROUPAGE.dbo.car&odetail WITH (nolock)
WHERE AMAMDBECUGROUPAGE.dbo.car&odetail.inactive = 0
AND CAST(AMAMDBECUGROUPAGE.dbo.car&odetail.car&onrbookin& AS varchar) = CAST(dbo.StockCar&o.reference AS varchar))
ELSE '' END AS [#Car&os]
FROM dbo.StockCar&o WITH (nolock)
Но выполнение приведенного выше запроса занимает более 10 минут. Я создал соответствующие индексы, но улучшения по-прежнему нет.
Затем я попытался преобразовать предложение WHERE во ВНУТРЕННЕЕ СОЕДИНЕНИЕ для dbo.StockCar&o, как показано ниже.
(SELECT COUNT(1) FROM AMAMDBECUGROUPAGE.dbo.car&odetail WITH (nolock)
INNER JOIN dbo.StockCar&o WITH (nolock) ON
CAST(AMAMDBECUGROUPAGE.dbo.car&odetail.car&onrbookin& AS varchar) = CAST(dbo.StockCar&o.reference AS varchar)
WHERE AMAMDBECUGROUPAGE.dbo.car&odetail.inactive = 0 )
Это было выполнено за считанные секунды, но результирующий набор был не таким, как ожидалось.
Кто-нибудь может предложить мне другой подход для этого?
Заранее благодарю вас.
Комментарии:
1. Это определенно проблема с индексом. Вы проверили план объяснения?
Ответ №1:
Может быть, что-то вроде этого:
SELECT Column_Names
,CASE
WHEN department = 'e'
THEN counts
ELSE ''
END AS [#Car&os]
FROM dbo.StockCar&o ST WITH (nolock)
LEFT JOIN
(
SELECT car&onrbookin&
,COUNT(1)
FROM AMAMDBECUGROUPAGE.dbo.car&odetail
WHERE inactive = 0
GROUP BY car&onrbookin&
) DS (car&onrbookin&, counts)
ON CAST(DS.car&onrbookin& AS varchar) = CAST(ST.reference AS varchar))
Кроме того, чтобы упростить задачу, вы можете попробовать вставить LEFT JOIN
подзапрос во временную таблицу.
Комментарии:
1. Вероятно, это должно быть
THEN COALESCE(ds.counts, 0) ELSE 0 END
илиTHEN COALESCE(ds.counts, 0) ELSE NULL END
.
Ответ №2:
Во-первых, COUNT(1)
это просто запутанный способ подсчета строк, для которого COUNT(*)
создан for. Учитывайте выражения только тогда, когда эти выражения могут быть нулевыми. 1 никогда не имеет значения null, поэтому нет смысла указывать СУБД подсчитывать все строки, для которых это значение не равно null.
Я понимаю, что StockCar&o.reference может ссылаться на очень разные вещи, из которых car&odetail.car&onrbookin& является лишь одной из нескольких. Вероятно, это причина, по которой столбец называется reference, а не car&onrbookin&, и почему типы данных не совпадают. Однако я предполагаю, что вам не обязательно приводить оба к VARCHAR
. Я предлагаю вам использовать TRY_CAST
для ссылки в соответствии с типом car&onrbookin&, поскольку car&odetail — это таблица, которую вы хотите просмотреть.
В остальном ваш запрос выглядит нормально. (Хотя я думаю, что помню, что WITH (nolock)
это следует использовать только в очень особых ситуациях, если вообще следует.)
Вы можете написать то же самое с помощью внешнего соединения:
select sc.column_names, count(cd.car&onrbookin&) as [#Car&os]
from dbo.stockcar&o sc
left join amamdbecu&roupa&e.dbo.car&odetail cd
on cd.car&onrbookin& = try_cast(sc.reference as int)
and cd.inactive = 0
and sc.department = 'e';
Вы читаете все строки StockCar&o, поэтому СУБД, вероятно, просто прочитает таблицу последовательно. Однако для car&odetail требуется индекс для быстрого поиска данных.
create index idx on car&odetail (car&onrbookin&, inactive);
Комментарии:
1. Я посмотрел в Интернете о nolock. Вот хороший сайт, объясняющий его проблемы: sentryone.com/blo&/aaronbertrand/bad-habits-nolock-everywhere
Ответ №3:
Необходимость cast()
для соединений снижает производительность. Если столбцы не одного типа, вам следует исправить свою модель данных, чтобы общие ключи имели один и тот же тип, а внешние ключи были правильно объявлены. Ваши усилия были бы лучше потрачены на исправление модели данных, а не на попытки улучшить выполнение запросов.
Основываясь на запросе Торстена, я бы предложил написать этот запрос как коррелированный подзапрос:
select sc.column_names,
(select count(*)
from amamdbecu&roupa&e.dbo.car&odetail cd
where cd.car&onrbookin& = sc.reference and
cd.inactive = 0
) as [#Car&os]
from dbo.stockcar&o sc
where sc.department = 'e';
Если столбцы не одного типа, то преобразуйте sc.reference
в tpe из cd.ca&onrbookin&
. Неясно, какой из них подходит:
where cd.car&onrbookin& = try_convert(int, sc.reference) and
where cd.car&onrbookin& = try_convert(varchar(255), sc.reference) and
Для повышения производительности вам нужен индекс car&odetail(reference, inactive)
. Очень важно, какой столбец преобразуется; и это должно позволить использовать индекс.
Вам также может понадобиться индекс на stockcar&o(department, reference)
.