Лучший способ понять сложные операторы SQL? - PullRequest
40 голосов
/ 18 декабря 2008

У кого-нибудь есть метод для понимания сложных операторов SQL? При чтении структурного / OO-кода обычно существуют уровни абстракции, которые помогают разбить его на управляемые куски. Однако часто в SQL кажется, что вам нужно отслеживать, что происходит в нескольких частях запроса одновременно.

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

Есть ли лучший способ разбить SQL-запрос на управляемые части?

Ответы [ 16 ]

38 голосов
/ 18 декабря 2008

Когда я смотрю на сложный фрагмент кода SQL, это то, что я делаю.

Во-первых, если это обновление или удаление, я добавляю код (если его там нет и он закомментирован), чтобы сделать его выбором. Никогда не пытайтесь обновить или удалить в первый раз, не увидев результаты сначала. Если это обновление, я проверяю, что выбор показывает текущее значение и то, что я буду устанавливать для него, чтобы убедиться, что я получаю желаемый результат.

Понимание объединений имеет решающее значение для понимания сложного SQL. Для каждого присоединения я спрашиваю себя, почему это здесь? Есть четыре основных причины. Вам нужен столбец для выбора, вам нужно поле для предложения where, вам нужно объединение в качестве моста к третьей таблице, или вам нужно присоединиться к таблице для фильтрации записей (например, для получения сведений о клиенте, у которого есть заказы). но не нуждаясь в деталях заказа, это часто может быть сделано лучше с условием IF EXISTS where). Если это левое или правое соединение (я склонен переписывать, чтобы все было левым соединением, которое упрощает жизнь.), Я думаю, сработает ли внутреннее соединение. Зачем мне нужно левое соединение? Если я не знаю ответа, я буду запускать его в обоих направлениях и увижу разницу в данных. Если есть производные таблицы, я сначала посмотрю на них (запустив только ту часть select, чтобы увидеть, каков результат), чтобы понять, почему она есть. Если есть подзапросы, я попытаюсь их понять, а если они будут медленными, вместо этого попробую преобразовать их в производную таблицу, поскольку они часто выполняются намного быстрее.

Далее я смотрю на предложения where. Это то место, где вам пригодится прочная основа в вашей конкретной базе данных. Например, я знаю в своих базах данных, в каких случаях мне может понадобиться увидеть только почтовый адрес и в каких случаях мне может понадобиться увидеть другие адреса. Это помогает мне узнать, что-то отсутствует в предложении where. В противном случае я рассматриваю каждый пункт в предложении where и выясняю, почему он должен быть там, а затем я думаю, есть ли что-то пропущенное, что должно быть там. Посмотрев его, я думаю, могу ли я внести коррективы, чтобы сделать запрос саргматичным.

Далее я также рассмотрю любые сложные биты из списка выбора. Что делает это заявление случая? Почему есть подзапрос? Что делают эти функции? (Я всегда ищу код функции для любой функции, с которой я еще не знаком.) Почему есть отличное? Можно ли избавиться от него с помощью производной таблицы или агрегатной функции и группы по операторам?

Наконец, и САМОЕ важное , я запускаю процедуру выбора и определяю, выглядят ли результаты правильно, основываясь на моих знаниях о бизнесе. Если вы не понимаете своего бизнеса, вы не узнаете, правильный ли запрос . Синтаксически правильный не означает правильных результатов. Часто в существующем пользовательском интерфейсе есть часть, которую вы можете использовать в качестве руководства для определения правильности ваших результатов. Если у меня есть экран, который показывает заказы для клиента, и я делаю отчет, который включает заказы клиентов, я мог бы провести выборочную проверку нескольких отдельных клиентов, чтобы убедиться, что они показывают правильный результат.

