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

Извините, я в значительной степени новичок в SQL. Это должно работать в MSFT SQL, Oracle и Sybase. В следующем фрагменте мне нужно изменить inner join между IJ и KL на IJ.PO_id = KL.PO_id на left join также на IJ.PO_id = KL.PO_id. Итак, я считаю, что мне нужно пересмотреть это. Что ж, неявные соединения не самые читаемые, по крайней мере, в глазах моего коллеги. Думаю, я соглашусь, пока не разовью свой собственный вкус. Извините, я на всякий случай исказил имена таблиц и полей.

/* @IJ_id is an input stored proc patrameter. */
from AB, 
     CD,
     EF,
     GH,
     IJ,
     KL
where
    EF.EF_id = IJ.EF_id and
    IJ.EF_id = AB.EF_id and
    EF.ZY_id = IJ.ZY_id and
    IJ.ZY_id = AB.ZY_id and
    IJ.IJ_id = AB.IJ_id and
    IJ.IJ_id = @IJ_id and
    EF.XW_id = GH.GH_id and
    AB.VU_code = CD.VU_code and
    IJ.TS > 0 and
    IJ.RQ = 0 and
    EF.RQ = 0 and
    AB.RQ = 0 and
    IJ.PO_id = KL.PO_id;

Теперь моя трудность заключается в том, что в предложении where происходит много всего. Вещи, которые не выглядят как a.b = c.d, останутся в предложении where, но не все вещи, которые выглядят как a.b = c.d, легко преобразовать в явное соединение. Сложность в том, что в идеале условия должны быть между соседями - AB+CD, CD+EF, EF+GH, GH+IJ, IJ+KL, но сейчас они не так организованы. Я мог бы переупорядочить некоторые, но в конечном итоге я не хочу забывать о своей цели: я хочу, чтобы новый запрос был не медленнее, и я хочу, чтобы новый запрос был не менее читаемым. Кажется, мне было бы лучше взломать только ту часть, которую мне нужно изменить, и оставить ее в основном такой же. Я не уверен, смогу ли я это сделать.

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


person Hamish Grubijan    schedule 30.07.2010    source источник
comment
В том виде, в котором это написано в настоящее время, все это будут ВНУТРЕННИЕ СОЕДИНЕНИЯ, и вы переписываете явное ВНУТРЕННЕЕ СОЕДИНЕНИЕ, чтобы затем можно было перейти на ЛЕВОЕ СОЕДИНЕНИЕ?   -  person Cade Roux    schedule 31.07.2010
comment
Ты прав. Я просто хочу изменить один из них на левое соединение, IJ+KL: IJ.PO_id = KL.PO_id. Итак, я подумал, что переписав все остальные как явные внутренние соединения, А) позволит внести изменения, Б) сделает их более читабельными.   -  person Hamish Grubijan    schedule 31.07.2010
comment
Приятно видеть, что вы переписываете это, поскольку t-sql вообще неправильно обрабатывает левые соединения в любой версии, использующей неявные соединения.   -  person HLGEM    schedule 31.07.2010
comment
Это прекрасный пример того, почему неявные соединения сложнее поддерживать!   -  person HLGEM    schedule 31.07.2010
comment
@HLGEM, я думаю, есть преимущества в том, чтобы быть невежественным и не знать, как это будет выглядеть :)   -  person Hamish Grubijan    schedule 31.07.2010
comment
@HLGEM: TSQL имеет собственный синтаксис внешнего соединения, такой же странный, как и Oracle...   -  person OMG Ponies    schedule 31.07.2010
comment
да, у него есть пользовательский синтаксис внешнего соединения, но даже в 2000 году они работали неправильно и никогда не должны использоваться. Иногда они будут давать перекрестное соединение вместо внешнего соединения.   -  person HLGEM    schedule 01.08.2010


Ответы (3)


Я думаю, что это должно быть что-то вроде этого:

FROM AB
JOIN CD ON AB.VU_code = CD.VU_code
JOIN IJ ON IJ.EF_id = AB.EF_id AND IJ.ZY_id = AB.ZY_id AND IJ.IJ_id = AB.IJ_id
JOIN EF ON EF.EF_id = IJ.EF_id AND EF.ZY_id = IJ.ZY_id
JOIN GH ON EF.XW_id = GH.GH_id
JOIN KL ON IJ.PO_id = KL.PO_id
WHERE
    IJ.IJ_id = @IJ_id AND
    IJ.TS > 0 AND
    IJ.RQ = 0 AND
    EF.RQ = 0 AND
    AB.RQ = 0

Я попытался расположить таблицы так, чтобы выполнялись следующие правила:

  • Каждое условие соединения упоминает новую таблицу, к которой оно присоединяется с одной стороны.
  • Ни одна таблица не упоминается в условии соединения, если эта таблица еще не была объединена.
  • Условия, в которых один из операндов является константой, оставляются как условие WHERE.

Последнее правило сложное: по вашим искаженным именам невозможно сказать, должно ли условие быть частью соединения или частью предложения where. Оба дадут одинаковый результат для ВНУТРЕННЕГО СОЕДИНЕНИЯ. Должно ли условие быть частью соединения или частью предложения where, зависит от семантики отношений между таблицами.

Каждое условие необходимо рассматривать в индивидуальном порядке:

  • Определяет ли он связь между двумя таблицами? Поместите это в ПРИСОЕДИНЯЙТЕСЬ.
  • Это фильтр результатов? Поместите его в предложение WHERE.

