SQL Оптимизация - Выполнять функцию только один раз для каждого отдельного идентификатора в объединенном запросе - PullRequest
0 голосов
/ 04 августа 2020

Я - разработчик полного стека с очень небольшими знаниями о SQL.

Рассмотрите этот код ->

(я сократил свой большой запрос до этого, чтобы выявить мои сомнения)

    SELECT get_lowest_due_date(f.fileId)
    FROM File f
    JOIN Order o 
     ON o.fileId =  f.fileId

    File

    fileId     fileName
    --------------------
    1            file1
    2            file2

    Order

    orderId     orderName      fileId
    ----------------------------------
    1            order1           1
    2            order2           1
    3            order3           2

Я считаю, что get_lowest_due_date() выполняется для всех fileId значений, возвращаемых запросом Join (даже дубликатов).

Можно ли каким-то образом заставить функцию выполняться только для уникальные значения fileId? Я пытаюсь оптимизировать запрос.

Ответы [ 4 ]

1 голос
/ 04 августа 2020

A SQL хранимая функция (или хранимая процедура) может быть DETERMINISTI C. Это означает, что результат вашей функции зависит только от входного значения, а не от других вещей. Например, GETDATE() не является детерминированным c, а SQRT((x * x) + (y * y)) равно.

Когда функция имеет DETERMINISTI C, оптимизатор сервера избегает вызова функции несколько раз для одних и тех же значений. Если функция не является детерминированной c, оптимизатор сервера обычно не имеет такой свободы.

Если ваша функция содержит SQL, вам может быть разумно преобразовать свой запрос в JOIN или аналогичную структуру, которая помещает функции SQL прямо в ваш запрос. Это позволит оптимизатору MySQL включить свои операции наилучшим образом.

В любом случае ваш пример вызывает вашу функцию для каждого значения в File.fileId. Вы можете заставить его работать только с разными значениями, используя вложенный запрос примерно так.

 SELECT get_lowest_due_date(fileId)
   FROM (
           SELECT DISTINCT f.fileId
             FROM File f
            JOIN Order o 
                 ON o.fileId =  f.fileId
        ) f
1 голос
/ 04 августа 2020

Вы можете попробовать:

SELECT get_lowest_due_date(a.fileId)
FROM (SELECT f.fileId 
      FROM File f
      JOIN Order o 
      ON o.fileId =  f.fileId
      GROUP BY f.fileId) a

Внутренний запрос возвращает уникальные идентификаторы файлов

0 голосов
/ 04 августа 2020

Исходя из контекста вашего вопроса, у вас, вероятно, есть другие столбцы, поэтому возвращение только отдельных идентификаторов файлов не является вариантом.

В вашем примере вы можете вызвать функцию в подзапросе на File:

SELECT f.*
FROM (SELECT f.*, get_lowest_due_date(f.fileId) as lowest_due_date
      FROM File f
     ) f JOIN
     Order o 
     ON o.fileId =  f.fileId;

Есть и другие приемы, которые вы можете использовать, например:

select fo.*,
       max(first_lowest_due_date) over (partition by fileid) as lowest_due_date
from (select . . .,
             (case when row_number() over (partition by fileid order by orderid) = 1
                   then get_lowest_due_date(f.fileId) 
              end) as first_lowest_due_date
      from file f join
           orders o
           on o.fileid = f.fileid
     ) fo;

Тем не менее, я подозреваю, что вы думаете, что определяемая пользователем функция - это хорошо идея. SQL не является языком программирования, и, увы, пользовательские функции в значительной степени имеют низкую производительность. Если возможно, я бы посоветовал отказаться от этой функции, возможно, используя представление или встроенную функцию с табличным значением.

0 голосов
/ 04 августа 2020

Скалярные функции вызываются для каждой строки, возвращаемой оператором select, из которого они вызываются, и да, скалярные функции не работают должным образом.

Вы можете преобразовать эту скалярную функцию в Inline-Table-Valued Функция (так что таблица возвращает таблицу вместо скалярного значения) и используйте CROSS APPLY или OUTER APPLY для вызова функции.

Определение функции:

CREATE FUNCTION dbo.get_lowest_due_date (@fileId INT)
RETURNS TABLE 
AS
RETURN 
(
  /* or whatever your logic is */
 SELECT Value from SomeTable WHERE fileId = @fileId
);

Ваш новый запрос:

 SELECT *
    FROM File f
    JOIN Order o 
     ON o.fileId =  f.fileId
    CROSS APPLY  get_lowest_due_date(f.fileId)
...