ЗАКАЗАТЬ и отфильтровать по дате добавления, в то время как КЛЮЧЕВЫЙ ИДЕНТИЧНЫЙ ПЕРВИЧНЫЙ КЛЮЧ в строке Id - PullRequest
0 голосов
/ 02 октября 2018

У меня есть таблица (Orders) с CLUSTERED IDENTITY PRIMARY KEY (OrderId), и я фильтрую и сортирую данные по столбцу добавления даты (AddDate),Есть ли способ сообщить оптимизатору запросов, что AddDate упорядочен так же, как OrderId (поэтому данные уже упорядочены по AddDate)?

SQL Server на самом деле не нужноПросканируйте всю таблицу, а затем отсортируйте ее.Все, что требуется от операции, - это сканировать таблицу до тех пор, пока не будет найдена дата окончания, затем отфильтровать данные до даты начала и вернуть их как есть (без сортировки).

Пример:

SELECT
      *
    FROM Orders
    WHERE AddDate BETWEEN @FromDate AND @ToDate
    ORDER BY AddDate

Ответы [ 3 ]

0 голосов
/ 10 октября 2018

Ты больше ничего не можешь сделать.Прежде всего, если вы знаете, что это отсортировано таким же образом, то нет никаких причин, по которым вы бы упорядочили свой результат в AddDate вместо OrderId.Во-вторых, если вы знаете об этом отношении, вы можете просто получить задание OrderId для того периода времени, который вам нужен, как показывал @Jesus.Наилучшим способом будет создание дополнительного индекса на OrderDate (только).Для такого простого запроса это будет работать лучше, нет необходимости включать дополнительные столбцы, PK будет в любом случае покрыт фоном и будет применяться фильтрация на основе всего.Последний вариант, который все еще принесет некоторые улучшения (не так хорошо, как индекс, но все же может быть полезен), - это добавить дополнительную статистику по столбцу OrderDate, основываясь на том, что оценщик кардинальности SQL Server сможет составить лучший планчто должно привести к лучшей производительности.

0 голосов
/ 16 октября 2018

Вы можете добавить некластеризованный индекс в AddDate, OrderId:

CREATE INDEX IX_Orders_AddDate_OrderID
ON dbo.Orders(AddDate, OrderID)

Затем переписать свой запрос:

SELECT       *
FROM Orders
WHERE OrderId >=
           (SELECT MIN(OrderId)
            FROM dbo.Orders
            WHERE AddDate >= @FromDate) AND
      OrderId <=
           (SELECT MAX(OrderId)
            FROM dbo.Orders
            WHERE AddDate <= @ToDate)
ORDER BY AddDate
0 голосов
/ 10 октября 2018

Есть ли способ сообщить оптимизатору запросов, что AddDate упорядочен так же, как и OrderId (поэтому данные уже упорядочены по AddDate)?

Нет, нетЭто невозможно.

Однако вы можете заказать по OrderId вместо AddDate, если AddDate упорядочен так же, как OrderId, он вернет те же результаты.Но, к сожалению, SQL Server все равно будет сканировать всю таблицу.

Не будем принимать таблицы заказов Northwind и столбец OrderDate.

Запрос:

SELECT *
FROM dbo.Orders
WHERE OrderDate BETWEEN '1997-12-10' AND '1998-03-05'
ORDER BY OrderDate

Производит этот план .Он полностью сканирует кластерный индекс при применении фильтра, а затем упорядочивает результат.

Запрос:

SELECT *
FROM dbo.Orders
WHERE OrderDate BETWEEN '1997-12-10' AND '1997-12-17'
ORDER BY OrderId -- It's equivalent to ordering by OrderDate

Создает этот план .Он также полностью сканирует кластеризованный индекс и применяет фильтр, но не упорядочивает.

Наличие OrderDate в качестве ключа кластеризованного индекса кардинально улучшит производительность запроса, но вы, возможно, не захотите иметьтакой ключ кластеризованного индекса.Однако вы создаете облачный индекс, который также значительно повысит производительность:

CREATE INDEX IX_Orders_OrderDate ON dbo.Orders(OrderDate)
INCLUDE ([OrderID], [CustomerID], [EmployeeID], [RequiredDate], [ShippedDate], [ShipVia], [Freight], [ShipName], [ShipAddress], [ShipCity], [ShipRegion], [ShipPostalCode], [ShipCountry])

Запрос:

SELECT *
FROM dbo.Orders
WHERE OrderDate BETWEEN '1997-12-10' AND '1998-03-05'
ORDER BY OrderDate

Создает этот план .Он просто ищет индекс.Это не может быть быстрее.

Но этот индекс толстый, он будет наказывать за изменение данных.

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

CREATE INDEX IX_Orders_OrderDate ON dbo.Orders(OrderDate, OrderId)

Используя запрос, подобный следующему:

DECLARE @FromOrderId int, @ToOrderId int;
SELECT TOP (1) @FromOrderId = OrderId FROM dbo.Orders WHERE OrderDate <= '1997-12-10' ORDER BY OrderDate DESC, OrderId DESC;
SELECT TOP (1) @ToOrderId = OrderId FROM dbo.Orders WHERE OrderDate >= '1998-03-05' ORDER BY OrderDate ASC, OrderId ASC;

SELECT *
FROM dbo.Orders
WHERE 
    (OrderId  >= @FromOrderId OR @FromOrderId IS NULL)
    AND (OrderId <= @ToOrderId OR @ToOrderId IS NULL)
ORDER BY OrderID
OPTION (RECOMPILE)

Выдает thisплан .Для решения вопроса достаточно 3 попыток.

...