Страницы

Поиск по вопросам

воскресенье, 15 марта 2020 г.

Как улучшить SQL запрос

#sql #sql_server


Мне нужно сделать выборку с таблиц со следующим условием:


  сделать запрос, который получает список всех продуктов, и цены на
  2013-03-01


Я написал вот такой скрипт:

select st.[ProductID], st.[ProductName], [Price].Price
from (
  select [Product].[ProductID], [ProductName], MAX(OnDate) as [OnDate]
  from [Product]
  inner join [dbo].[Price] on
    [Product].[ProductID] = [Price].[ProductID]
  where [OnDate] <= '2013-03-01'
  group by [Product].[ProductID], [ProductName]
) st
inner join [dbo].[Price] on
st.[ProductID] = [Price].[ProductID] and [Price].[OnDate] = st.[OnDate]
order by [ProductID]


И структура таблиц:

create table Product
(
    ProductID int not null identity(1,1) primary key,
    ProductName varchar(255),
    Description varchar(max),
    Color varchar(15)
)
create table Price
(
    ProductID int not null,
    OnDate datetime not null,
    Price int not null
)


Все работает отлично, но хотелось бы как то красивее все написать.
Какие есть идеи?
    


Ответы

Ответ 1



Проблема не столько в запросе, сколько в схеме базы. В таблице Price нет PK. Совсем. Единственный способ, которым SQL Server может выбрать что-то из таблицы без PK - это перечитать ее целиком. На большом объеме это будет тормозить вне зависимости от красоты запроса. Добавьте ключ - или составной (ProductID + OnDate, или какой-нибудь PriceID identity). После этого посмотрите план запроса и добавляйте индексы по необходимости. Я бы предсказал CREATE NONCLUSTERED INDEX [NonClusteredIndex-OnDate_Prod] ON [dbo].[Price] ( [OnDate] ASC, [ProductID] ASC ) что (на паре сотен строк) приведет к плану вида т.е. хоть вы и упомянули Price дважды, реально данные из нее будут вычитаны один раз - при чтении конкретной цены. Если очень хочется - можно переписать запрос с одним JOIN: ;WITH PricesBeforeDate as ( SELECT * from Price WHERE [OnDate] <= '2013-03-01' ), IndexedPrices as ( SELECT ROW_NUMBER() OVER(PARTITION BY [ProductID] ORDER BY OnDate DESC) AS Row, ProductID, Price FROM PricesBeforeDate ), LastPrices as ( SELECT * FROM IndexedPrices WHERE Row = 1 ) SELECT LastPrices.ProductID, ProductName, Price FROM LastPrices INNER JOIN Product on LastPrices.ProductID = Product.ProductID этот запрос дает чуть меньше чтений на тех данных, что я у себя навбивал, но всегда стоит сравнить на реальных значениях: видно что есть Sort, кушающий CPU. Это сортировка по ProductID и OnDate, вызванная тем, что я использовал PriceID в качестве ключа. Если использовать ключ по ProductID ASC, OnDate DESC, то сортировка исчезнет ALTER TABLE dbo.Price ADD CONSTRAINT PK_Price_1 PRIMARY KEY CLUSTERED ( ProductID, OnDate DESC ) план: Но в целом выбирать между разными вариантами запроса и разными индексами стоит на реальных данных. Включаете SET Statistics io on SET Statistics time on и смотрите output и план. Все остальное - гадание.

Комментариев нет:

Отправить комментарий