-- For the demonstration
USE tempdb;
GO
-- Drop any objects left over from previous runs
IF OBJECT_ID(N'dbo.PetColours', N'V') IS NOT NULL
DROP VIEW dbo.PetColours;
IF OBJECT_ID(N'dbo.GetPetColourString', N'IF') IS NOT NULL
DROP FUNCTION dbo.GetPetColourString;
IF OBJECT_ID(N'dbo.PetColourMap', N'U') IS NOT NULL
DROP TABLE dbo.PetColourMap;
IF OBJECT_ID(N'dbo.Pet', N'U') IS NOT NULL
DROP TABLE dbo.Pet;
IF OBJECT_ID(N'dbo.Colour', N'U') IS NOT NULL
DROP TABLE dbo.Colour;
IF OBJECT_ID(N'dbo.Animal', N'U') IS NOT NULL
DROP TABLE dbo.Animal;
IF OBJECT_ID(N'dbo.Sound', N'U') IS NOT NULL
DROP TABLE dbo.Sound;
GO
-- Create tables
CREATE TABLE dbo.Sound
(
sound_id INTEGER NOT NULL PRIMARY KEY,
name NVARCHAR(50) NOT NULL,
);
CREATE TABLE dbo.Animal
(
animal_id INTEGER NOT NULL PRIMARY KEY,
name NVARCHAR(50) NOT NULL,
leg_count SMALLINT NOT NULL,
has_fur BIT NOT NULL,
sound_id INTEGER NOT NULL REFERENCES dbo.Sound,
);
CREATE TABLE dbo.Colour
(
colour_id INTEGER NOT NULL PRIMARY KEY,
name NVARCHAR(50) NOT NULL,
)
CREATE TABLE dbo.Pet
(
pet_id INTEGER IDENTITY PRIMARY KEY,
name NVARCHAR(50) NOT NULL,
animal_id INTEGER NOT NULL REFERENCES dbo.Animal,
);
CREATE TABLE dbo.PetColourMap
(
map_id INTEGER IDENTITY PRIMARY KEY,
pet_id INTEGER NOT NULL REFERENCES dbo.Pet,
colour_id INTEGER NOT NULL REFERENCES dbo.Colour,
)
GO
-- For foreign keys
CREATE NONCLUSTERED INDEX [IX dbo.Animal sound_id] ON dbo.Animal (sound_id);
CREATE NONCLUSTERED INDEX [IX dbo.Pet animal_id] ON dbo.Pet (animal_id);
CREATE UNIQUE NONCLUSTERED INDEX [UQ dbo.PetColourMap pet_id, colour_id] ON dbo.PetColourMap (pet_id, colour_id);
CREATE UNIQUE NONCLUSTERED INDEX [UQ dbo.PetColourMap colour_id, pet_id)] ON dbo.PetColourMap (colour_id, pet_id);
GO
-- Define sounds
INSERT dbo.Sound (sound_id, name) VALUES (0, N'silent');
INSERT dbo.Sound (sound_id, name) VALUES (1, N'ruff ruff');
INSERT dbo.Sound (sound_id, name) VALUES (2, N'hiss');
INSERT dbo.Sound (sound_id, name) VALUES (3, N'oink');
INSERT dbo.Sound (sound_id, name) VALUES (4, N'meow');
INSERT dbo.Sound (sound_id, name) VALUES (5, N'eek eek');
-- Define colours
INSERT dbo.Colour (colour_id, name) VALUES (0, N'black');
INSERT dbo.Colour (colour_id, name) VALUES (1, N'white');
INSERT dbo.Colour (colour_id, name) VALUES (2, N'brown');
INSERT dbo.Colour (colour_id, name) VALUES (3, N'green');
INSERT dbo.Colour (colour_id, name) VALUES (4, N'pink');
INSERT dbo.Colour (colour_id, name) VALUES (5, N'tabby');
INSERT dbo.Colour (colour_id, name) VALUES (6, N'dark brown');
INSERT dbo.Colour (colour_id, name) VALUES (7, N'lime green');
-- Define animals
INSERT dbo.Animal (animal_id, name, leg_count, has_fur, sound_id)
VALUES (1, N'dog', 4, 1, 1);
INSERT dbo.Animal (animal_id, name, leg_count, has_fur, sound_id)
VALUES (2, N'snake', 0, 0, 2);
INSERT dbo.Animal (animal_id, name, leg_count, has_fur, sound_id)
VALUES (3, N'pig', 4, 0, 3);
INSERT dbo.Animal (animal_id, name, leg_count, has_fur, sound_id)
VALUES (4, N'cat', 4, 1, 4);
INSERT dbo.Animal (animal_id, name, leg_count, has_fur, sound_id)
VALUES (5, N'monkey', 4, 1, 5);
INSERT dbo.Animal (animal_id, name, leg_count, has_fur, sound_id)
VALUES (6, N'iguana', 4, 0, 0);
-- Create pets
INSERT dbo.Pet (name, animal_id) VALUES (N'Spike', 1);
INSERT dbo.Pet (name, animal_id) VALUES (N'Rex', 1);
INSERT dbo.Pet (name, animal_id) VALUES (N'Slither', 2);
INSERT dbo.Pet (name, animal_id) VALUES (N'Wilbur', 3);
INSERT dbo.Pet (name, animal_id) VALUES (N'Fluffy', 4);
INSERT dbo.Pet (name, animal_id) VALUES (N'Hunter', 4);
INSERT dbo.Pet (name, animal_id) VALUES (N'Mr. Biggles', 5);
INSERT dbo.Pet (name, animal_id) VALUES (N'Godzilla', 6);
-- Add entries to the pet colour mapping table
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (1, 0);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (1, 2);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (2, 0);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (2, 1);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (3, 3);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (4, 4);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (5, 0);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (5, 1);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (6, 5);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (7, 6);
INSERT dbo.PetColourMap (pet_id, colour_id) VALUES (8, 7);
GO
-- An in-line table-valued function to combine pet colours
-- into a delimited string with guaranteed order of colours
CREATE FUNCTION dbo.GetPetColourString (@PetID INTEGER)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
SELECT pet_colours =
STUFF(
Colours.xml_string.value('./text()[1]', 'NVARCHAR(100)')
, 1, 1, N'')
FROM (
SELECT N'/' + C.name
FROM dbo.PetColourMap CM
JOIN dbo.Colour C
ON C.colour_id = CM.colour_id
WHERE CM.pet_id = @PetID
ORDER BY
C.colour_id ASC
FOR XML PATH(''), TYPE
) Colours (xml_string);
GO
CREATE VIEW dbo.PetColours
AS
SELECT P.pet_id,
P.name,
PCS.pet_colours
FROM dbo.Pet P
CROSS
APPLY dbo.GetPetColourString (P.pet_id) PCS;
GO
-- Pet names and sounds made
SELECT pet_name = P.name,
sound_name = S.name
FROM dbo.Pet P
JOIN dbo.Animal A
ON A.animal_id = P.animal_id
JOIN dbo.Sound S
ON S.sound_id = A.sound_id;
-- Pets and colours
SELECT PC.pet_id,
pet_name = PC.name,
PC.pet_colours
FROM dbo.PetColours PC
WHERE pet_colours = N'black/white';
GO
-- Tidy up
DROP VIEW dbo.PetColours;
DROP FUNCTION dbo.GetPetColourString;
DROP TABLE dbo.PetColourMap;
DROP TABLE dbo.Pet;
DROP TABLE dbo.Colour;
DROP TABLE dbo.Animal;
DROP TABLE dbo.Sound;
-- End script
Ok, so I was bored :laugh:
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi