sql >> Databáze >  >> RDS >> Database

Minimální protokolování s INSERT…SELECT a Fast Load Context

Tento příspěvek poskytuje nové informace o předpokladech pro minimálně protokolované hromadné zatížení při použití INSERT...SELECT do indexovaných tabulek .

Interní zařízení, které umožňuje tyto případy, se nazývá FastLoadContext . Lze jej aktivovat od SQL Server 2008 až 2014 včetně pomocí zdokumentovaného příznaku trasování 610. Od SQL Server 2016 dále, FastLoadContext je ve výchozím nastavení povoleno; příznak trasování není vyžadován.

Bez FastLoadContext , jediné indexové vložky, které lze minimálně protokolovat jsou do prázdného seskupený index bez sekundárních indexů, jak je popsáno ve druhé části této řady. Minimální protokolování podmínky pro neindexované tabulky haldy byly popsány v první části.

Další informace naleznete v příručce Data Performance Loading Guide a Tiger Team poznámky ke změnám chování pro SQL Server 2016.

Kontext rychlého načítání

Pro rychlé připomenutí, RowsetBulk zařízení (popsané v částech 1 a 2) umožňuje minimálně protokolované hromadné zatížení pro:

  • Prázdná a neprázdná hromada tabulky s:
    • Zamykání stolu; a
    • Žádné sekundární indexy.
  • Prázdné seskupené tabulky , s:
    • Zamykání stolu; a
    • Žádné sekundární indexy; a
    • DMLRequestSort=true na Vložení seskupeného rejstříku operátor.

FastLoadContext kódová cesta přidává podporu pro minimálně protokolované a souběžně hromadné zatížení na:

  • Prázdné a neprázdné seskupené indexy b-stromu.
  • Prázdné a neprázdné neshlukované b-tree indexy spravované vyhrazeným Vložení indexu operátor plánu.

FastLoadContext také vyžaduje DMLRequestSort=true na příslušného operátora plánu ve všech případech.

Možná jste si všimli překrývání mezi RowsetBulk a FastLoadContext pro prázdné seskupené tabulky bez sekundárních indexů. TABLOCK nápověda není vyžadována pomocí FastLoadContext , ale není vyžadováno, aby chyběl buď. V důsledku toho vhodná vložka s TABLOCK může stále splňovat podmínky pro minimální protokolování prostřednictvím FastLoadContext pokud selže podrobný RowsetBulk testy.

FastLoadContext lze zakázat na SQL Server 2016 pomocí zdokumentovaného příznaku trasování 692. Rozšířená událost kanálu ladění fastloadcontext_enabled lze použít ke sledování FastLoadContext využití na oddíl indexu (sadu řádků). Tato událost se pro RowsetBulk nespustí načte.

Smíšené protokolování

Jediný INSERT...SELECT pomocí FastLoadContext může se plně přihlásit některé řádky při minimálním protokolování ostatní.

Řádky se vkládají po jednom pomocí vložení indexu operátor a plně přihlášeni v následujících případech:

  • Všechny řádky přidány do prvního stránka indexu, pokud index byl prázdný na začátku operace.
  • Řádky byly přidány do existujících indexové stránky.
  • Řádky přesunuty mezi stránkami o rozdělení stránky.

V opačném případě se na zcela novou stránku přidají řádky z objednaného streamu vložení pomocí optimalizovaného a minimálně protokolovaného cesta kódu. Jakmile je na novou stránku zapsáno co nejvíce řádků, je tato stránka přímo propojena se stávající strukturou cílového indexu.

Nově přidaná stránka nemusí být být plný (ačkoli to je samozřejmě ideální případ), protože SQL Server si musí dávat pozor, aby na novou stránku nepřidal řádky, které logicky patří na existující indexová stránka. Nová stránka bude „sešita“ do indexu jako jednotka, takže na nové stránce nemůžeme mít žádné řádky, které by patřily jinam. Jde především o problém při přidávání řádků v rámci stávající rozsah klíčů indexu, spíše než před začátkem nebo po konci stávajícího rozsahu klíčů indexu.

