207/2009
Jag behövde formattera några databastexter som (x)html och körde följande för att få paragrafer runt textstyckena. På Stack Overflow lärde jag mig att CHAR(13) är tecknet för radbryt.
UPDATE Post SET Body='<p>' + REPLACE(Body,CHAR(13),'</p><p>') + '</p>'
Vissa rekommenderar att formattera först när man visar upp texten för
användaren, men jag brukar vilja ha även htmltaggarna i databasen.
Av Jesper Lind
252/2009
Har på många tidigare projekt använt Id-nummer i url:erna för att IIS och .NET haft så dåligt stöd för att skapa användarvänliga url:er. Gör nu om en del gamla sidor till ASP.NET MVC och lägger över dem till IIS 7-servrar som har bättre stöd för url-omskrivning.
Brukar lägga till ett speciellt databas-fält för att spara den sista delen av url:en som ska vara till för att identifiera blogginlägget, produkten, kategorien eller vad det nu handlar om och basera denna på den äldre titeln. Använder det uttryck som även Wordpress använder för att beskriva ett sånt fält, "Slug".
Här är ett skript som jag precis körde på en sådan databas. Detta byter ut de tecken jag inte vill ha i url:erna mot ett vanligt bindestreck, gör om allt till gemener och byter ut å,ä,ö mot a,a,o.
UPDATE BlogPost SET Slug=Lower(Title);
UPDATE BlogPost SET Slug=REPLACE(Slug,'å','a');
UPDATE BlogPost SET Slug=REPLACE(Slug,'ä','a');
UPDATE BlogPost SET Slug=REPLACE(Slug,'ö','o');
UPDATE BlogPost SET Slug=REPLACE(Slug,' ','-');
UPDATE BlogPost SET Slug=REPLACE(Slug,'&','-');
UPDATE BlogPost SET Slug=REPLACE(Slug,'/','-');
UPDATE BlogPost SET Slug=REPLACE(Slug,'-','-');
UPDATE BlogPost SET Slug=REPLACE(Slug,'.','-');
UPDATE BlogPost SET Slug=REPLACE(Slug,',','-');
UPDATE BlogPost SET Slug=REPLACE(Slug,'_','-');
Av Jesper Lind
311/2008
Ja även fast det är spännande att jobba med nya databas-tekniker som LinqToSQL och Subsonic, så kan det vara bra att kunna lite hederliga DataSet-operationer.
Just nu så hade jag ett problem med Subsonics Paging-funktioner, där en Where-sats inte kom med. Jag fick dubbla poster hur jag än gjorde. Då kom jag på att jag kunde filtrera DataSet:et i efterhand och skrev följande lilla funktion.
private static DataSet FilterDataSet(DataSet ds,string filter)
{
DataSet clone = ds.Clone();
DataRow[] foundRows = ds.Tables[0].Select(filter);
for (int i = 0; i < foundRows.Length; i++)
{
DataRow row = foundRows[i];
clone.Tables[0].ImportRow(row);
}
return clone;
}
Anropar den sedan på följande vis. I detta fallet ville jag ha ut texter på en visst språk, och inte alla språk i databas-tabellen som Subsonic envisade sig med att returnera.
return FilterDataSet(q.ExecuteDataSet(),"Culture='sv-SE'");
Är du sugen på liknade exempel, så kan jag rekommendera tidigare inlägg där vi skrivit om hur man sorterar ett DataTable eller hur man skapar nya kolumner i DataSet och sparar det i Cacheminne.
Av Jesper Lind
1010/2008
Blev ställd inför uppgiften att konfigurera inloggningar till SQL Server 2008. Som tur va hittade jag en bra guide där författandet illustreras med screenshots. Hade inte klarat det utan den.
Av Jesper Lind
910/2008
Lärde mig just hur man gör så att nyckleln på en tabell i en SQL Server databas börjar på ett visst nummer. Bra om man gjort massa tester och vill återställa dem innan man börjar lägga in riktiga data.
-- Reseed identity on [dbo].[Products]
DBCC CHECKIDENT('[dbo].[Products]', RESEED, 0)
GO
Ännu bättre är ju ifall man inte lägger in data alls, utan istället använder transaktioner för att rulla tillbaks testdata. Läs mer hos Johan Lindfors, teknisk chef på Microsoft, om detta.
Av Jesper Lind
129/2008
Vi hade en databas med massa postorter i versaler (ALINGSÅS, GÖTEBORG, STOCKHOLM) osv men ville ha dem så här istället (Alingsås, Göteborg, Stockholm).
Hittade tips i ett forum hur man gör.
Först för en SELECT för att testa:
SELECT UPPER(left(CityName, 1)) + substring(LOWER(CityName), 2, LEN(CityName)) FROM [Cities]
Och sen en UPDATE som ändrar fältet:
UPDATE Cities SET CityName = UPPER(left(CityName, 1)) + SUBSTRING(LOWER(CityName), 2, len(CityName))
Av Jesper Lind
59/2008
Om man vill ändra Collation på en databas kan det vara svårt ifall man har andra användare uppkopplade. Detta blockerar kommandot och man får "The database could not be exclusively locked to perform the operation".
För att komma runt problemet kan man gå in i "SINGLE_USER"-läge (Glöm inte att gå tillbaks till "MULTI_USER" efter åt.
ALTER DATABASE <DBNAME> SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
ALTER DATABASE <DBNAME>
COLLATE SQL_SwedishStd_Pref_CP1_CI_AS;
GO
ALTER DATABASE <DBNAME> SET MULTI_USER
GO
Av Jesper Lind
248/2008
När man utvecklar en webbapplikation (eller andra typer av program för den delen också) så är det ofta en stor utmaning att hålla strukturen på databaserna likadan. Man gör ändringar i sin orginalmodell och försöker ändra alla de databaser som är i drift enligt bästa förmåga. Hittills har jag inte haft något speciellt bra sätt att göra detta på utan det slutar ofta med felsökning steg för steg och ändra databasen manuellt. Tänkte här skriva om några sätt som kan förenkla detta jobb.
Lägga in databasskripten i källkodsprojektet
Ett sätt är att skripta ut hela databasen och sedan inkludera skripten i källkodsprojektet som Coding Horror förklarar. Inte helt på det klara om detta kan hjälpa en för att uppdatera befintliga databaser, men att ha strukturen i kod är ju ett bra första steg.
Använda sig av databas-migrering
Detta är ett koncept som funnits länge i Ruby On Rails-världen och innebär att varje förändring i databasen sparar i uppdateringsskript som man kan köra på sina databaser.
Subsonic-teamet har nyligen inspirerats av Rails och lagt in liknande Migrations-funktionalitet i Subsonic. Har provat detta lite under sommaren och även fast det är väldigt ny teknik så verkar det fungera mycket bra.
Program för att Jämnföra databasscheman
Detta sätt tycker jag är det mest bekväma och innebär minst jobb. Man utgår helt enkels från sin orginalstruktur för databasen och jämför denna med de databaser som ska uppgraderas.
Denna funktionallitet finns i Visual Studio men bara i team-edition så den har jag inte testat själv. Verkar fungera fint och jag kan rekommendera läsning hos Emad Ibrahim som har provat på det.
Det smidigaste programmet som jag har provat är dock utan tvekan Redgate SQL Compare. Att jämföra två databaser går på nån minut och man får sedan SQL Skript redo att köra på den databas som ska uppgraderas. Har bara provat testversionen men funderar skarpt på att göra en investering i en licens.
Om du har några erfarenheter om hur man kan göra version-hantering av databaser lättare, så uppskattas kommentarer.
Av Jesper Lind
77/2008

APPLY (msdn) är ett nytt SQL-kommando i SQL Server från och med version 2005 och finns i två olika varianter, CROSS APPLY och OUTER APPLY.
Kan inte säga att jag förstått dessa funktionerna helt, men jag har hittat ett bra användningsområde för CROSS APPLY. Nämligen att kunna bygga upp VIEWs med data från en relationstabell och sammanfoga dessa i en ny kolumn separerade med valfritt tecken. Detta kallas tydligen "aggregate concatenation".
Läste även att denna metod går emot huvudideén för hur en relationsdatabas är tänkt att fungera, så har du ett behov att göra såna här SQL-frågor kan det vara snart att tänka om ifall det går att göra på ett bättre sätt. Eller kanske hämta ut datan separat och bygga ihop vyn i buisness-lagret.
Kommer här visa hur det kan fungera genom ett exempel, här först databasdiagrammet.
Sen ett skript (skapat med SubSonic) som bygger upp hela databasen:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Products]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Products](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [nvarchar](150) COLLATE Finnish_Swedish_CI_AS NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
/****** Object: Table [dbo].[Colors] Script Date: 07/07/2008 16:58:21 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Colors]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Colors](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ColorName] [nvarchar](150) COLLATE Finnish_Swedish_CI_AS NULL,
CONSTRAINT [PK_Colors] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
/****** Object: Table [dbo].[ProductColor] Script Date: 07/07/2008 16:58:21 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ProductColor]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[ProductColor](
[ProductId] [int] NOT NULL,
[ColorId] [int] NOT NULL,
CONSTRAINT [PK_ProductColor] PRIMARY KEY CLUSTERED
(
[ProductId] ASC,
[ColorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
/****** Object: View [dbo].[ListOfColorsView] Script Date: 07/07/2008 16:58:21 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[ListOfColorsView]'))
EXEC dbo.sp_executesql @statement = N'CREATE VIEW dbo.ListOfColorsView
AS
SELECT DISTINCT ProductName, ListOfColors = LEFT(o.list, LEN(o.list) - 1)
FROM Products CROSS APPLY
(SELECT CONVERT(VARCHAR(12), ColorName) + '','' AS [text()]
FROM dbo.Colors c INNER JOIN
dbo.ProductColor ON c.Id = dbo.ProductColor.ColorId
WHERE (dbo.ProductColor.ProductId = dbo.Products.Id)
ORDER BY c.ColorName FOR XML PATH('''')) o(list)
'
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_ProductColor_Colors]') AND parent_object_id = OBJECT_ID(N'[dbo].[ProductColor]'))
ALTER TABLE [dbo].[ProductColor] WITH CHECK ADD CONSTRAINT [FK_ProductColor_Colors] FOREIGN KEY([ColorId])
REFERENCES [Colors] ([Id])
ALTER TABLE [dbo].[ProductColor] CHECK CONSTRAINT [FK_ProductColor_Colors]
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_ProductColor_Products]') AND parent_object_id = OBJECT_ID(N'[dbo].[ProductColor]'))
ALTER TABLE [dbo].[ProductColor] WITH CHECK ADD CONSTRAINT [FK_ProductColor_Products] FOREIGN KEY([ProductId])
REFERENCES [Products] ([Id])
ALTER TABLE [dbo].[ProductColor] CHECK CONSTRAINT [FK_ProductColor_Products]
Och lite testdata:
ALTER TABLE [Colors] NOCHECK CONSTRAINT ALL
GO
ALTER TABLE [Colors] DISABLE TRIGGER ALL
GO
SET IDENTITY_INSERT [Colors] ON
PRINT 'Begin inserting data in Colors'
INSERT INTO [Colors] ([Id], [ColorName])
VALUES(1, 'Red')
INSERT INTO [Colors] ([Id], [ColorName])
VALUES(2, 'Blue')
INSERT INTO [Colors] ([Id], [ColorName])
VALUES(3, 'Green')
SET IDENTITY_INSERT [Colors] OFF
ALTER TABLE [Colors] CHECK CONSTRAINT ALL
GO
ALTER TABLE [Colors] ENABLE TRIGGER ALL
GO
ALTER TABLE [Products] NOCHECK CONSTRAINT ALL
GO
ALTER TABLE [Products] DISABLE TRIGGER ALL
GO
SET IDENTITY_INSERT [Products] ON
PRINT 'Begin inserting data in Products'
INSERT INTO [Products] ([Id], [ProductName])
VALUES(1, 'Test product 1')
INSERT INTO [Products] ([Id], [ProductName])
VALUES(2, 'Test product 2 - No Colors')
SET IDENTITY_INSERT [Products] OFF
ALTER TABLE [Products] CHECK CONSTRAINT ALL
GO
ALTER TABLE [Products] ENABLE TRIGGER ALL
GO
ALTER TABLE [ProductColor] NOCHECK CONSTRAINT ALL
GO
ALTER TABLE [ProductColor] DISABLE TRIGGER ALL
GO
PRINT 'Begin inserting data in ProductColor'
INSERT INTO [ProductColor] ([ProductId], [ColorId])
VALUES(1, 1)
INSERT INTO [ProductColor] ([ProductId], [ColorId])
VALUES(1, 2)
INSERT INTO [ProductColor] ([ProductId], [ColorId])
VALUES(1, 3)
ALTER TABLE [ProductColor] CHECK CONSTRAINT ALL
GO
ALTER TABLE [ProductColor] ENABLE TRIGGER ALL
GO
Det mest intressanta är ju själva vyn som separerar datan från relationstabellen med ett kommatecken mellan varje värde.
SELECT DISTINCT ProductName, ListOfColors = LEFT(o.list, LEN(o.list) - 1)
FROM Products CROSS APPLY
(SELECT CONVERT(VARCHAR(12), ColorName) + ',' AS [text()]
FROM dbo.Colors c INNER JOIN
dbo.ProductColor ON c.Id = dbo.ProductColor.ColorId
WHERE (dbo.ProductColor.ProductId = dbo.Products.Id)
ORDER BY c.ColorName FOR XML PATH('')) o(list)
Svaret från denna vyn ser ut så här:
Test product 1 Blue,Green,Red
Test product 2 - No Colors NULL
Mer läsning:
http://zulfiqar.typepad.com/zulfiqars_web/2005/04/tsql_concatenat.html
http://blog.sqlauthority.com/2008/01/04/sql-server-2005-cross-apply/
Av Jesper Lind
256/2008
Har funderat på ibland hur man kan göra foor-loop i SQL Server. Efter en sökning hittade jag svaret hos SQLAuthority som också visar upp exempel på break och continue.
I mitt fall ville jag köra en insert för varje loopning och mitt skript såg ut ungefär som följer. Variablen intFlag används också för att sätta in ett stigande heltal i tabellen.
DECLARE @intFlag INT
SET @intFlag = 1
WHILE (@intFlag <=100)
BEGIN
PRINT @intFlag
INSERT INTO TheTable (IntValue,StringValue,CounterValue) VALUES (1,'MyDefaultString,@intFlag))
SET @intFlag = @intFlag + 1
END
GO
Av Jesper Lind
1
2
Nästa>>