PostgreSQL - Рефрактинг монстра Запроса - PullRequest
0 голосов
/ 17 июня 2020

Я каким-то образом попал в свой рекорд по самому большому запросу, который я сделал на эту дату, к счастью, он довольно хорошо структурирован ... но он действительно уродлив, мне интересно, есть ли способ, которым я мог бы преломить его с некоторыми знаниями от сообщества, это будет очень признательно!

Вот монстр со временем выполнения 5 мс в таблице из 10 строк

SELECT
q.frontend AS platform,
r.frontend AS library,

CONCAT( -- Image Reference eg. px_iej1k321foa8_0
    CASE WHEN p.is_temporary THEN q.prefix
        ELSE (SELECT prefix FROM files.platform a WHERE a.platform = 'local') END,
    '_', CASE WHEN p.is_temporary THEN p.reference ELSE p.file_name END,
    CASE WHEN (
        (SELECT real_choice FROM -- Image Count
            (SELECT id, ROW_NUMBER() OVER (ORDER BY b.choice ASC)
            AS real_choice FROM files.main b
            WHERE b.platform = p.platform AND b.reference = p.reference
        ) c WHERE c.id = p.id)::integer) = 1 THEN '' ELSE CONCAT('_', p.choice)
    END
) AS reference,

CONCAT( -- Image Directory
    'https://',
    CASE WHEN p.is_temporary THEN q.temp_bucket ELSE q.bucket END,
    '/', t.path, '/', p.file_name, '.', s.name
) AS directory,


CASE WHEN u.path IS NOT NULL AND w.name IS NOT NULL THEN
CONCAT( -- Image Thumbnail
    'https://',
    CASE WHEN p.is_temporary THEN q.temp_bucket ELSE q.bucket END,
    '/', u.path, '/', p.file_name, '.', w.name
) END AS thumbnail,

CONCAT(q.file_source_path, p.reference) AS source, -- Image Original Source
v.frontend AS rating,

(SELECT real_choice FROM -- Image Count
(SELECT id, ROW_NUMBER() OVER (ORDER BY b.choice ASC)
    AS real_choice FROM files.main b
    WHERE b.platform = p.platform AND b.reference = p.reference
) c WHERE c.id = p.id)::integer AS choice,

(SELECT COUNT(*) FROM files.main b -- Image Total Count
    WHERE b.platform = p.platform
    AND b.reference = p.reference
)::integer AS total_choices

FROM files.main p
LEFT JOIN files.platform q
ON p.platform = q.id
LEFT JOIN files.library r
ON p.library = r.id
LEFT JOIN files.extension s
ON p.extension = s.id
LEFT JOIN files.path t
ON p.path = t.id
LEFT JOIN files.path u
ON p.path_thumbnail = u.id
LEFT JOIN files.rating v
ON p.rating = v.id
LEFT JOIN files.extension w
ON p.extension_thumbnail = w.id
WHERE p.id = $1;

В то время как $ 1 представляет собой целое число (в самом низу)

Ответы [ 2 ]

1 голос
/ 18 июня 2020

Прежде всего, я бы начал с форматирования.

SELECT
  q.frontend AS platform,
  r.frontend AS library,

  CONCAT( -- Image Reference eg. px_iej1k321foa8_0
      CASE WHEN p.is_temporary THEN q.prefix
          ELSE (SELECT prefix FROM files.platform a WHERE a.platform = 'local') 
  END,
      '_', CASE WHEN p.is_temporary THEN p.reference ELSE p.file_name END,
      CASE WHEN (
          (SELECT real_choice FROM -- Image Count
              (SELECT id, ROW_NUMBER() OVER (ORDER BY b.choice ASC)
              AS real_choice FROM files.main b
              WHERE b.platform = p.platform AND b.reference = p.reference
          ) c WHERE c.id = p.id)::integer) = 1 THEN '' ELSE CONCAT('_', p.choice)
      END
  ) AS reference,

  CONCAT( -- Image Directory
      'https://',
      CASE WHEN p.is_temporary THEN q.temp_bucket ELSE q.bucket END,
      '/', t.path, '/', p.file_name, '.', s.name
  ) AS directory,


  CASE WHEN u.path IS NOT NULL AND w.name IS NOT NULL THEN
  CONCAT( -- Image Thumbnail
      'https://',
      CASE WHEN p.is_temporary THEN q.temp_bucket ELSE q.bucket END,
      '/', u.path, '/', p.file_name, '.', w.name
  ) END AS thumbnail,

  CONCAT(q.file_source_path, p.reference) AS source, -- Image Original Source
  v.frontend AS rating,

  (SELECT real_choice FROM -- Image Count
    (SELECT id, ROW_NUMBER() OVER (ORDER BY b.choice ASC)
        AS real_choice FROM files.main b
        WHERE b.platform = p.platform AND b.reference = p.reference
    ) c WHERE c.id = p.id)::integer AS choice,

  (SELECT COUNT(*) FROM files.main b -- Image Total Count
      WHERE b.platform = p.platform
      AND b.reference = p.reference
  )::integer AS total_choices