Stále je to možné přidat nové stránky v rámci stávající rozsah klíče indexu, ale nové řádky musí být seřazeny výše než nejvyšší klíč v předchozím existující stránku indexu a seřadit níže než nejnižší klávesa na následujících existující indexovou stránku. Pro nejlepší šanci na dosažení minimálního protokolování za těchto okolností zajistěte, aby se vložené řádky pokud možno nepřekrývaly s existujícími řádky.

Podmínky DLRequestSort

Pamatujte, že FastLoadContext lze aktivovat pouze v případě, že DMLRequestSort je nastaveno na true pro odpovídající Vložení indexu operátora v prováděcím plánu.

Existují dvě hlavní cesty kódu, které mohou nastavit DMLRequestSort pravda pro indexové vložky. Jakákoli cesta vrací true je dostačující.

1. FOptimizeInsert

sqllang!CUpdUtil::FOptimizeInsert kód vyžaduje:

  • Více než 250 řádků odhadem vložit; a
  • Více než 2 stránky odhadem vložit velikost dat; a
  • cílový index musí mít méně než 3 listové stránky .

Tyto podmínky jsou stejné jako RowsetBulk na prázdném seskupeném indexu s dodatečným požadavkem na maximálně dvě stránky na úrovni listu indexu. Pozorně si všimněte, že se jedná o velikost existujícího indexu před vložkou, ne odhadovaná velikost dat, která mají být přidána.

Níže uvedený skript je modifikací dema použitého v dřívějších dílech této série. Zobrazuje minimální protokolování když jsou před naplněny méně než tři indexové stránky test INSERT...SELECT běží. Schéma testovací tabulky je takové, že se na jednu stránku o velikosti 8 kB vejde 130 řádků, když je pro databázi vypnuté verzování řádků. Násobitel v prvním TOP klauzuli lze změnit a určit tak počet existujících stránek indexu před test INSERT...SELECT se provede:

IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL
BEGIN
    DROP TABLE dbo.Test;
END;
GO
CREATE TABLE dbo.Test 
(
    id integer NOT NULL IDENTITY
        CONSTRAINT [PK dbo.Test (id)]
        PRIMARY KEY,
    c1 integer NOT NULL,
    padding char(45) NOT NULL
        DEFAULT ''
);
GO
-- 130 rows per page for this table 
-- structure with row versioning off
INSERT dbo.Test
    (c1)
SELECT TOP (3 * 130)    -- Change the 3 here
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV;
GO
-- Show physical index statistics
-- to confirm the number of pages
SELECT
    DDIPS.index_type_desc,
    DDIPS.alloc_unit_type_desc,
    DDIPS.page_count,
    DDIPS.record_count,
    DDIPS.avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats
(
    DB_ID(), 
    OBJECT_ID(N'dbo.Test', N'U'), 
    1,      -- Index ID
    NULL,   -- Partition ID
    'DETAILED'
) AS DDIPS
WHERE
    DDIPS.index_level = 0;  -- leaf level only
GO
-- Clear the plan cache
DBCC FREEPROCCACHE;
GO
-- Clear the log
CHECKPOINT;
GO
-- Main test
INSERT dbo.Test
    (c1)
SELECT TOP (269)
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV;
GO
-- Show log entries
SELECT
    FD.Operation,
    FD.Context,
    FD.[Log Record Length],
    FD.[Log Reserve],
    FD.AllocUnitName,
    FD.[Transaction Name],
    FD.[Lock Information],
    FD.[Description]
FROM sys.fn_dblog(NULL, NULL) AS FD;
GO
-- Count the number of  fully-logged rows
SELECT 
    [Fully Logged Rows] = COUNT_BIG(*) 
FROM sys.fn_dblog(NULL, NULL) AS FD
WHERE 
    FD.Operation = N'LOP_INSERT_ROWS'
    AND FD.Context = N'LCX_CLUSTERED'
    AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)';
GO

Když je v seskupeném indexu předem načten 3 stránky , testovací vložka je plně přihlášena (podrobné záznamy protokolu transakcí jsou pro stručnost vynechány):

Když je v tabulce předem načtena pouze 1 nebo 2 stránky , testovací vložka je minimálně přihlášena :