Если текущий запрос фильтруется некорректно, я удалю его фрагменты, чтобы узнать, как избавиться от записей, которые мне не нужны, или добавить записи, которые мне не нужны. Часто вы обнаружите, что объединение является одним ко многим, и вам нужно одно к одному (используйте производную таблицу в этом случае!) Или вы обнаружите, что некоторая часть информации, которая, по вашему мнению, вам нужна в предложении where, не является Значение true для всех данных, которые вам нужны, или что отсутствует какой-то фрагмент предложения where. Помогает иметь все поля в предложении where (если они еще не были выбраны) во время выбора, когда вы это делаете. Это может даже помочь показать все поля из всех соединенных таблиц и реально посмотреть на данные. Когда я делаю это, я часто добавляю немного к предложению where, чтобы получить только некоторые записи, которые у меня есть, которых не должно быть, а не все записи.

Одной хитрой вещью, которая нарушает многие запросы, является предложение where, ссылающееся на поле в таблице справа от левого соединения. Это превращает это во внутреннее соединение. Если вам действительно нужно левое соединение, вы должны добавить такие условия в само соединение.

17 голосов
/ 18 декабря 2008

Это могут быть полезные советы.

  • Комментарии - выясните, что делает маленький кусок, и прокомментируйте его, чтобы вы поняли это, когда вернетесь к нему позже.
  • Подсветка синтаксиса - убедитесь, что вы просматриваете код с тем, что будет окрашивать запрос в цвете.
  • Отступ - реорганизовать запрос так, чтобы он имел смысл для вас .. Переберите все, добавьте возврат каретки.

Например:

select ID, Description, Status from ABC where Status = 1 OR Status = 3

может быть лучше написано как:

select 
  ID,
  Description,
  Status
from ABC
where
  Status = 1 OR
  Status = 3

с более сложным запросом вы увидите гораздо большее преимущество.

12 голосов
/ 18 декабря 2008

Вот процедура, чтобы следовать, чтобы распутать запрос.

  1. Сначала я форматирую SQL.
  2. Затем я закомментирую все части SQL, кроме основных частей самой основной или самой важной таблицы, чтобы ответить на вопрос.
  3. Затем я начну раскомментировать объединения, выбрать столбцы, группировки, поля порядка и фильтры, чтобы изолировать различные части запроса и посмотреть, что происходит. Или подсвеченное исполнение работает в некоторых инструментах.
  4. Подзапросы обычно могут выполняться независимо.

Выполнение каждого из них обычно позволяет мне лучше понять, что происходит в запросе.

9 голосов
/ 18 декабря 2008

В основном это просто опыт и правильное отступление.

8 голосов
/ 18 декабря 2008

Отступы и комментарии очень помогают. Самым ценным, с чем я столкнулся, является утверждение WITH. Он находится в Oracle и занимается рефакторингом подзапроса. Это позволяет разбить большой запрос на набор, казалось бы, меньших. Каждый чуть более управляем.

Вот пример

WITH 
ssnInfo AS
(
    SELECT SSN, 
           UPPER(LAST_NAME), 
           UPPER(FIRST_NAME), 
           TAXABLE_INCOME,          
           CHARITABLE_DONATIONS
    FROM IRS_MASTER_FILE
    WHERE STATE = 'MN'                 AND -- limit to in-state
          TAXABLE_INCOME > 250000      AND -- is rich 
          CHARITABLE_DONATIONS > 5000      -- might donate too

),
doltishApplicants AS
(
    SELECT SSN, 
           SAT_SCORE,
           SUBMISSION_DATE
    FROM COLLEGE_ADMISSIONS
    WHERE SAT_SCORE < 100          -- About as smart as a Moose.
),
todaysAdmissions AS
(
    SELECT doltishApplicants.SSN, 
           TRUNC(SUBMISSION_DATE)  SUBMIT_DATE, 
           LAST_NAME, FIRST_NAME, 
           TAXABLE_INCOME
    FROM ssnInfo,
         doltishApplicants
    WHERE ssnInfo.SSN = doltishApplicants.SSN

)
SELECT 'Dear ' || FIRST_NAME || 
       ' your admission to WhatsaMattaU has been accepted.'
FROM todaysAdmissions
WHERE SUBMIT_DATE = TRUNC(SYSDATE)    -- For stuff received today only
;

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

Эта форма также позволяет помещать предложения фильтра с отдельным подзапросом и сохранять предложения объединения для окончательного выбора.

На работе наша группа разработчиков, как правило, находит их проще в обслуживании и зачастую быстрее.

5 голосов
/ 18 декабря 2008

