Pokud je to špatné používat inline SQL, jak se pomocí LINQ provádět dotazy se liší v praxi?




Jaká je obecná shoda o použití LINQ provádět dotazy a manipulaci inline a jak se to liší se vkládání příkazů SQL do kódu (který je považován za ne ne)?
Ignoruje všechny používá související non-SQL z LINQ a právě s ohledem na LINQ-to-SQL.


LINQ dotazy jsou objekty, které si zachovávají dotazu *definici* a ekvivalentní SQL je instanci pouze v okamžiku dotazu je vlastně opakována. Který umožňuje komponenty musí být řádně odděleny a umožňují **zpracování ve stylu potrubí** , kde jsou dotazy vrácené spodních vrstev (např. Podle údajů) a transformovány vyššími složkami v zásobníku. Dotaz může přizpůsobit nové filtry, se připojí a další ‚artefakty‘ z každé složky ve zpracovatelském potrubí, dokud nedosáhne konečného formulář, který je případně opakována. Tato manipulace je prakticky nemožné pomocí rovné textu SQL.


Další výhodou je, že LINQ jeho syntaxe je snazší pochopit vývojářům non-databáze. SQL syntax odbornost je překvapivě těžké najít aktivum. Několik vývojáři jsou ochotni trávit čas učení jemnější body SQL mimo `SELECT * FROM ... WHERE ...`. Syntaxe LINQ je blíže ke každodenním prostředí .NET a využívá stávající dovednost nastavit v této vrstvě (např. Výrazy lambda) v protikladu k vynucení vývojář naučit správné SQL pro cílené zadní konce (T-SQL, PL-SQL atd). Vzhledem k tomu, že výraz LINQ vyjadřuje požadovaný výsledek do značné míry přímo do relační algebře, poskytovatelé mají mnohem snazší práci do generuje odpovídající SQL (nebo dokonce vytvářet přímou plán provádění!). Srovnejte to s nemožný úkol ‚generické‘ databázové řidiči museli čelit vzácně s textem SQL. Pamatovat ODBC syntaxe escape, někdo?
<p>tady nesouhlasím s velkými a komplikovanými dotazy s datagodem. vidím je pouze jako problémy, pokud jsou zmatené. pokud jde o výkon, jsou téměř vždy lepší, protože plánovač má mnohem větší svobodu při získávání informací. velké dotazy však musí být psány s ohledem na údržbu. obecně jsem zjistil, že jednoduchý, dobře strukturovaný sql lze snadno ladit, i když jediný dotaz pokračuje pro 200+ řádků. je to proto, že obvykle máte docela dobrou představu o tom, s jakým problémem se zabýváte, takže v dotazu je jen několik oblastí, které musíte zkontrolovat.</p><p>problémy s údržbou, ime, přicházejí, když se rozpadne struktura sql. dlouhé, složité dotazy v dílčím výběru zhoršují čitelnost a odstraňování problémů, stejně jako inline pohledy, a při obou dotazech je třeba se vyhnout. místo toho použijte zobrazení, pokud je to možné (všimněte si, že jste na mysql, zobrazení nefungují tak dobře, ale na většině ostatních db to dělají), a použijte běžné výrazy tabulek, kde ti nefungují (mysql tyto nepodporuje mimochodem).</p><p>dlouhé složité dotazy fungují velmi dobře, a to jak z důvodu údržby, tak z hlediska výkonu, kdy si necháváte klauzule tam, kde je to jednoduché, a kde uděláte cokoli, co můžete, s připojením namísto podvýberů. cílem je zajistit, aby „záznamy se nezobrazovaly“, vám poskytne několik velmi konkrétních míst v dotazu ke kontrole (je to upuštěno do spojení nebo odfiltrováno v klauzuli where?) a tým údržby dokáže věci skutečně udržovat.</p><p>pokud jde o škálovatelnost, mějte na paměti, že čím větší flexibilita má projektant, to je také dobrá věc ....</p><p>edit: vy zmiňujete, že se jedná o mysql, takže je nepravděpodobné, že by pohledy fungovaly tak dobře, a cte jsou mimo otázku. uvedený příklad navíc není příliš dlouhý nebo složitý, takže to není žádný problém.</p> <p>už nemůžu najít ten pravý příspěvek, ale eric lippert (a možná i několik dalších softies) se při několika příležitostech opíral o to, jak je linq <strong>deklarativní</strong>, což je pro několik tříd problémů mnohem intuitivnější než <strong>imperativní</strong> syntaxe.</p><p>linq umožňuje psát kód, který vyjadřuje <em> záměr </em>, nikoli mechanismus <em> </em> .</p><p>řekni mi, co je lépe čitelné. tento:</p><pre class="lang-cs prettyprint-override"><code>ienumerable&lt;customer&gt; getvipcustomers(ienumerable&lt;customer&gt; source) { list&lt;customer&gt; results = new list&lt;customer&gt;(); foreach (customer c in source) { if (c.firstname == "aaron") { results.add(c); } } results.sort(new lastnamecomparer()); return results; } class lastnamecomparer : icomparer&lt;customer&gt; { public int compare(customer a, customer b) { return x.lastname.compareto(b.lastname); } } </code></pre><p>nebo toto?</p><pre class="lang-cs prettyprint-override"><code>ienumerable&lt;customer&gt; getvipcustomers(ienumerable&lt;customer&gt; source) { return from c in source where c.firstname == "aaron" orderby c.lastname select c; } </code></pre><p>nebo dokonce tohle?</p><pre class="lang-cs prettyprint-override"><code>ienumerable&lt;customer&gt; getvipcustomers(ienumerable&lt;customer&gt; source) { return source.where(c =&gt; c.firstname == "aaron").orderby(c =&gt; c.lastname); } </code></pre><p>prvním příkladem je jen svazek zbytečných kotlových desek, aby bylo dosaženo co nejjednodušších výsledků. každý, kdo si myslí, že je <em> více </em> čitelnější než verze linq, musí nechat vyšetřit hlavu. nejen to, ale první ztrácí paměť. nemůžete to ani napsat pomocí <code>yield return</code> kvůli třídění.</p><p>váš spolupracovník může říct, co chce; osobně si myslím, že linq nezměřitelně zlepšil <em> můj </em> kód čitelnosti.</p><p>ani o linqovi není nic „relačního“. může mít některé povrchové podobnosti s sql, ale nepokouší se v jakémkoli tvaru nebo formě implementovat relační počet. je to jen banda rozšíření, která usnadňují vyhledávání a projektování sekvencí. „dotaz“ neznamená „relační“ a ve skutečnosti existuje několik nerelačních databází, které používají syntaxi podobnou sql. linq je <em> čistě </em> objektově orientovaný, prostě se stává, že pracuje s relačními databázemi prostřednictvím frameworků jako linq do sql kvůli nějakému výrazu voodoo stromů a chytrému designu od tým c #, čímž se anonymní funkce implicitně převádějí na stromy výrazů.</p> <p>je to velmi užitečné v každém scénáři, kde je jedna část třídy generována nějakým vlastním nástrojem, protože umožňuje přidat vlastní logiku do vygenerovaného kódu bez zdědění generované třídy. mimochodem. ze stejného důvodu existují také částečné metody.</p><p>nejde jen o uživatelské rozhraní, ale také o jiné technologie, jako je linq-to-sql nebo entity framework.</p> <p>jako uživatel 100% foss bych rád řekl ano:</p><p>funguje to však dobře, dokud si neuvědomíte, že knihovna je implementována napůl a je nová a skvělá a byla by opravdu užitečná (jako příklad linq to sql)</p> <h2>souhrn</h2><p>nechte propojený server dělat co nejvíce.<br> je <em> nemožné </em> pro sql server optimalizovat dotaz na propojeném serveru, dokonce i na jiném serveru sql</p><h2>dlouho</h2><p>klíčovým faktorem je <em> kde </em> je dotaz spuštěn.</p><p>v tomto případě se jedná o triviální výběr, takže všechny řádky z tabulky budou odeslány po drátu. nezáleží na tom.</p><p>když přidáte připojení a kde, pak na tom může záležet. chcete, aby sql server umožnil propojenému serveru provést co největší filtrování, aby se zmenšila velikost dat přicházejících po síti.</p><p>například druhý případ by zde měl být efektivnější.</p><pre><code>select * from openquery(&lt;linked server&gt;, 'select &lt;column list&gt; from mytable') t1 join somelocaltable t2 on ... where t1.foo = 'bar' select * from openquery(&lt;linked server&gt;, 'select &lt;column list&gt; from mytable where foo = ''bar''') join somelocaltable t2 on ... </code></pre><p>omezení openquery je v tom, že nemůžete parametrizovat: proto potřebujete dynamický sql, abyste mohli přidat klauzule where atd.</p><p>výkon propojených serverů může být ovlivněn <a href="http://msdn.microsoft.com/en-us/library/ms178532.aspx"><code>sp\_serveroption</code></a> . nastavení <code>collation compatible</code> říká vše</p><blockquote> <p>pokud je tato možnost nastavena na true, sql server předpokládá, že všechny znaky na propojeném serveru jsou kompatibilní s místním serverem, pokud jde o znakovou sadu a pořadí řazení (nebo pořadí řazení). to umožňuje serveru sql server odesílat porovnání sloupců znaků zprostředkovateli. pokud tato možnost není nastavena, sql server vždy vyhodnocuje porovnání na sloupcích znaků místně.</p> </blockquote><p>to znamená, zkuste nenechat sql server zpracovat data místně.</p><p>poznámka: na mé <code>foo = 'bar'</code> druhý příklad výše je filtr odeslán na propojený server, protože je to pouze řetězcová konstanta na sql server. skutečná klauzule where v prvním příkladu může nebo nemusí být zaslána vzdáleně.</p><p>nakonec jsem také zjistil, že rozložení dat do dočasné tabulky a jejich připojení k místním tabulkám je často lepší než připojení přímo k openquery.</p> <p>na základě marianova návrhu jsem našel <a href="https://dba.stackexchange.com/questions/688/replicating-a-remote-mysql-database-to-ms-sql-server-2008/689#689">tato odpověď</a> o replikaci v opačném směru nastavením mysql serveru jako propojeného serveru v ms sql serveru. když je mysql nastaven jako propojený server, mohu současně spouštět dotazy sql v obou databázích, což poskytuje přesně funkčnost, kterou jsem potřeboval k vyřešení tohoto problému.</p> <p>to, co chcete, je možné, ale bude vyžadovat, abyste se ponořili do<strong> sql, kterému se chci vyhnout, kdykoli je to možné </strong>(ne proto, že to nevím, jsem vývojář sql, ale protože<strong> in wordpress chcete použít api kdykoli je to možné </strong>minimalizovat budoucí problémy s kompatibilitou související s budoucími potenciálními změnami struktury databáze.)</p><h2>sql s operátorem <code>union</code> je možnost</h2><p>chcete-li použít sql, co potřebujete, je operátor <code>union</code> ve vašem dotazu, něco takového za předpokladu, že vaše kategorie slugs jsou <code>"category-1"</code>, <code>"category-1"</code> a <code>"category-3"</code>:</p><pre><code>select * from wp\_posts where id in ( select tr.object\_id from wp\_terms t inner join wp\_term\_taxonomy tt on t.term\_id = tt.term\_id inner join wp\_term\_relationships tr on tt.term\_taxonomy\_id = tr.term\_taxonomy\_id where tt.taxonomy='category' and t.slug='category-1' limit 5 union select tr.object\_id from wp\_terms t inner join wp\_term\_taxonomy tt on t.term\_id = tt.term\_id inner join wp\_term\_relationships tr on tt.term\_taxonomy\_id = tr.term\_taxonomy\_id where tt.taxonomy='category' and t.slug='category-2' limit 5 union select tr.object\_id from wp\_terms t inner join wp\_term\_taxonomy tt on t.term\_id = tt.term\_id inner join wp\_term\_relationships tr on tt.term\_taxonomy\_id = tr.term\_taxonomy\_id where tt.taxonomy='category' and t.slug='category-3' limit 5 ) </code></pre><h2>sql union můžete použít s <code>posts\_join</code> filtrem</h2><p>pomocí výše uvedeného můžete buď<strong> jen zavolat přímo </strong>nebo můžete<strong> použít <code>posts\_join</code> háček filtru </strong>jako následovně; poznámka používám <a href="http://www.phpf1.com/tutorial/php-heredoc-syntax.html"><strong> php heredoc </strong></a> , takže se ujistěte, že <code>sql;</code> je flush vlevo. také jsem si všiml, že jsem použil globální var, abych vám umožnil definovat kategorie mimo háček výčtem kategorie slimáků v poli. tento kód můžete umístit do pluginu nebo do souboru <code>functions.php</code> motivu:</p><pre><code>&lt;?php global $top\_5\_for\_each\_category\_join; $top\_5\_for\_each\_category\_join = array('category-1','category-2','category-3'); add\_filter('posts\_join','top\_5\_for\_each\_category\_join',10,2); function top\_5\_for\_each\_category\_join($join,$query) { global $top\_5\_for\_each\_category\_join; $unioned\_selects = array(); foreach($top\_5\_for\_each\_category\_join as $category) { $unioned\_selects[] =&lt;&lt;&lt;sql select object\_id from wp\_terms t inner join wp\_term\_taxonomy tt on t.term\_id = tt.term\_id inner join wp\_term\_relationships tr on tt.term\_taxonomy\_id = tr.term\_taxonomy\_id where tt.taxonomy='category' and t.slug='{$category}' limit 5 sql; } $unioned\_selects = implode("\n\nunion\n\n",$unioned\_selects); return $join . " inner join ($unioned\_selects) categories on wp\_posts.id=categories.object\_id " ; } </code></pre><h2>ale mohou být vedlejší účinky</h2><p>samozřejmě s použitím háčků pro modifikaci dotazů, jako je <code>posts\_join</code>, vždy zvou vedlejší efekty v tom, že působí globálně na dotazy, a proto je obvykle nutné zabalit vaše změny do <code>if</code>, které je používají pouze v případě potřeby a jaká kritéria pro testování mohou být složitá.</p><h2>zaměřte se spíše na optimalizaci?</h2><p>domnívám se však, že vaše otázka se týká<strong> více o optimalizaci </strong>než o tom, že dokážete udělat 5 nejlepších dotazů v čase 3, ne? pokud tomu tak je, pak možná existují další možnosti, které používají wordpress api?</p><h2>lepší je použití přechodových api pro ukládání do mezipaměti?</h2><p>předpokládám, že vaše příspěvky se nezmění tak často, správně? co když přijmete periodicky tří (3) dotaz a poté výsledky uložíte pomocí <a href="http://codex.wordpress.org/transients\_api"><strong> transients api </strong></a> ? získáte udržovatelný kód a skvělý výkon; dobrý bit lepší než <code>union</code> dotaz výše, protože wordpress uloží seznamy příspěvků jako serializované pole v jednom záznamu tabulky <code>wp\_options</code>.</p><p>chcete-li si to vyzkoušet, můžete provést následující příklad a vložit jej do kořenového adresáře webu jako <code>test.php</code>:</p><pre><code>&lt;?php $timeout = 4; // 4 hours $categories = array('category-1','category-2','category-3'); include "wp-load.php"; $category\_posts = get\_transient('top5\_posts\_per\_category'); if (!is\_array($category\_posts) || count($category\_posts)==0) { echo "hello every {$timeout} hours!"; $category\_posts = array(); foreach($categories as $category) { $posts = get\_posts("post\_type=post&amp;numberposts=5&amp;taxonomy=category&amp;term={$category}"); foreach($posts as $post) { $category\_posts[$post-&gt;id] = $post; } } set\_transient('top5\_posts\_per\_category',$category\_posts,60*60*$timeout); } header('content-type:text/plain'); print\_r($category\_posts); </code></pre><h2>souhrn</h2><p>zatímco ano, můžete udělat to, co jste požadovali pro použití dotazu sql <code>union</code> a háčku <code>posts\_join</code>, což je pravděpodobně lepší nabídka pomocí mezipaměti s<strong> <em>transients api</em> </strong>.</p><p>snad to pomůže?</p> <p>tady je to, co jsem udělal předtím:</p><pre><code>(select 'tablea', * from tablea except select 'tablea', * from tableb) union all (select 'tableb', * from tableb except select 'tableb', * from tablea) </code></pre><p>funguje to dost dobře na stolech, které mají asi 1 000 000 řádků, ale nejsem si jistý, jak dobře by to fungovalo na extrémně velkých stolech.</p><p><strong>přidáno:</strong></p><p>spustil jsem dotaz proti svému systému, který porovnává dvě tabulky s 21 poli běžných typů ve dvou různých databázích připojených ke stejnému serveru se systémem sql server 2005. tabulka obsahuje asi 3 miliony řádků a liší se přibližně 25 000 řádků. primární klíč v tabulce je však zvláštní, protože se jedná o složený klíč z 10 polí (je to kontrolní tabulka).</p><p>plány provádění dotazů mají celkové náklady 184,255879 pro <code>union</code> a 184,228383 pro <code>union all</code>. cena stromu se liší pouze v posledním kroku před vrácením řádků, zřetězením.</p><p>skutečné provedení jednoho dotazu trvá asi 42 s plus asi 3 s, než se řádky skutečně přenesou. čas mezi dvěma dotazy je stejný.</p><p><strong>druhý dodatek:</strong></p><p>toto je ve skutečnosti <em> extrémně </em> rychlé, každý z nich běží proti 3 milionům řádků přibližně za 2,5 s:</p><pre><code>select checksum\_agg(binary\_checksum(*)) from tablea select checksum\_agg(binary\_checksum(*)) from tableb </code></pre><p>pokud se výsledky těchto neshodují, víte, že tabulky jsou odlišné. pokud se však výsledky <em> shodují </em>, nemáte <em> </em> zaručeno, že tabulky jsou totožné kvůli [velmi nepravděpodobné] možnosti kolize kontrolního součtu.</p><p>nejsem si jistý, jak změny datového typu mezi tabulkami ovlivní tento výpočet. spustil bych dotaz proti zobrazením <code>system</code> nebo <code>information\_schema</code> zobrazení.</p><p>zkoušel jsem dotaz proti jiné tabulce s 5 miliony řádků a ten běžel asi za 5 s, takže se zdá být převážně o (n).</p> <p>měli byste se držet společné úmluvy o něčem takovém.</p><p>pokud jsou vstupy nastaveny jako tabulkový procesor, lidé pochopí, že stisknutím záložky přejdou na další záznam a první záznam bude zpracován.</p><p>protože říkáte, že se jedná o aplikaci technické podpory, nezní to tak, jako byste to udělali, takže bych řekl, že byste měli mít tlačítko.</p><p>to znamená, že profesionální uživatelé vždy ocení klávesové zkratky, takže práce s kartou <strong>také</strong> zní jako dobrý nápad.</p> <p>příkaz <code>merge</code> má složitou syntaxi a ještě složitější implementaci, ale v zásadě jde o spojení dvou tabulek, filtrování dolů na řádky, které je třeba změnit (vložit, aktualizovat nebo smazat), a poté provést požadované změny. vzhledem k těmto vzorovým údajům:</p><pre><code>declare @categoryitem as table ( categoryid integer not null, itemid integer not null, primary key (categoryid, itemid), unique (itemid, categoryid) ); declare @datasource as table ( categoryid integer not null, itemid integer not null primary key (categoryid, itemid) ); insert @categoryitem (categoryid, itemid) values (1, 1), (1, 2), (1, 3), (2, 1), (2, 3), (3, 5), (3, 6), (4, 5); insert @datasource (categoryid, itemid) values (2, 2); </code></pre><p><strong> cíl </strong></p><pre><code>╔════════════╦════════╗ ║ categoryid ║ itemid ║ ╠════════════╬════════╣ ║ 1 ║ 1 ║ ║ 2 ║ 1 ║ ║ 1 ║ 2 ║ ║ 1 ║ 3 ║ ║ 2 ║ 3 ║ ║ 3 ║ 5 ║ ║ 4 ║ 5 ║ ║ 3 ║ 6 ║ ╚════════════╩════════╝ </code></pre><p><strong> zdroj </strong></p><pre><code>╔════════════╦════════╗ ║ categoryid ║ itemid ║ ╠════════════╬════════╣ ║ 2 ║ 2 ║ ╚════════════╩════════╝ </code></pre><p>požadovaným výsledkem je nahrazení dat v cíli daty ze zdroje, ale pouze pro <code>categoryid = 2</code>. po výše uvedeném popisu <code>merge</code> bychom měli napsat dotaz, který spojuje zdroj a cíl pouze na klíče, a filtrovat řádky pouze v klauzulích <code>when</code>:</p><pre><code>merge into @categoryitem as target using @datasource as source on source.itemid = target.itemid and source.categoryid = target.categoryid when not matched by source and target.categoryid = 2 then delete when not matched by target and source.categoryid = 2 then insert (categoryid, itemid) values (categoryid, itemid) output $action, isnull(inserted.categoryid, deleted.categoryid) as categoryid, isnull(inserted.itemid, deleted.itemid) as itemid ; </code></pre><p>to dává následující výsledky:</p><pre><code>╔═════════╦════════════╦════════╗ ║ $action ║ categoryid ║ itemid ║ ╠═════════╬════════════╬════════╣ ║ delete ║ 2 ║ 1 ║ ║ insert ║ 2 ║ 2 ║ ║ delete ║ 2 ║ 3 ║ ╚═════════╩════════════╩════════╝ ╔════════════╦════════╗ ║ categoryid ║ itemid ║ ╠════════════╬════════╣ ║ 1 ║ 1 ║ ║ 1 ║ 2 ║ ║ 1 ║ 3 ║ ║ 2 ║ 2 ║ ║ 3 ║ 5 ║ ║ 3 ║ 6 ║ ║ 4 ║ 5 ║ ╚════════════╩════════╝ </code></pre><p>realizační plán je: <img src="https://i.stack.imgur.com/fovhu.jpg" alt="merge plan"></p><p>všimněte si, že jsou obě tabulky plně naskenovány. můžeme to považovat za neefektivní, protože pouze řádky, kde <code>categoryid = 2</code> bude ovlivněno v cílové tabulce. zde přicházejí varování v books online. jeden chybný pokus o optimalizaci, aby se dotkl pouze nezbytných řádků v cíli, je:</p><pre><code>merge into @categoryitem as target using ( select categoryid, itemid from @datasource as ds where categoryid = 2 ) as source on source.itemid = target.itemid and target.categoryid = 2 when not matched by target then insert (categoryid, itemid) values (categoryid, itemid) when not matched by source then delete output $action, isnull(inserted.categoryid, deleted.categoryid) as categoryid, isnull(inserted.itemid, deleted.itemid) as itemid ; </code></pre><p>logika v klauzuli <code>on</code> je použita jako součást spojení. v tomto případě je spojení úplným vnějším spojením (viz <a href="http://technet.microsoft.com/en-us/library/bb522522%28v=sql.105%29.aspx">tento záznam books online</a> proč). použití kontroly kategorie 2 na cílových řádcích jako součásti vnějšího spojení nakonec povede k odstranění řádků s jinou hodnotou (protože neodpovídají zdroji):</p><pre><code>╔═════════╦════════════╦════════╗ ║ $action ║ categoryid ║ itemid ║ ╠═════════╬════════════╬════════╣ ║ delete ║ 1 ║ 1 ║ ║ delete ║ 1 ║ 2 ║ ║ delete ║ 1 ║ 3 ║ ║ delete ║ 2 ║ 1 ║ ║ insert ║ 2 ║ 2 ║ ║ delete ║ 2 ║ 3 ║ ║ delete ║ 3 ║ 5 ║ ║ delete ║ 3 ║ 6 ║ ║ delete ║ 4 ║ 5 ║ ╚═════════╩════════════╩════════╝ ╔════════════╦════════╗ ║ categoryid ║ itemid ║ ╠════════════╬════════╣ ║ 2 ║ 2 ║ ╚════════════╩════════╝ </code></pre><p>kořenová příčina je ze stejného důvodu, proč se predikáty chovají v klauzuli vnější spojení <code>on</code> odlišně, než když jsou specifikovány v klauzuli <code>where</code>. syntaxe <code>merge</code> (a implementace spojení v závislosti na zadaných klauzulích) jen ztěžují vidět, že tomu tak je.</p><p> <a href="http://technet.microsoft.com/en-us/library/bb510625%28v=sql.105%29.aspx">navádění v books online</a> (rozšířeno v položce <a href="http://technet.microsoft.com/en-us/library/cc879317%28v=sql.105%29.aspx">optimizing performance</a> ) nabízí navádění, které zajistí, že správná sémantika je vyjádřena pomocí syntaxe <code>merge</code>, aniž by uživatel nutně musel muset pochopit všechny podrobnosti implementace nebo zohlednit způsoby, kterými by optimalizátor mohl legitimně přeskupit věci z důvodů efektivnosti provádění.</p><p>dokumentace nabízí tři možné způsoby implementace časného filtrování:</p><p><strong> zadání podmínky filtrování v klauzuli <code>when</code> </strong> zaručuje správné výsledky, ale může znamenat, že více řádků je čteno a zpracováno ze zdroje a cíle tabulky, než je nezbytně nutné (viz první příklad).</p><p><strong> aktualizace prostřednictvím zobrazení </strong>, která obsahuje podmínku filtrování, také zaručuje správné výsledky (protože změněné řádky musí být přístupné pro aktualizaci prostřednictvím zobrazení), ale vyžaduje to vyhrazené zobrazení, které splňuje liché podmínky pro aktualizaci zobrazení.</p><p><strong> použití společného výrazu tabulky </strong> přináší podobné riziko při přidávání predikátů k klauzuli <code>on</code>, ale z poněkud odlišných důvodů. v mnoha případech to bude bezpečné, ale vyžaduje to odbornou analýzu prováděcího plánu, aby se to potvrdilo (a rozsáhlé praktické testování). například:</p><pre><code>with target as ( select * from @categoryitem where categoryid = 2 ) merge into target using ( select categoryid, itemid from @datasource where categoryid = 2 ) as source on source.itemid = target.itemid and source.categoryid = target.categoryid when not matched by target then insert (categoryid, itemid) values (categoryid, itemid) when not matched by source then delete output $action, isnull(inserted.categoryid, deleted.categoryid) as categoryid, isnull(inserted.itemid, deleted.itemid) as itemid ; </code></pre><p>výsledkem jsou správné výsledky (neopakované) s optimálnějším plánem:</p><p><img src="https://i.stack.imgur.com/tazmk.jpg" alt="merge plan 2"></p><p>plán čte pouze řádky pro kategorii 2 z cílové tabulky. to může být důležitým hlediskem výkonu, pokud je cílová tabulka velká, ale je příliš snadné tuto chybu získat pomocí syntaxe <code>merge</code>.</p><p>někdy je snazší zapsat <code>merge</code> jako samostatné operace dml. tento přístup může dokonce <strong> vést lépe </strong> než jediný <code>merge</code>, což je skutečnost, která lidi často překvapuje.</p><pre><code>delete ci from @categoryitem as ci where ci.categoryid = 2 and not exists ( select 1 from @datasource as ds where ds.itemid = ci.itemid and ds.categoryid = ci.categoryid ); insert @categoryitem select ds.categoryid, ds.itemid from @datasource as ds where ds.categoryid = 2; </code></pre>