Když tabulka není předem načtena se všemi stránkami je test ekvivalentní spuštění prázdné ukázky seskupené tabulky z druhé části, ale bez TABLOCK nápověda:

Prvních 130 řádků je plně protokolováno . Důvodem je, že index byl prázdný, než jsme začali, a na první stránku se vešlo 130 řádků. Pamatujte, že při FastLoadContext je vždy plně zaprotokolována první stránka a index byl předem prázdný. Zbývajících 139 řádků je vloženo s minimálním protokolováním .

Pokud TABLOCK nápověda je přidána do přílohy, všechny stránky jsou minimálně protokolovány (včetně prvního), protože prázdné načtení klastrovaného indexu se nyní kvalifikuje pro RowsetBulk mechanismus (za cenu převzetí Sch-M zámek).

2. FDemandRowsSortedForPerformance

Pokud FOptimizeInsert testy se nezdaří, DMLRequestSort může být stále nastaveno na pravda pomocí druhé sady testů v sqllang!CUpdUtil::FDemandRowsSortedForPerformance kód. Tyto podmínky jsou trochu složitější, takže bude užitečné definovat některé parametry:

  • P – počet existujících stránek na úrovni listu v cílovém indexu .
  • Iodhadem počet řádků k vložení.
  • R =P / I (cílové stránky na vložený řádek).
  • T – počet cílových oddílů (1 pro nerozdělené).

Logika k určení hodnoty DMLRequestSort je pak:

  • Pokud P <= 16 vrátit false , jinak :
    • Pokud R < 8 :
      • Pokud P > 524 vrátit pravda , jinak false .
    • Pokud R >= 8 :
      • Pokud T > 1 a I > 250 vrátit pravda , jinak false .

Výše uvedené testy jsou vyhodnoceny procesorem dotazů během sestavování plánu. Existuje konečná podmínka hodnoceno kódem úložiště (IndexDataSetSession::WakeUpInternal ) v době provádění:

  • DMLRequestSort je aktuálně pravda; a
  • I >= 100 .

Příště celou tuto logiku rozdělíme na zvládnutelné kousky.

Více než 16 stávajících cílových stránek

První test P <= 16 znamená, že indexy s méně než 17 existujícími listovými stránkami nebudou způsobilé pro FastLoadContext přes tuto kódovou cestu. Aby bylo v tomto bodě naprosto jasno, P je počet stránek na úrovni listu v cílovém indexu před INSERT...SELECT se provede.

Abychom tuto část logiky demonstrovali, předem načteme testovací seskupenou tabulku s 16 stránkami dat. To má dva důležité efekty (pamatujte, že obě cesty kódu musí vrátit false skončí s nepravdou hodnotu pro DMLRequestSort ):

  1. Zajistí, že předchozí FOptimizeInsert test selhal , protože není splněna třetí podmínka (P < 3 ).
  2. P <= 16 stav v FDemandRowsSortedForPerformance také nebude být splněn.

Proto očekáváme FastLoadContext nebude povoleno. Upravený demo skript je:

IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL
BEGIN
    DROP TABLE dbo.Test;
END;
GO
CREATE TABLE dbo.Test 
(
    id integer NOT NULL IDENTITY
        CONSTRAINT [PK dbo.Test (id)]
        PRIMARY KEY,
    c1 integer NOT NULL,
    padding char(45) NOT NULL
        DEFAULT ''
);
GO
-- 130 rows per page for this table 
-- structure with row versioning off
INSERT dbo.Test
    (c1)
SELECT TOP (16 * 130) -- 16 pages
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV;
GO
-- Show physical index statistics
-- to confirm the number of pages
SELECT
    DDIPS.index_type_desc,
    DDIPS.alloc_unit_type_desc,
    DDIPS.page_count,
    DDIPS.record_count,
    DDIPS.avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats
(
    DB_ID(), 
    OBJECT_ID(N'dbo.Test', N'U'), 
    1,      -- Index ID
    NULL,   -- Partition ID
    'DETAILED'
) AS DDIPS
WHERE
    DDIPS.index_level = 0;  -- leaf level only
GO
-- Clear the plan cache
DBCC FREEPROCCACHE;
GO
-- Clear the log
CHECKPOINT;
GO
-- Main test
INSERT dbo.Test
    (c1)
