207/2009

Radbryt på SQL Server

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

2 kommentarer

252/2009

SQL Replace för att skapa Slugs

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

1 kommentarer

311/2008

Filtrera DataSet

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

Skriv kommentar

1010/2008

Konfigurera inloggning till SQL Server 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

Skriv kommentar

910/2008

Reseed identity på SQL Server

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

Skriv kommentar

129/2008

Uppdatera databasfält till inledande versal följt av gemener

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

Skriv kommentar

59/2008

Ändra Collation på databas i SINGLE_USER-läge

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

Skriv kommentar

248/2008

Jämnföra databasscheman och hålla ordning på versioner

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

2 kommentarer

77/2008

SQL-fråga med CROSS APPLY

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

1 kommentarer

256/2008

For-loop till SQL Server

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

Skriv kommentar

1 2 Nästa>>

Sidor

Etiketter

Ads