Nyheter i SQL-Server 2008

Det finns några intressanta nyheter för utvecklare i sql-server 2008. Jag vill här visa några exempel.

Tilldela värde till variabler redan vid deklarationen

Innan sql-server 2008 var man tvungen att först deklarera en variabel via declare @var int för att sedan tilldela variabeln via set @var = 42. Det har nu blivit möjligt att tilldela ett värde direkt vid deklarationen. Exempelvis.

declare @var int = 42

Mata in flera rader samtidigt i en insert

När man skapar poster i en tabell med insert så har det tidigare krävts ganska mycket kod, varav det mesta varit repetitivt. Man skrev ofta på följande sätt.

insert into mytable values (1, 'first')
insert into mytable values (2, 'second')
insert into mytable values (3, 'third')

Det har nu blivit minte repetitivt genom att man bara behöver skriva insert into mytable values en gång. Nedanstående kod resulterar alltså i tre nya poster i tabellen.

insert into mytable
  values (1, 'first'), (2, 'second'), (3, 'third')

Smidigare tilldelning

Det har länge varit möjligt att öka eller minska en variabels värde genom += eller -= i c#. Nu kommer samma möjlighet i sql. Istället för att skriva.

update mytable set price = price + 10 where product = 'first'

så kan vi nu skriva:

update mytable set price += 10 where product = 'first'

De nya operatorerna är: +=, -=, *=, /= och %=.

Nyheterna som jag beskrivit hittills handlar mycket om att få ner mängden kod som behöver skrivas för att utföra något. Det är inte bara bra för utvecklarna, det är också bra för prestandan eftersom det blir mindre nätverkstrafik.

Bättre datumhantering

Det finns nu fler datatyper för att hantera datum.
Exempelvis finns datetime2 som har en noggrannhet på 100 nanosekunder, exempelvis 2009-08-17 11:14:45.3875725. Detta är att jämföra med datetime vars noggrannhet endast är 3.33 millisekunder, exempelvis 2009-08-17 11:14:45.387.

Förutom ovanstående så finns det två nya datatyper i form av date och time.
Datetime lagras i form av 8 bytes.
Datetime2 lagras med 7 bytes som standard, men kan ta upp till 8 bytes om fler decimaler används.
Date lagras med 3 bytes.
Time lagras med 5 bytes.

Ofta när man jobbar med datum har man ingen användning av tiden, därför är det bra att nu kunna skapa kolumner som bara är datum. Dessutom har det ibland varit problematiskt att hämta ut poster baserat på datum. Detta eftersom sql-server avrundar. Som exempel kan vi ta nedanstående.

CREATE TABLE [DateTest](
	[PostDate] [datetime]
)

INSERT INTO DateTest VALUES
	('2009-08-08 23:59:59'), 
	('2009-08-09 00:00:00'), 
	('2009-08-10 00:00:00'), 
	('2009-08-10 00:00:01')
	
SELECT * FROM DateTest WHERE PostDate BETWEEN '2009-08-09' AND '2009-08-10'

Ovanstående SELECT resulterar i två poster. Alltså 2009-08-09 00:00:00 och 2009-08-10 00:00:00. Här förväntar vi oss förmodligen att få tre poster där PostDate är antingen 2009-08-09 eller 2009-08-10, men istället får vi alla datum som är mellan eller lika med 2009-08-09 00:00:00 och 2009-08-10 00:00:00 och missar därmed 2009-08-10 00:00:01. Ovanstående har många gånger orsakat buggar i diverse applikationer. Med datatypen date blir det genast enklare.

CREATE TABLE [DateTest](
	[PostDate] [date]
)

INSERT INTO DateTest VALUES
	('2009-08-08 23:59:59'), 
	('2009-08-09 00:00:00'), 
	('2009-08-10 00:00:00'), 
	('2009-08-10 00:00:01')

	
SELECT * FROM DateTest WHERE PostDate BETWEEN '2009-08-09' AND '2009-08-10'

Ovanstående kod returnerar alla tre poster. Dessutom sparar vi 5 bytes.

Merge

Merge används för att jämföra två datamängder och sedan köra insert/update/delete baserat på vad man har definierat. Det kan exempelvis användas istället för att göra en så kallad UPSERT som kortfattat innebär att man först kontrollerar om posten finns och gör en update eller insert baserat på det. Exempel på en upsert kan vara.

IF EXISTS (SELECT * FROM UPSERTEXAMPLE WHERE name = 'john doe')
BEGIN
	UPDATE UPSERTEXAMPLE SET name = 'john doe 2' WHERE name = 'john doe'