SELECT TOP (269)
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV1
CROSS JOIN master.dbo.spt_values AS SV2;
GO
-- Show log entries
SELECT
    FD.Operation,
    FD.Context,
    FD.[Log Record Length],
    FD.[Log Reserve],
    FD.AllocUnitName,
    FD.[Transaction Name],
    FD.[Lock Information],
    FD.[Description]
FROM sys.fn_dblog(NULL, NULL) AS FD;
GO
-- Count the number of  fully-logged rows
SELECT 
    [Fully Logged Rows] = COUNT_BIG(*) 
FROM sys.fn_dblog(NULL, NULL) AS FD
WHERE 
    FD.Operation = N'LOP_INSERT_ROWS'
    AND FD.Context = N'LCX_CLUSTERED'
    AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)';

Všech 269 řádků je plně přihlášeno podle předpovědi:

Všimněte si, že bez ohledu na to, jak vysoký počet nových řádků k vložení nastavíme, výše uvedený skript nikdy nebude produkovat minimální protokolování kvůli P <= 16 test (a P < 3 otestujte v FOptimizeInsert ).

Pokud se rozhodnete spustit demo sami s větším počtem řádků, zakomentujte sekci, která zobrazuje jednotlivé záznamy protokolu transakcí, jinak budete čekat velmi dlouho a SSMS může selhat. (Abych byl spravedlivý, může to udělat i tak, ale proč přidávat na riziku.)

Poměr stránek na vložený řádek

Pokud je jich 17 nebo více listové stránky ve stávajícím indexu, předchozí P <= 16 test neprojde. Další část logiky se zabývá poměrem existujících stránek do nově vložených řádků . To musí také projít, aby bylo dosaženo minimálního protokolování . Pro připomenutí, relevantní podmínky jsou:

  • Poměr R =P / I .
  • Pokud R < 8 :
    • Pokud P > 524 vrátit pravda , jinak false .

Musíme si také pamatovat závěrečný test skladovacího motoru pro alespoň 100 řádků:

  • I >= 100 .

Trochu přeorganizujte ty podmínky, vše z následujícího musí platit:

  1. P > 524 (stávající indexové stránky)
  2. I >= 100 (odhadované vložené řádky)
  3. P / I < 8 (poměr R )

Existuje několik způsobů, jak splnit tyto tři podmínky současně. Zvolme minimální možné hodnoty pro P (525) a I (100) dává R hodnota (525 / 100) =5,25. To splňuje (R < 8 test), takže očekáváme, že tato kombinace povede k minimálnímu protokolování :

IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL
BEGIN
    DROP TABLE dbo.Test;
END;
GO
CREATE TABLE dbo.Test 
(
    id integer NOT NULL IDENTITY
        CONSTRAINT [PK dbo.Test (id)]
        PRIMARY KEY,
    c1 integer NOT NULL,
    padding char(45) NOT NULL
        DEFAULT ''
);
GO
-- 130 rows per page for this table 
-- structure with row versioning off
INSERT dbo.Test
    (c1)
SELECT TOP (525 * 130) -- 525 pages
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV1
CROSS JOIN master.dbo.spt_values AS SV2;
GO
-- Show physical index statistics
-- to confirm the number of pages
SELECT
    DDIPS.index_type_desc,
    DDIPS.alloc_unit_type_desc,
    DDIPS.page_count,
    DDIPS.record_count,
    DDIPS.avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats
(
    DB_ID(), 
    OBJECT_ID(N'dbo.Test', N'U'), 
    1,      -- Index ID
    NULL,   -- Partition ID
    'DETAILED'
) AS DDIPS
WHERE
    DDIPS.index_level = 0;  -- leaf level only
GO
-- Clear the plan cache
DBCC FREEPROCCACHE;
GO
-- Clear the log
CHECKPOINT;
GO
-- Main test
INSERT dbo.Test
    (c1)
SELECT TOP (100)
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV1
CROSS JOIN master.dbo.spt_values AS SV2;
GO
-- Show log entries
SELECT
    FD.Operation,
    FD.Context,
    FD.[Log Record Length],
    FD.[Log Reserve],
    FD.AllocUnitName,
    FD.[Transaction Name],
    FD.[Lock Information],
    FD.[Description]
