principlesqlMinor
Replace cursor with set-based approach
Viewed 0 times
withreplacebasedapproachsetcursor
Problem
I am looking to replace my cursor-based solution if possible in a particular stored procedure. If it makes any difference, this is running on SQL Server 2008 R2. I am looking more for an algorithm than precise code.
Background:
The SP is part of a system for a company that sends mailings via direct mail or email. The mailings contain a personalized code that the recipient can enter when visiting the merchant to get special discounts or offers. Code usage is tracked and aggregate reports on the response to various mailings is provided to the merchants. A "customer" is defined as the target of one of the mailings with a unique first name, last name, and address; if there is no address, then email replaces address.
The tables at issue are as follows (simplified versions):
The job table is populated by an external process which is involved in the creation of the address lists to which the mailing will be sent. It then calls the SP I wish to
Background:
The SP is part of a system for a company that sends mailings via direct mail or email. The mailings contain a personalized code that the recipient can enter when visiting the merchant to get special discounts or offers. Code usage is tracked and aggregate reports on the response to various mailings is provided to the merchants. A "customer" is defined as the target of one of the mailings with a unique first name, last name, and address; if there is no address, then email replaces address.
The tables at issue are as follows (simplified versions):
CREATE TABLE job (
id INT PRIMARY KEY IDENTITY (1,1),
job_num VARCHAR(32) NOT NULL,
mailing_id INT NOT NULL,
personal_code NVARCHAR(50) NOT NULL,
fname NVARCHAR(50) NOT NULL,
lname NVARCHAR(50) NOT NULL,
email NVARCHAR(50),
address NVARCHAR(50),
city NVARCHAR(50),
state CHAR(2),
zip NVARCHAR(10),
extra NVARCHAR(150)
);
CREATE TABLE customer (
id INT PRIMARY KEY IDENTITY (1,1),
fname NVARCHAR(50) NOT NULL,
lname NVARCHAR(50) NOT NULL,
email NVARCHAR(50),
address NVARCHAR(50),
city NVARCHAR(50),
state CHAR(2),
zip NVARCHAR(10)
);
CREATE TABLE personal_code (
id INT PRIMARY KEY IDENTITY (1,1),
customer_id INT NOT NULL,
mailing_id INT NOT NULL,
personal_code NVARCHAR(50) NOT NULL,
email NVARCHAR(50),
FOREIGN KEY (customer_id) REFERENCES customer(id)
);
CREATE TABLE personal_code_extra (
personal_code_id INT PRIMARY KEY,
extra NVARCHAR(150),
FOREIGN KEY (personal_code_id) REFERENCES personal_code(id)
);The job table is populated by an external process which is involved in the creation of the address lists to which the mailing will be sent. It then calls the SP I wish to
Solution
I would probably just do this the brute force way, and add indexes to support these joins where they don't exist. Not much gain to treating new customers and old customers any different once you've inserted all the customers that don't already exist:
INSERT dbo.Customer(fname, lname, address, city, state, zip, email)
SELECT fname, lname, address, city, state, zip, email
FROM dbo.job AS j
WHERE job_no = @job_no
AND NOT EXISTS
(
SELECT 1 FROM dbo.Customer
WHERE fname = j.fname
AND lname = j.lname
AND (address = j.address OR email = j.email)
);
INSERT INTO dbo.personal_code (customer_id, mailing_id, personal_code, email)
SELECT c.customer_id, j.mailing_id, j.personal_code, c.email
FROM dbo.Customer AS c
INNER JOIN dbo.job AS j
ON c.fname = j.fname AND c.lname = j.lname
AND (c.address = j.address OR c.email = j.email)
WHERE j.job_no = @job_no;
INSERT dbo.personal_code_extra(personal_code_id, extra)
SELECT pc.personal_code_id, j.extra
FROM dbo.personal_code AS pc
INNER JOIN dbo.Customer AS c
ON pc.customer_id = c.customer_id
INNER JOIN dbo.job AS j
ON c.fname = j.fname AND c.lname = j.lname
AND (c.address = j.address OR c.email = j.email)
WHERE j.job_no = @job_no;Code Snippets
INSERT dbo.Customer(fname, lname, address, city, state, zip, email)
SELECT fname, lname, address, city, state, zip, email
FROM dbo.job AS j
WHERE job_no = @job_no
AND NOT EXISTS
(
SELECT 1 FROM dbo.Customer
WHERE fname = j.fname
AND lname = j.lname
AND (address = j.address OR email = j.email)
);
INSERT INTO dbo.personal_code (customer_id, mailing_id, personal_code, email)
SELECT c.customer_id, j.mailing_id, j.personal_code, c.email
FROM dbo.Customer AS c
INNER JOIN dbo.job AS j
ON c.fname = j.fname AND c.lname = j.lname
AND (c.address = j.address OR c.email = j.email)
WHERE j.job_no = @job_no;
INSERT dbo.personal_code_extra(personal_code_id, extra)
SELECT pc.personal_code_id, j.extra
FROM dbo.personal_code AS pc
INNER JOIN dbo.Customer AS c
ON pc.customer_id = c.customer_id
INNER JOIN dbo.job AS j
ON c.fname = j.fname AND c.lname = j.lname
AND (c.address = j.address OR c.email = j.email)
WHERE j.job_no = @job_no;Context
StackExchange Database Administrators Q#18238, answer score: 4
Revisions (0)
No revisions yet.