sql >> Database >  >> RDS >> Sqlserver

Wanneer is het beter om ad-hoc sql versus opgeslagen procedures te schrijven?

SQL Server slaat de uitvoeringsplannen voor ad-hocquery's op in de cache, dus (afgezien van de tijd die nodig is voor de eerste aanroep) zullen de twee benaderingen qua snelheid identiek zijn.

Over het algemeen betekent het gebruik van opgeslagen procedures dat u een deel van de code die nodig is voor uw toepassing (de T-SQL-query's) neemt en deze op een plaats plaatst die niet onder bronbeheer staat (het kan zijn, maar meestal niet ) en waar het door anderen kan worden gewijzigd zonder uw medeweten.

De vragen op een centrale plaats zoals deze kan een goede zaak zijn, afhankelijk van hoeveel verschillende toepassingen toegang nodig hebben tot de gegevens die ze vertegenwoordigen. Ik vind het over het algemeen veel gemakkelijker om de zoekopdrachten die door een applicatie worden gebruikt, in de applicatiecode zelf te houden.

Halverwege de jaren negentig zei de conventionele wijsheid dat opgeslagen procedures in SQL Server de beste keuze waren in prestatiekritieke situaties, en op dat moment was dat zeker het geval. De redenen achter deze CW zijn echter al lang niet meer geldig.

Bijwerken: Ook wordt vaak in discussies over de levensvatbaarheid van opgeslagen procedures de noodzaak ingeroepen om SQL-injectie te voorkomen ter verdediging van procedures. Zeker, niemand bij zijn volle verstand denkt dat het samenstellen van ad-hocquery's door middel van stringconcatenatie de juiste is om te doen (hoewel dit je alleen blootstelt aan een SQL-injectie-aanval als je gebruikersinvoer aaneenschakelt ). Het is duidelijk dat ad-hocquery's moeten worden geparametriseerd, niet alleen om het monster onder het bed van een sql-injectie-aanval te voorkomen, maar ook om uw leven als programmeur over het algemeen gemakkelijker te maken (tenzij u het leuk vindt om erachter te komen wanneer u enkele aanhalingstekens rond uw waarden).

Update 2: Ik heb meer onderzoek gedaan. Gebaseerd op dit MSDN-witboek , lijkt het erop dat het antwoord afhangt van wat u precies bedoelt met "ad-hoc" met uw vragen. Bijvoorbeeld een eenvoudige zoekopdracht als deze:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

... zal het uitvoeringsplan in de cache hebben. Bovendien, omdat de query bepaalde diskwalificerende elementen niet bevat (zoals bijna alles anders dan een eenvoudige SELECT uit één tabel), zal SQL Server de query in feite "auto-parametriseren" en de letterlijke constante "5" vervangen door een parameter, en cache het uitvoeringsplan voor de geparametriseerde versie. Dit betekent dat als u vervolgens dit . uitvoert ad-hocvraag:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23

... het zal het in de cache opgeslagen uitvoeringsplan kunnen gebruiken.

Helaas is de lijst met diskwalificerende query-elementen voor automatische parametrering lang (vergeet bijvoorbeeld het gebruik van DISTINCT , TOP , UNION , GROUP BY , OR etc.), dus je kunt hier echt niet op rekenen voor prestaties.

Als u een "supercomplexe" zoekopdracht heeft die niet automatisch wordt geparametriseerd, zoals:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23

... het wordt nog steeds in de cache opgeslagen door de exacte tekst van de query, dus als uw toepassing deze query herhaaldelijk met dezelfde letterlijke "hard-coded" waarden aanroept, zal elke query na de eerste het in de cache opgeslagen uitvoeringsplan opnieuw gebruiken (en dus zo snel zijn als een opgeslagen proces).

Als de letterlijke waarden veranderen (op basis van gebruikersacties, zoals het filteren of sorteren van bekeken gegevens), dan hebben de zoekopdrachten geen baat bij caching (behalve af en toe wanneer ze per ongeluk exact overeenkomen met een recente zoekopdracht).

De manier om te profiteren van caching met "ad-hoc"-query's is om ze te parametriseren. On-the-fly een query maken in C# als volgt:

int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + 
        itemCount.ToString();

is onjuist. De juiste manier (met behulp van ADO.Net) zou ongeveer als volgt zijn:

using (SqlConnection conn = new SqlConnection(connStr))
{
    SqlCommand com = new SqlCommand(conn);
    com.CommandType = CommandType.Text;
    com.CommandText = 
        "DELETE FROM tblSTUFF WHERE ITEM_COUNT > @ITEM_COUNT";
    int itemCount = 5;
    com.Parameters.AddWithValue("@ITEM_COUNT", itemCount);
    com.Prepare();
    com.ExecuteNonQuery();
}

De query bevat geen letterlijke waarden en is al volledig geparametriseerd, dus volgende query's die de identieke geparametriseerde instructie gebruiken, zouden het cacheplan gebruiken (zelfs als ze worden aangeroepen met verschillende parameterwaarden). Merk op dat de code hier vrijwel hetzelfde is als de code die u toch zou gebruiken om een ​​opgeslagen procedure aan te roepen (het enige verschil is het CommandType en de CommandText), dus het komt er een beetje op neer waar u wilt dat de tekst van die query "live " (in uw applicatiecode of in een opgeslagen procedure).

Ten slotte, als u met "ad-hoc"-query's bedoelt dat u dynamisch query's maakt met verschillende kolommen, tabellen, filterparameters en dergelijke, zoals misschien deze:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`
    ORDER BY LASTNAME DESC

... dan kun je vrijwel niet doe dit met opgeslagen procedures (zonder de EXEC hack waar in de beleefde samenleving niet over gesproken mag worden), dus het punt is onbespreekbaar.

Update 3: Dit is de enige echt goede prestatiegerelateerde reden (die ik in ieder geval kan bedenken) voor het gebruik van een opgeslagen procedure. Als uw query een langlopende vraag is waarbij het opstellen van het uitvoeringsplan aanzienlijk langer duurt dan de daadwerkelijke uitvoering, en de query slechts zelden wordt aangeroepen (zoals bijvoorbeeld een maandelijks rapport), kan het plaatsen van deze in een opgeslagen procedure zorg ervoor dat SQL Server het gecompileerde plan lang genoeg in de cache bewaart zodat het er volgende maand nog is. Het verslaat me echter of dat waar is of niet.



  1. High Sierra + Python + Postgresql-fout:Illegale instructie:4

  2. Beperkingencontrole:TRY/CATCH vs Exists()

  3. Verbind Android-app-gegevens met Website-database

  4. De Heroku Lumen-app verbinden met Amazon RDS MySQL 5.7.19