FROM sys.fn_dblog(NULL, NULL) AS FD;
GO
-- Count the number of  fully-logged rows
SELECT 
    [Fully Logged Rows] = COUNT_BIG(*) 
FROM sys.fn_dblog(NULL, NULL) AS FD
WHERE 
    FD.Operation = N'LOP_INSERT_ROWS'
    AND FD.Context = N'LCX_CLUSTERED'
    AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)';

100řádkový INSERT...SELECT je skutečně minimálně přihlášen :

Snížení odhadu vložené řádky na 99 (přerušení I >= 100 ) a/nebo snížením počtu stávajících stránek indexu na 524 (porušení P > 524 ) vede k úplnému protokolování . Můžeme také provést změny, jako je R již není menší než 8, aby bylo dosaženo úplného protokolování . Například nastavení P = 1000 a I = 125 dává R = 8 , s následujícími výsledky:

125 vložených řádků bylo plně protokolováno podle očekávání. (Není to způsobeno úplným protokolováním na první stránce, protože index nebyl předem prázdný.)

Poměr stránek pro dělené indexy

Pokud všechny předchozí testy selžou, jeden zbývající test vyžaduje R >= 8 a může pouze být spokojen, když počet oddílů (T ) je větší než 1 a existuje více než 250 odhadovaných vložené řádky (I ). Připomeňme:

  • Pokud R >= 8 :
    • Pokud T > 1 a I > 250 vrátit pravda , jinak false .

Jedna jemnost:Pro rozdělené indexy, pravidlo, které říká, že všechny řádky na první stránce jsou plně protokolovány (pro původně prázdný index), platí na oddíl . Pro objekt s 15 000 oddíly to znamená 15 000 plně přihlášených „prvních“ stránek.

Souhrn a závěrečné myšlenky

Vzorce a pořadí vyhodnocení popsané v těle jsou založeny na kontrole kódu pomocí debuggeru. Byly prezentovány ve formě, která přesně reprezentuje načasování a pořadí použité ve skutečném kódu.

Tyto podmínky je možné přeuspořádat a trochu zjednodušit a vytvořit tak stručnější souhrn praktických požadavků na minimální protokolování při vkládání do b-stromu pomocí INSERT...SELECT . Upřesněné výrazy níže používají následující tři parametry:

  • P =počet existujících indexovat stránky na úrovni listu.
  • I =odhad počet řádků k vložení.
  • S =odhad vložte velikost dat ve stránkách o velikosti 8 kB.

Hromadné načtení sady řádků

  • Používá sqlmin!RowsetBulk .
  • Vyžaduje prázdné seskupený cíl indexu s TABLOCK (nebo ekvivalentní).
  • Vyžaduje DMLRequestSort = true na Vložení seskupeného rejstříku operátor.
  • DMLRequestSort je nastavena na true pokud I > 250 a S > 2 .
  • Všechny vložené řádky jsou minimálně protokolovány .
  • Sch-M zámek zabraňuje souběžnému přístupu k tabulce.

Kontext rychlého načítání

  • Používá sqlmin!FastLoadContext .
  • Povoluje minimálně protokolované vloží do indexů b-stromu:
    • Shlukovaný nebo neshlukovaný.
    • Se zámkem stolu nebo bez něj.
    • Cílový index je prázdný nebo není.
  • Vyžaduje DMLRequestSort = true na přidružené Vložení indexu operátor plánu.
  • Pouze řádky zapsané na zcela nové stránky jsou hromadně načteny a minimálně protokolovány .
  • První stránka dříve prázdného indexu oddíl je vždy plně přihlášen .
  • Absolutní minimum I >= 100 .
  • Vyžaduje příznak trasování 610 před SQL Server 2016.
  • Ve výchozím nastavení k dispozici od SQL Server 2016 (příznak trasování 692 deaktivuje).

DMLRequestSort je nastavena na true pro:

  • Jakýkoli index (rozdělené nebo ne) if:
    • I > 250 a P < 3 a S > 2; nebo
    • I >= 100 a P > 524 a P < I * 8