END
ELSE
BEGIN
	INSERT INTO UPSERTEXAMPLE VALUES ('john doe 2')
END

Med merge kan man istället skriva.

MERGE UPSERTEXAMPLE as t --target
USING (SELECT 'john doe' as name) as s --source
ON t.name = s.name
WHEN MATCHED THEN
   UPDATE SET t.name = 'john doe 2'
WHEN NOT MATCHED BY TARGET THEN
   INSERT VALUES(s.name);

Notera att semikolon i slutet är obligatoriskt för merge.
Merge fungerar som så att man först anger ett mål (target) och sedan en källa (source) samt deras relation i form av en join. I vårt fall joinar vi t.name och s.name. Sedan lägger man in ett antal villkor. Om båda posterna finns i både källan och målet så genomförs en update. Om posten inte finns i målet så lägger man till posten med en insert. Vid de prestandamätningar som jag har gjort på merge så är merge långsammare än en upsert. Men om det är flera poster som skall jämföras så blir merge givetvis användbar eftersom den är set-baserad.

Ett annat tillfälle då merge kan vara bra är om man vill synkronisera två tabeller. I mitt exempel har jag två likadana tabeller. Den ena är för produktion, och den andra för utveckling. Det rör sig om en tabell med texter. Om man vill synkronisera dessa tabeller så informationen är identisk kan man göra på följande sätt.

Vi börjar med att skapa tabellerna och mata in exakt samma innehåll.

CREATE TABLE productioncontent (contentkey int primary key, content varchar(max))
CREATE TABLE developmentcontent (contentkey int primary key, content varchar(max))

INSERT INTO productioncontent values (1, 'text'), (2, 'text'), (3, 'text')
INSERT INTO developmentcontent values (1, 'text'), (2, 'text'), (3, 'text')

Sedan gör vi diverse ändringar i produktionstabellen.

INSERT INTO productioncontent values (4, 'text')
UPDATE productioncontent SET content = 'new text' where contentkey = 3
DELETE FROM productioncontent WHERE contentkey = 2

Nu vill vi med merge se till att alla ändringar vi gjorde nyss på produktionstabellen även kommer till utvecklingstabellen.

MERGE developmentcontent as d --target
USING productioncontent as p --source
ON d.contentkey = p.contentkey
WHEN MATCHED AND d.content <> p.content THEN
   UPDATE SET d.content = p.content
WHEN NOT MATCHED BY SOURCE THEN
   DELETE
WHEN NOT MATCHED BY TARGET THEN
   INSERT VALUES(p.contentkey, p.content);

Det som skiljer ovanstående från föregående exempel är att vi endast gör en update när posterna matchar (eftersom vi kör join på contentkey) och content är förändrat. Utöver det gör vi en delete och raderar därmed posten när raden inte matchar i källan (source). Vi utför en insert när posten inte finns i målet (target). Läs gärna mer om merge på microsofts hemsida.

Table valued parameters

Table valued parameters gör det möjligt att skicka in en hel kollektion från .net direkt till sql-server. Tidigare har man behövt köra en insert/update/delete i taget eller skicka in exempelvis en kommaseparerad sträng att jobba vidare med. Förutom enklare hantering i både .net-koden och på databassidan så blir det bättre prestanda eftersom färre databasanrop krävs. Jag har skrivit ett längre blogginlägg om table valued parameters, läs det gärna för att lära dig mer.

1 kommentar

  1. […] 14 augusti 2009 vid 14:05 · Arkiverad under SQL-Server Jag vill tipsa om en ny funktion i sql-server 2008 som gör det enkelt att skicka in flera värden till en lagrad procedur. Detta är användbart exempelvis när man behöver skapa flera nya poster samtidigt, eller kanske radera. Innan sql-server 2008 och möjligheten med table valued parameters så fick man skicka in en kommaseparerad sträng som man delar upp och kör en insert/update/delete för varje del. Med table valued parameters så kan man skicka in en lista, exempelvis datatable, eller en List som innehåller ditt data. Detta sparar givetvis en hel del jobb eftersom man då kan skicka sin kollektion direkt utan att behöva göra om den till en kommaseparerad sträng. Dessutom får man på databassidan direkt ett set att jobba med som man exempelvis kan använda för att göra MERGE eller JOIN. Jag skriver om merge i mitt inlägg om nyheter i sql-server 2008. […]

RSS feed for comments on this post

Kommentarer inaktiverade.

%d bloggare gillar detta: