A Developer's Guide to Data Modeling for SQL Server:
Covering SQL Server 2005 and 2008
Eric Johnson & Josh Jones
ISBN: 0321497643
“A Developer’s Guide to Data Modeling for SQL Server explains the concepts and practice of data modeling with a clarity that makes the technology accessible to anyone building databases and data-driven applications. If you're new to data modeling, or find the need to brush up on its concepts, this book is for you."
Peter Varhol, Executive Editor, Redmond Magazine
Model SQL Server Databases That Work Better, Do More, Evolve More Smoothly
Effective data modeling is essential to ensuring that your databases will perform well, scale well, and evolve to meet changing requirements. However, if you’re modeling databases to run on Microsoft SQL Server 2008 or 2005, theoretical or platform-agnostic data modeling knowledge isn’t enough: models that don’t reflect SQL Server’s unique real-world strengths and weaknesses often lead to disastrous performance.
A Developer’s Guide to Data Modeling for SQL Server is a practical, SQL Server-specific guide to data modeling for every developer, architect, and administrator. This book offers you invaluable start-to-finish guidance for designing new databases, redesigning existing SQL Server data models, and migrating databases from other platforms.
You’ll begin with a concise, practical overview of the core data modeling techniques that are in demand in today’s businesses. Next, you’ll walk through requirements gathering, and discover how to convert requirements into effective SQL Server logical models. Finally, you’ll systematically transform those logical models into physical models that make the most of SQL Server’s extended functionality. All of this book’s many examples are available for download on a companion Web site.
This book enables you to
Master all the elements of a successful logical data model
Understand your data model’s physical elements, from storage to referential integrity
Provide programmability via stored procedures, user-defined functions, triggers, and .NET CLR integration
Normalize data models, one step at a time
Gather and interpret requirements more effectively
Learn an effective, start-to-finish methodology for creating logical models
Overcome modeling problems related to entities, attribute, data types, storage overhead, performance, and relationships
Create physical models with SQL Server 2008 or 2005: from establishing naming guidelines through implementing business rules and constraints
Use SQL Server’s unique indexing capabilities, and overcome their limitations
Create abstraction layers that enhance security, extensibility, and flexibility
Sample Code
Chapter 3
*********************************************************
CREATE VIEW customer_address
AS
SELECT customer.first_name, customer.last_name,
customer.phone,
address.address_line1, address.city,
address.state, address.zip
FROM customer br />
ON address.customer_id = customer.customer_id
WHERE address.type = 'home' WHERE address.type = 'home'
*********************************************************
CREATE TABLE Products(
sku int NOT NULL PRIMARY KEY,
modelnumber varchar(25) NOT NULL,
name varchar(100) NOT NULL,
manufacturer varchar(25) NOT NULL,
description varchar(255) NOT NULL,
warranty_details varchar(500) NOT NULL,
price money NOT NULL,
weight decimal(5, 2) NOT NULL,
shippingweight decimal(5, 2) NOT NULL,
height decimal(4, 2) NOT NULL,
width decimal(4, 2) NOT NULL,
depth decimal(4, 2) NOT NULL,
isserialized bit NOT NULL,
status tinyint NOT NULL
)
*********************************************************
CREATE TABLE Products(
sku int NOT NULL,
modelnumber varchar(25) NOT NULL,
name varchar(100) NOT NULL,
manufacturer varchar(25) NOT NULL,
description varchar(255) NOT NULL,
price money NOT NULL,
weight decimal(5, 2) NOT NULL,
shippingweight decimal(5, 2) NOT NULL,
height decimal(4, 2) NOT NULL,
width decimal(4, 2) NOT NULL,
depth decimal(4, 2) NOT NULL,
isserialized bit NOT NULL,
status tinyint NOT NULL,
CONSTRAINT pk_product_sku PRIMARY KEY (sku)
)
**********************************************************
ALTER TABLE Products
ADD CONSTRAINT pk_product_sku PRIMARY KEY (sku)
*********************************************************
CREATE TABLE dbo.vehicle(
objid int IDENTITY(1,1) NOT NULL,
make varchar(50) NOT NULL,
model varchar(50)NOT NULL,
year char(4) NOT NULL,
employee_objid int NOT NULL,
CONSTRAINT PK_vehicle PRIMARY KEY (objid),
CONSTRAINT FK_vehicle_employee
FOREIGN KEY(employee_objid)
REFERENCES employee (objid)
)
*********************************************************
ALTER TABLE dbo.Products
ADD CONSTRAINT chk_non_negative_values
CHECK
(
weight >= 0
AND (shippingweight >= 0
AND shippingweight >= weight)
AND height >= 0
AND width >= 0
AND depth >= 0
)
*********************************************************
DECLARE @num_in_stock int
EXEC sp_check_product_stock @sku = 4587353,
@stock_level = @num_in_stock OUTPUT
PRINT @num_in_stock
*********************************************************
DECLARE @num_in_stock int
SET @num_in_stock = dbo.CheckProductStock (4587353)
PRINT @num_in_stock
********************************************************* INSERT INTO employee (employeeid, firstname, lastname) VALUES (dbo.GetNewEmployeeID(), 'Eric', 'Johnson')
Chapter 8
**********************************************************
CREATE TABLE SortTest(
StringDate varchar(50),
RealDate datetime
)
*********************************************************
INSERT INTO SortTest VALUES('1/1/2007' , '1/1/2007')
INSERT INTO SortTest VALUES('1/2/2007' , '1/2/2007')
INSERT INTO SortTest VALUES('1/3/2007' , '1/3/2007')
INSERT INTO SortTest VALUES('1/4/2007' , '1/4/2007')
…
INSERT INTO SortTest VALUES('1/31/2007' , '1/31/2007')
Chapter 9
*********************************************************
SELECT where, and, name, date
FROM INSERT
WHERE and = 1
AND where = 'Omaha'
*********************************************************
SELECT [where], [and], name, date
FROM [INSERT]
WHERE [and] = 1
AND [where] = 'Omaha'
*********************************************************
ALTER TABLE dbo.tbl_employee
ADD CONSTRAINT DF_status
DEFAULT 1 FOR status
*********************************************************
ALTER TABLE dbo.tbl_order
ADD CONSTRAINT [DF_ordernumber]
DEFAULT
dbo.udf_new_orderid() FOR ordernumber
*********************************************************
(
[homephone] IS NOT NULL
OR [workphone] IS NOT NULL
OR [mobilephone] IS NOT NULL
)
*********************************************************
ALTER TABLE dbo.tbl_customer
WITH CHECK
ADD CONSTRAINT CK_phone_number
CHECK
(([homephone] IS NOT NULL
OR [workphone] IS NOT NULL
OR [mobilephone] IS NOT NULL))
*********************************************************
ALTER TABLE dbo.tbl_order
ADD CONSTRAINT UNQ_ordernumber
UNIQUE NONCLUSTERED
(ordernumber)
*********************************************************
CREATE TRIGGER trg_payment_data
ON dbo.tbl_payment
FOR INSERT, UPDATE
AS
DECLARE @payment_type tinyint
,@cc_type varchar(16)
,@cc_number bigint
,@cc_expire date
,@ccv_code smallint
,@gc_number bigint
,@account_number bigint
,@routing_number int
,@license_number varchar(20)
,@license_state char(10)
SELECT @payment_type = payment_type
,@cc_type = cc_type
,@cc_number = cc_number
,@cc_expire = cc_expire
,@ccv_code = ccv_code
,@gc_number = gc_number
,@account_number = account_number
,@routing_number = routing_number
,@license_number = license_number
,@license_state = license_state
FROM inserted
IF @payment_type= 1 --Credit Card
BEGIN& RAISERROR ('Payment Error: Credit Card Data is Missing.',11,1)
ROLLBACK
END
END
IF @payment_type = 2 --Gift Card
BEGIN
IF @gc_number IS NULL
BEGIN
RAISERROR ('Payment Error: Gift Card Data is Missing.',11,1)
ROLLBACK
END
END
IF @payment_type = 3 --Bank Draft
BEGIN
IF @account_number IS NULL
OR @routing_number IS NULL
OR @license_number IS NULL
OR @license_state IS NULL
BEGIN
RAISERROR ('Payment Error: Bank Account Data is Missing.',11,1)
ROLLBACK
END
END
*********************************************************
ALTER TRIGGER trg_one_dean_per_college
ON tbl_college
FOR INSERT, UPDATE
AS
DECLARE @college_count int
SELECT @college_count = COUNT(tbl_college.id)
FROM tbl_college
JOIN tbl_faculty
ON tbl_college.dean_id = tbl_faculty.id
WHERE tbl_faculty.id = (SELECT dean_id FROM INSERTED)
IF @college_count > 1
BEGIN
RAISERROR('This faculty member is already dean of another college',11,1)
ROLLBACK
END
Chapter 10
*********************************************************
CREATE NONCLUSTERED INDEX idx_Customer_LastName_FirstName
ON Customer (LastName ASC, FirstName ASC)
WITH (FILlFACTOR = 70,
SORT_IN_TEMPDB = ON,
ONLINE = ON)
ON IndexFileGroup
Chapter 11
*********************************************************
CREATE VIEW vw_customer_detail
AS
SELECT email
,customer_id
,firstname
,lastname
,homephone ,workphone
,mobilephone
FROM tbl_customer
*********************************************************
CREATE VIEW vw_customer_addresses
AS
SELECT
address_objid = objid
,address_label
,addressline1
,addressline2
,city
,region
,zipcode
,customer_objid
FROM tbl_address
WHERE customer_objid IS NOT NULL
*********************************************************
ALTER PROCEDURE prc_save_customer
@email varchar(50)
,@customer_id char(10)
,@firstname varchar(50)
,@lastname varchar(50)
,@homephone varchar(15)
,@workphone varchar(15)
,@mobilephone varchar(15)
,@addresses CustomerAddress ReadOnly
,@customer_objid int OUTPUT
AS
MERGE tbl_customer AS pri_customer
USING
(
SELECT customer_id = @customer_id
) AS source_customer(customer_id)
ON
(
pri_customer.customer_id =
source_customer.customer_id
)
WHEN NOT MATCHED THEN
INSERT (email, customer_id, firstname, lastname,
homephone, workphone, mobilephone)
VALUES (@email,@customer_id,@firstname,@lastname,
@homephone,@workphone,@mobilephone)
WHEN MATCHED THEN
UPDATE
SET email=@email,
firstname=@firstname,
lastname=@lastname,
homephone=@homephone,
workphone=@workphone,
mobilephone=@mobilephone;
SELECT @customer_objid = objid
FROM tbl_customer
WHERE cu MERGE tbl_address AS current_addresses
USING
SELECT customer_objid = @customer_objid,
address_label, addressline1, addressline2,
city, region, country, zipcode, is_deleted
FROM @addresses
)
AS
source_addresss(customer_objid,
address_label, addressline1,addressline2,
city, region, country, zipcode, is_deleted)
ON
(current_addresses.address_label =
source_addresses.address_label
AND
current_addresses.customer_objid =
source_addresses.customer_objid
)
WHEN NOT MATCHED THEN
INSERT(address_label, addressline1, addressline2,
city, region, country, zipcode,
customer_objid)
VALUES(source_addresses.address_label,
source_addresses.addressline1,
source_addresses.addressline2,
source_addresses.city,
source_addresses.region,
source_addresses.country,
source_addresses.zipcode,
@customer_objid)
WHEN MATCHED AND source_addresses.is_deleted = 1
THEN DELETE
WHEN MATCHED THEN
UPDATE
SET address_label=source_addresses.address_label,
addressline1=source_addresses.addressline1,
addressline2=source_addresses.addressline2,
city=source_addresses.city,
region=source_addresses.region,
country=source_addresses.country,
zipcode=source_addresses.zipcode;