Некоторые рекомендации:

  • Условие, включающее параметр от пользователя, вряд ли следует перемещать в объединение.
  • Неравенства обычно не встречаются в условиях соединения.
person Mark Byers    schedule 30.07.2010
comment
из других примеров я вижу, что действительно некоторые условия могут быть частью соединения или частью предложения where. Мое наивное понимание состоит в том, что я помогу оптимизатору запросов выполнить свою работу, если помогу ему устранить как можно больше вещей на раннем этапе. Этот запрос является частью серьезной хранимой процедуры, которая работает с довольно большими таблицами (от нескольких сотен до нескольких тысяч записей). В этом случае, если мне придется выбирать между удобочитаемостью и производительностью, я бы выбрал производительность. 75% пользователей будут работать на MSFT SQL, 20% времени на Oracle и 5% времени на Sybase, если это поможет. - person Hamish Grubijan; 31.07.2010
comment
@Hamish Grubijan: выбор между ГДЕ и ВКЛ не является вопросом производительности - производительность должна быть одинаковой. Когда вы измените его на LEFT JOIN, вы получите разные результаты в зависимости от того, есть ли у вас постоянные условия внутри соединения или как часть предложения where. Я не думаю, что могу дать вам правило, которое работает всегда. Вы должны выяснить, что куда идет в каждом конкретном случае. - person Mark Byers; 31.07.2010
comment
@Hamish Grubijan: при использовании синтаксиса JOIN ANSI-92 наличие критериев в WHERE и JOIN может действительно повлиять на возвращаемый результат. Критерии в соединении (IE: IJ.RQ = 0) выполняются до соединения таблицы. - person OMG Ponies; 31.07.2010
comment
Марк, поэтому в качестве упражнения я начал заполнять лист Excel, чтобы убедиться, что перевод был кошерным, но это быстро заставило меня изобрести нотацию. Следующей моей мыслью было написать скрипт на Python, что потом заставило меня задуматься — наверняка кто-то уже делал это раньше. Итак, мой вопрос: знаете ли вы об инструменте рефакторинга для баз данных. Мы поддерживаем Ora, Syb и MSFT SQL, но мы являемся магазином MSFT и получаем большую часть их программного обеспечения по оптовой подписке. Мы бы посмотрели и на других поставщиков, так как MSFT не всегда лучший. Я хотел бы что-то вроде re-sharper для кода SQL. - person Hamish Grubijan; 31.07.2010
comment
На самом деле, я только что нашел stackoverflow.com/questions/2192655/ - person Hamish Grubijan; 31.07.2010

Это не может быть менее читаемым, чем пример, который вы привели...

from AB a
join CD c on a.VU_Code = c.VU_Code
join EF e on a.EF_id = e.EF_id and e.RQ = 0
join GH g on e.XW_id = g.GH_id
join IJ i on a.IJ_id = i.IJ_id and e.EF_id = i.EF_id
         and a.EF_id = i.EF_id and e.ZY_id = i.ZY_id
         and a.ZY_id = i.ZY_id and i.TS > 0 and i.RQ = 0
LEFT join KL k on i.PO_id = k.PO_id
where 
    i.IJ_id = @IJ_id and 
    a.RQ = 0
person Fosco    schedule 30.07.2010
comment
«Это не может быть менее читабельным, чем пример, который вы привели… Вы вырвали слова прямо из моего рта. - person HLGEM; 31.07.2010
comment
лол, ты чуть не написал что-то менее читаемое. Не совсем, тем не менее, хорошая попытка :P - person Ash Burlaczenko; 31.07.2010

Использовать:

 FROM AB t1
 JOIN CD t2 ON t2.VU_code = t1.VU_code
 JOIN GH t4 ON t4.gh_id = t3.xw_id
 JOIN IJ t5 ON t5.ZY_id = t1.ZY_id
           AND t5.IJ_id = t1.IJ_id
           AND t5.EF_id = t1.EF_id 
           AND t5.IJ_id = @IJ_id 
           AND t5.TS > 0 
           AND t5.RQ = 0 
 JOIN EF t3 ON t3.ef_id = t5.ef_id
           AND t3.zy_id = t5.zy_id
           AND t3.RQ = 0
 JOIN KL t6 ON t6.po_id = t5.po_id -- Add LEFT before JOIN for LEFT JOIN
WHERE ab.qu = 0

Они имеют псевдонимы в последовательности исходного синтаксиса ANSI-89, но порядок корректируется из-за ссылки на псевдоним — нельзя ссылаться на псевдоним таблицы до того, как он будет определен.

Это синтаксис ANSI-92 JOIN — это не дает выигрыша в производительности, но это означает, что синтаксис соединения OUTER согласован. Просто нужно добавить LEFT перед "JOIN KL...", чтобы превратить это в LEFT JOIN.

person OMG Ponies    schedule 30.07.2010
comment
Кстати, один из них должен быть левым соединением. Являются ли t1, t2 и т. д. псевдонимами? если да, то я не уверен, что захочу использовать их в производственном коде. Извините еще раз, я не хотел бы раскрывать настоящие имена. Просто любопытно - вы использовали t1 ... t6, чтобы прийти к этому? - person Hamish Grubijan; 31.07.2010
comment
@Hamish Grubijan: я обновлял информацию, когда вы комментировали ЛЕВОЕ СОЕДИНЕНИЕ. Смотрите обновление. Псевдонимы таблиц - ваш выбор - используйте то, что хотите. - person OMG Ponies; 31.07.2010