форматирование помогает, но понимание теории множеств и, соответственно, реляционной теории, помогает еще больше.

также не помешает смутное понимание того, как выполняются запросы (сканирование таблиц, сканирование индексов, переходы по индексам, слияние хеш-таблиц и т. Д.); планировщик запросов может показать вам эти операции

несколько операций (имеющих, существует, с) могут быть неприятными вначале

сначала понять, что происходит с каждой таблицей, и как таблицы объединяются

2 голосов
/ 18 декабря 2008

Как и во всём, ЛУЧШИЙ способ - написать множество сложных SQL-операторов самостоятельно. В конце концов, общий способ структурирования вещей становится очевидным. Конечно, если вы ищете что-то быстрое, это, вероятно, не так.

Пробел очень важен. Запрос, который выглядит невероятно сложным, может выглядеть почти упрощенно при наличии надлежащего пробела.

Что касается объединений ... Извините, но я не могу быть здесь очень полезным, потому что я отвечаю, что лучший способ понять конкретное объединение - это понять, как объединения в целом работают. Каждый тип объединения служит очень специфической цели, и если вы знаете, как они работают, на самом деле не должно быть большой разницы от соединения x к y, x к y к z или x и y к a и b.

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

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

2 голосов
/ 18 декабря 2008

Еще один важный момент - использование стандартного синтаксиса соединения:

SELECT A 
  FROM B
  JOIN C ON B.ID = C.ID
 WHERE C.X = 1

Вместо

SELECT A 
  FROM B
     , C 
 WHERE B.ID = C.ID 
   AND C.X = 1
2 голосов
/ 18 декабря 2008

Полагаю, все зависит от опыта. Я не нашел, что запросы в этих вопросах были очень сложными, возможно, так как большинство запросов, которые я выполняю, являются более сложными, чем те.

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

1 голос
/ 05 июля 2017

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

(Следующее заимствовано из Inside Microsoft SQL Server 2005: запросы T-SQL, Ицик Бен-Ган.)

(8) SELECT (9) DISTINCT (11) <TOP_specification> <select_list>
(1) FROM <left_table>
(3) <join_type> JOIN <right_table>
(2) ON <join_condition>
(4) WHERE <where_condition>
(5) GROUP BY <group_by_list>
(6) WITH {CUBE | ROLLUP}
(7) HAVING <having_condition>
(10) ORDER BY <order_by_list>
  1. ОТ : декартово произведение (перекрестное соединение) выполняется между первыми двумя таблицами в FROM, и в результате создается виртуальная таблица VT1.
  2. ON : Фильтр ON применяется к VT1. Только строки, для которых ИСТИНА вставляются в VT2.
  3. OUTER (объединение) : если указано OUTER JOIN (в отличие от CROSS JOIN или INNER JOIN), строки из сохраненной таблицы или таблиц, для которых не найдено совпадений добавляются к строкам из VT2 как внешние строки, генерируя VT3. Если более двух таблиц появляются в предложении FROM, шаги с 1 по 3 применяются неоднократно между результатом последнего соединения и следующей таблицы в предложении FROM, пока не будут обработаны все таблицы.
  4. ГДЕ : фильтр ГДЕ применяется к VT3. Только строки, для которых TRUE вставлены в VT4.
  5. GROUP BY : строки из VT4 упорядочены в группы на основе указанного списка столбцов в предложении GROUP BY. VT5 генерируется.
  6. CUBE | ROLLUP : супергруппы (группы групп) добавляются в строки из VT5, генерация VT6.
  7. HAVING : Фильтр HAVING применяется к VT6. Только группы, для которых TRUE вставлены в VT7.
  8. SELECT : список SELECT обрабатывается, генерируя VT8.
  9. DISTINCT : повторяющиеся строки удаляются из VT8. VT9 генерируется.
  10. ORDER BY : строки из VT9 сортируются в соответствии со списком столбцов, указанным в ЗАКАЗАТЬ ПОЛОЖЕНИЕ. Курсор генерируется (VC10).
  11. TOP : указанное число или процент строк выбирается с начала VC10. Таблица VT11 генерируется и возвращается вызывающей стороне.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...