sql >> Database teknologi >  >> RDS >> Sqlserver

Hvornår er det bedre at skrive ad hoc sql vs lagrede procedurer

SQL Server cacherer eksekveringsplanerne for ad-hoc-forespørgsler, så (med rabat på den tid, det første kald tager) vil de to tilgange være identiske med hensyn til hastighed.

Generelt betyder brugen af ​​lagrede procedurer, at du tager en del af den kode, som din applikation kræver (T-SQL-forespørgslerne) og placerer den et sted, der ikke er under kildekontrol (det kan være, men normalt er det ikke ) og hvor den kan ændres af andre uden din viden.

At have forespørgslerne på et centralt sted som dette kan være en god ting, afhængigt af hvor mange forskellige applikationer der skal have adgang til de data, de repræsenterer. Jeg synes generelt, det er meget nemmere at beholde de forespørgsler, der bruges af en applikation, der er bosiddende i selve applikationskoden.

I midten af ​​1990'erne sagde den konventionelle visdom, at lagrede procedurer i SQL Server var vejen at gå i præstationskritiske situationer, og på det tidspunkt var de bestemt det. Årsagerne bag denne CW har dog ikke været gyldige i lang tid.

Opdatering: Også ofte i debatter om levedygtigheden af ​​lagrede procedurer, påberåbes behovet for at forhindre SQL-injektion til forsvar for procs. Sikkert, ingen ved deres rette sind tror, ​​at samling ad hoc-forespørgsler gennem strengsammenkædning er den rigtige ting at gøre (selvom dette kun vil udsætte dig for et SQL-injektionsangreb, hvis du sammenkæder brugerinput ). Det er klart, at ad hoc-forespørgsler skal parametreres, ikke kun for at forhindre monster-under-sengen af ​​et sql-injektionsangreb, men også bare for at gøre dit liv som programmør generelt nemmere (medmindre du nyder at skulle finde ud af, hvornår du skal bruge single citater omkring dine værdier).

Opdatering 2: Jeg har lavet mere research. Baseret på denne MSDN-hvidbog , ser det ud til, at svaret afhænger af, hvad du præcist mener med "ad-hoc" med dine forespørgsler. For eksempel en simpel forespørgsel som denne:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

... vil få sin udførelsesplan cachelagret. Desuden, fordi forespørgslen ikke indeholder visse diskvalificerende elementer (som næsten alt andet end en simpel SELECT fra én tabel), vil SQL Server faktisk "auto-parameterisere" forespørgslen og erstatte den bogstavelige konstant "5" med en parameter og cache udførelsesplanen for den parametrerede version. Det betyder, at hvis du derefter udfører dette ad hoc-forespørgsel:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23

... den vil være i stand til at bruge den cachelagrede udførelsesplan.

Desværre er listen over diskvalificerende forespørgselselementer til auto-parameterisering lang (glem f.eks. at bruge DISTINCT , TOP , UNION , GROUP BY , OR osv.), så du kan virkelig ikke regne med dette for ydeevne.

Hvis du har en "superkompleks" forespørgsel, der ikke vil blive automatisk parametreret, f.eks.:

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

... den vil stadig blive cachelagret af den nøjagtige tekst i forespørgslen, så hvis din applikation kalder denne forespørgsel med de samme bogstavelige "hard-coded" værdier gentagne gange, vil hver forespørgsel efter den første genbruge den cachelagrede eksekveringsplan (og dermed være lige så hurtig som en lagret proc).

Hvis de bogstavelige værdier ændres (baseret på brugerhandlinger, f.eks. filtrering eller sortering af viste data), vil forespørgslerne ikke drage fordel af cachelagring (undtagen lejlighedsvis, når de ved et uheld matcher en nylig forespørgsel nøjagtigt).

Måden at drage fordel af caching med "ad-hoc" forespørgsler er at parameterisere dem. Oprettelse af en forespørgsel i farten i C# som denne:

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

er forkert. Den korrekte måde (ved at bruge ADO.Net) ville være noget som denne:

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();
}

Forespørgslen indeholder ingen bogstaver og er allerede fuldt parametriseret, så efterfølgende forespørgsler, der bruger den identiske parametrerede sætning, vil bruge den cachelagrede plan (selvom hvis den kaldes med forskellige parameterværdier). Bemærk, at koden her stort set er den samme som den kode, du ville bruge til at kalde en lagret procedure alligevel (den eneste forskel er CommandType og CommandText), så det kommer lidt ned på, hvor du ønsker, at teksten i den forespørgsel skal "live " (i din ansøgningskode eller i en lagret procedure).

Endelig, hvis du med "ad-hoc"-forespørgsler mener, at du dynamisk konstruerer forespørgsler med forskellige kolonner, tabeller, filtreringsparametre og andet, som måske disse:

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

... så kan du stort set ikke gør dette med lagrede procedurer (uden EXEC). hack, som ikke er til at tale om i et høfligt samfund), så pointen er omstridt.

Opdatering 3: Her er den eneste rigtig gode ydelsesrelaterede grund (som jeg i hvert fald kan komme i tanke om) til at bruge en lagret procedure. Hvis din forespørgsel er en langvarig en, hvor processen med at kompilere eksekveringsplanen tager betydeligt længere tid end den faktiske udførelse, og forespørgslen kun kaldes sjældent (som f.eks. en månedlig rapport), så kan det at lægge den i en lagret procedure få SQL Server til at beholde den kompilerede plan i cachen længe nok til, at den stadig eksisterer næste måned. Det slår mig, om det er sandt eller ej.



  1. Java opretter forbindelse til flere databaser

  2. Php multi-dimensional array fra mysql resultat

  3. PostgreSQL Upsert differentiere indsatte og opdaterede rækker ved hjælp af systemkolonner XMIN, XMAX og andre

  4. MySQL tæller på hinanden følgende datoer for den aktuelle streak