Pouze pro dělené indexy (s> 1 oddílem), DMLRequestSort je také nastaveno na true pokud:

  • I > 250 a P > 16 a P >= I * 8

Existuje několik zajímavých případů vyplývajících z těchto FastLoadContext podmínky:

  • Vše vloží do nerozděleného index s mezi 3 a 524 (včetně) stávající listové stránky budou plně přihlášeny bez ohledu na počet a celkovou velikost přidaných řádků. Nejvýrazněji to ovlivní velké přílohy do malých (ale ne prázdných) tabulek.
  • Vše vloží do rozděleného index s 3 až 16 existující stránky budou plně přihlášeny .
  • Velké vložky na velké nerozdělené indexy nemusí být minimálně protokolovány kvůli nerovnosti P < I * 8 . Když P je velký, odpovídající odhadem počet vložených řádků (I ) je požadováno. Například index s 8 miliony stránek nemůže podporovat minimální protokolování při vkládání 1 milionu řádků nebo méně.

Neclusterované indexy

Stejné úvahy a výpočty použité pro seskupené indexy v ukázkách platí pro nonclustered indexy b-tree také, pokud je index udržován operátorem vyhrazeného plánu (široký nebo na index plán). Neshlukované indexy spravované operátorem základní tabulky (např. Vložení seskupeného indexu ) nejsou způsobilé pro FastLoadContext .

Pamatujte, že parametry vzorce je třeba znovu vyhodnotit pro každý neshlukovaný operátor indexu — vypočítaná velikost řádku, počet existujících stránek indexu a odhad mohutnosti.

Obecné poznámky

Dejte si pozor na nízké odhady mohutnosti na Vložení indexu operátor, protože ty ovlivní I a S parametry. Pokud není prahové hodnoty dosaženo kvůli chybě odhadu mohutnosti, vložka bude úplně zaprotokolována .

Pamatujte, že DMLRequestSort je uloženo do mezipaměti s plánem — nevyhodnocuje se při každém provedení znovu použitého plánu. To může zavést formu dobře známého problému citlivosti parametrů (také známého jako „sniffování parametrů“).

Hodnota P (listové stránky indexu) není obnoveno na začátku každého prohlášení. Aktuální implementace ukládá do mezipaměti hodnotu pro celou dávku . To může mít neočekávané vedlejší účinky. Například TRUNCATE TABLE ve stejné dávce jako INSERT...SELECT neresetuje P na nulu pro výpočty popsané v tomto článku — budou nadále používat hodnotu před oříznutím a rekompilace nepomůže. Řešením je odeslat velké změny v samostatných dávkách.

Příznaky sledování

Je možné vynutit FDemandRowsSortedForPerformance vrátit true nastavením nedokumentované a nepodporované trasovací příznak 2332, jak jsem psal v Optimalizace T-SQL dotazů, které mění data. Když je TF 2332 aktivní, počet odhadovaných řádků k vložení stále musí být nejméně 100 . TF 2332 ovlivňuje minimální protokolování rozhodnutí pro FastLoadContext pouze (je účinné pro rozdělené hromady až po DMLRequestSort se týká, ale nemá žádný vliv na samotnou haldu, protože FastLoadContext platí pouze pro indexy).

široký/na index tvar plánu pro údržbu neshlukovaného indexu lze vynutit pro tabulky rowstore pomocí příznaku trasování 8790 (není oficiálně zdokumentováno, ale zmíněno v článku znalostní báze Knowledge Base a také v mém článku, který je propojen s TF2332 výše).

Vše od Sunila Agarwala z týmu SQL Server:

  • Co jsou optimalizace hromadného importu?
  • Optimalizace hromadného importu (minimální protokolování)
  • Minimální změny protokolování v SQL Server 2008
  • Minimální změny protokolování v SQL Server 2008 (část 2)
  • Minimální změny protokolování v SQL Server 2008 (část 3)

  1. Jak uniknout apostrofu (') v MySql?

  2. Jak zavolám uloženou proceduru s argumenty pomocí sqlcmd.exe?

  3. Jak mohu odstranit pomocí INNER JOIN s SQL Server?

  4. Oracle DROP TABLE, POKUD EXISTUJE Alternativy