Другой подход для COUNT(1)

#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) .