FROM files.main p
LEFT JOIN files.platform q ON p.platform = q.id
LEFT JOIN files.library r ON p.library = r.id
LEFT JOIN files.extension s ON p.extension = s.id
LEFT JOIN files.path t ON p.path = t.id
LEFT JOIN files.path u ON p.path_thumbnail = u.id
LEFT JOIN files.rating v ON p.rating = v.id
LEFT JOIN files.extension w ON p.extension_thumbnail = w.id
WHERE p.id = $1;

Это уже немного короче и легче читается.

Затем возьмите любые выражения в предложении select которые не зависят от чего-либо еще в запросе, и переместите их в CTE.

WITH image_count AS (
  SELECT
    COUNT(*)::integer c AS total_choices,
    ROW_NUMBER() OVER (ORDER BY b.choice ASC) AS real_choice
  FROM files.main p
  INNER JOIN files.main b ON b.platform = p.platform AND b.reference = p.reference
  WHERE p.id = $1
)
SELECT
  q.frontend AS platform,
  r.frontend AS library,

  CONCAT( -- Image Reference eg. px_iej1k321foa8_0
      CASE WHEN p.is_temporary THEN q.prefix
          ELSE (SELECT prefix FROM files.platform a WHERE a.platform = 'local') 
  END,
      '_', CASE WHEN p.is_temporary THEN p.reference ELSE p.file_name END,
      CASE WHEN (image_count.real_choice::integer) = 1 THEN '' ELSE CONCAT('_', p.choice)
      END
  ) AS reference,

  CONCAT( -- Image Directory
      'https://',
      CASE WHEN p.is_temporary THEN q.temp_bucket ELSE q.bucket END,
      '/', t.path, '/', p.file_name, '.', s.name
  ) AS directory,

  CASE WHEN u.path IS NOT NULL AND w.name IS NOT NULL THEN
  CONCAT( -- Image Thumbnail
      'https://',
      CASE WHEN p.is_temporary THEN q.temp_bucket ELSE q.bucket END,
      '/', u.path, '/', p.file_name, '.', w.name
  ) END AS thumbnail,

  CONCAT(q.file_source_path, p.reference) AS source, -- Image Original Source
  v.frontend AS rating,

  total_image_count.real_choice, -- Image Count
  total_image_count.total_choices -- Image Total Count

FROM files.main p
LEFT JOIN files.platform q ON p.platform = q.id
LEFT JOIN files.library r ON p.library = r.id
LEFT JOIN files.extension s ON p.extension = s.id
LEFT JOIN files.path t ON p.path = t.id
LEFT JOIN files.path u ON p.path_thumbnail = u.id
LEFT JOIN files.rating v ON p.rating = v.id
LEFT JOIN files.extension w ON p.extension_thumbnail = w.id
CROSS JOIN total_image_count
WHERE p.id = $1;

Лично я, вероятно, оставлю это там. Мне нетрудно читать, но, конечно, это субъективно.

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

0 голосов
/ 17 июня 2020

Попробуйте использовать временные таблицы в блоке транзакции для создания промежуточных результатов вместо гигантских сложных операторов sql. Их намного легче читать и поддерживать. Вот общая идея:

Start transaction block
Create Temp table with just needed columns
Sql statement to perform joins into table (repeat as needed)
Sql statement to perform conversions
Sql statement to move results to permanent location
Commit (end of transaction)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...