sql >> Databáze >  >> RDS >> Sqlserver

nvarchar zřetězení / index / nvarchar (max) nevysvětlitelné chování

TLDR; Toto není zdokumentovaný/podporovaný přístup pro zřetězení řetězců přes řádky. Někdy to funguje, ale také někdy selže, protože záleží na tom, jaký plán provádění dostanete.

Místo toho použijte jeden z následujících zaručených přístupů

SQL Server 2017+

SELECT @a = STRING_AGG([msg], '') WITHIN GROUP (ORDER BY [priority] ASC)
FROM bla
where   autofix = 0

SQL Server 2005+

SELECT @a = (SELECT [msg] + ''
             FROM   bla
             WHERE  autofix = 0
             ORDER  BY [priority] ASC
             FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)') 

Pozadí

Článek KB, na který již odkazoval VanDerNorth, tento řádek obsahuje

Správné chování pro agregační zřetězení není definováno.

ale pak jde trochu zabahnit vody poskytnutím řešení, které, jak se zdá, naznačuje, že deterministické chování je možné.

Chcete-li dosáhnout očekávaných výsledků z agregačního zřetězení dotazu, použijte jakoukoli funkci nebo výraz Transact-SQL na sloupce v seznamu SELECT, nikoli na klauzuli ORDER BY.

Váš problematický dotaz neaplikuje žádné výrazy na sloupce v ORDER BY doložka.

Článek z roku 2005 Ordering Guarantees in SQL Server... uvádí

Z důvodů zpětné kompatibility poskytuje SQL Server podporu pro přiřazení typu SELECT @p =@p + 1 ... ORDER BY v nejvyšším rozsahu.

V plánech, kde zřetězení funguje tak, jak jste očekávali, vypočítejte skalár s výrazem [Expr1003] = Scalar Operator([@x]+[Expr1004]) se zobrazí nad řazením.

V plánu, kde nefunguje, se pod řazením objeví výpočetní skalár. Jak je vysvětleno v této položce připojení z roku 2006, kdy výraz @x = @x + [msg] se zobrazí pod řazením, které je vyhodnoceno pro každý řádek, ale všechna hodnocení skončí pomocí hodnoty před přiřazením @x . V další podobné položce Connect z roku 2006 odpověď od Microsoftu hovořila o „opravě“ problému.

Odpověď společnosti Microsoft u všech pozdějších položek Connect k tomuto problému (a je jich mnoho) uvádí, že to prostě není zaručeno

Příklad 1

neposkytujeme žádné záruky za správnost zřetězení dotazů (jako je použití proměnných přiřazení s načítáním dat v určitém pořadí). Výstup dotazu se může v SQL Server 2008 měnit v závislosti na volbě plánu, datech v tabulkách atd. Neměli byste se spoléhat na to, že bude fungovat konzistentně, i když vám syntaxe umožňuje napsat příkaz SELECT, který kombinuje načítání uspořádaných řádků s přiřazením proměnných.

Příklad 2

Chování, které vidíte, je záměrné. Použití operací přiřazení (v tomto příkladu zřetězení) v dotazech s klauzulí ORDER BY má nedefinované chování. To se může změnit od vydání k vydání nebo dokonce v rámci konkrétní verze serveru kvůli změnám v plánu dotazů. Na toto chování se nemůžete spolehnout, i když existují zástupná řešení. Další podrobnosti naleznete v následujícím článku znalostní databáze:
http://support.microsoft.com/kb/287515 JEDINÝ zaručený mechanismus je následující:

  1. Pomocí kurzoru procházejte řádky v určitém pořadí a spojte hodnoty
  2. Použijte pro xml dotaz s ORDER BY ke generování zřetězených hodnot
  3. Použijte agregát CLR (toto nebude fungovat s klauzulí ORDER BY)

Příklad 3

Chování, které vidíte, je ve skutečnosti záměrné. To souvisí s tím, že SQL je jazyk pro manipulaci se sadami. U všech výrazů v seznamu SELECTlist (a to zahrnuje i přiřazení) není zaručeno, že budou provedeny přesně jednou pro každý výstupní řádek. Ve skutečnosti se SQL queryoptimizer snaží provést je co nejméněkrát. To poskytne očekávané výsledky, když počítáte hodnotu proměnné na základě některých dat v tabulkách, ale když hodnota, kterou přiřazujete, závisí na předchozí hodnotě stejné proměnné, mohou být výsledky docela neočekávané. Pokud optimalizátor dotazů přesune výraz na jiné místo ve stromu dotazů, může být vyhodnocen méněkrát (nebo jen jednou, jako v jednom z vašich příkladů). To je důvod, proč nedoporučujeme používat přiřazení typu "iterace" k výpočtu agregovaných hodnot. Zjistili jsme, že řešení založená na XML... obvykle pro zákazníky fungují dobře

Příklad 4

Ani bez ORDER BY nezaručujeme, že @var =@var + vytvoří zřetězenou hodnotu pro jakýkoli příkaz, který ovlivňuje více řádků. Pravá strana výrazu může být vyhodnocena buď jednou nebo vícekrát během provádění dotazu a chování, jak jsem řekl, je závislé na plánu.

Příklad 5

Přiřazení proměnné pomocí příkazu SELECT je proprietární syntaxe (pouze T-SQL), kde chování není definováno nebo pokud je vytvořeno více řádků, závisí na plánu. Pokud potřebujete provést zřetězení řetězce, použijte agregaci SQLCLR nebo zřetězení založené na dotazu FOR XML nebo jiné relační metody.



  1. Existuje ekvivalent Profileru pro MySql?

  2. Načítání propojeného seznamu v databázi MySQL

  3. Jak funguje NULLIF() na serveru SQL Server

  4. Proč se učit Cassandru s Hadoopem?