Я ненавиджу SQL підзапити. Ось чому

pic

Фото: Tim Gouw на Unsplash

Як фахівці з даних, ми розуміємо, що розповідання історій — це наша найпотужніша навичка. Ми ретельно готуємо наші презентації, вибираємо впливові візуалізації і постійно думаємо про те, як довго увага нашої аудиторії може зберігатися. Але коли мова йде про написання коду, ми часто забуваємо ці самі принципи.

У сучасному робочому середовищі перегляд коду (code review) є основною частиною нашого робочого процесу. Незалежно від того, чи переглядаєте ви запит колеги, чи ваш код переглядають, читабельність не є чимось необов'язковим — це необхідність. Тож чому б не застосувати ці самі принципи розповідання історій до написання SQL?

Давайте поговоримо про підзапити (subqueries) і чому я вважаю їх ворогом читаємого коду. Люди природно читають зліва направо, але підзапити змушують наші мізки виконувати складні логічні вправи. Розглянемо цей приклад:

SELECT   
 customer_name,  
 order_count,  
 avg_order_value  
FROM customers c  
WHERE customer_id IN (  
 SELECT   
 customer_id   
 FROM orders   
 WHERE total_amount > (  
 SELECT   
 AVG(total_amount)   
 FROM orders  
 )  
 GROUP BY customer_id  
 HAVING COUNT(*) > 5  
);

Щоб зрозуміти цей запит, вам, можливо, доведеться:

  1. Почати читати з зовнішнього SELECT
  2. Перейти до найглибшого підзапиту, щоб зрозуміти, як обчислюється середнє значення
  3. Повернутися на один рівень, щоб побачити, як це середнє значення використовується
  4. І, зрештою, повернутися до початку, щоб зрозуміти, які дані ми насправді отримуємо

Це схоже на читання книги, де вам потрібно перевертати на останню сторінку, щоб зрозуміти перший розділ. Натомість ось та сама логіка, але з використанням загальних таблиць (Common Table Expressions, CTE):

WITH avg_order AS (  
 SELECT AVG(total_amount) as avg_amount  
 FROM orders  
),  
frequent_high_value_customers AS (  
 SELECT   
 customer_id,  
 COUNT(*) as order_count  
 FROM orders  
 WHERE total_amount > (SELECT avg_amount FROM avg_order)  
 GROUP BY customer_id  
 HAVING COUNT(*) > 5  
)  
SELECT   
 c.customer_name,  
 fhc.order_count,  
 AVG(o.total_amount) as avg_order_value  
FROM frequent_high_value_customers fhc  
JOIN customers c ON c.customer_id = fhc.customer_id  
JOIN orders o ON o.customer_id = c.customer_id;

Варіант з CTE розповідає набагато зрозумілішу історію:

  1. Спочатку ми обчислюємо середню суму замовлення
  2. Потім ідентифікуємо клієнтів, які часто роблять замовлення вище середнього
  3. І, зрештою, отримуємо деталі цих клієнтів та їхні патерни замовлень

CTE — це не лише про читаємість. Вони модульні. Ви можете легко тестувати кожен CTE окремо, запустивши його самостійно, що значно спрощує налагодження та валідацію. Вони також полегшують підтримку коду, оскільки ви можете змінювати одну частину без необхідності розплутувати вкладену логіку.

Отже, наступного разу, коли вас захопить бажання написати підзапит, пам’ятайте: ваш код не лише для комп’ютерів — він також для людей. Вибирайте ясність замість складності, і ваше майбутнє "я" (та колеги) будуть вдячні.

Перекладено з: I hate SQL subqueries. Here’s why

Leave a Reply

Your email address will not be published. Required fields are marked *