vendredi 28 novembre 2014

SQL select all related records in a table based on 2 columns (non set based working example included)


I'm trying to find related records based on 2 columns, however there must be a more elegant set based solution, or if not a set based solution, various ways of making this query better


The real world application for this query is that sometimes customers create multiple accounts with the same email address and/or have the same account with multiple emails, so I am trying to collate orders that are essentially from the same person, using either the userId or their email address


The example below basically stores the found orders ids, userIds and emails in to temporary tables, then researches for every new userId or email found


A working example



declare @UserId varchar(20) = '1'
declare @Email varchar(50)= 'blah1'

-- set test src tables
DECLARE @src TABLE
(Id int,UserId nvarchar(20),Email nvarchar(50),PostCode int,Country nvarchar(50))

-- inset test data
INSERT @src SELECT *
FROM (VALUES
(1, '1', 'blah1', 111, 'au'),
(2, '1', 'blah2', 111, 'au'),
(3, '1', 'blah3', 111, 'au'),
(4, '2', 'blah4', 111, 'au'),
(5, '2', 'blah3', 111, 'nz'),
(6, '4', 'blah4', 111, 'nz'),
(7, '5', 'blah4', 111, 'nz'),
(8, '5', 'blah6', 111, 'nz'),
(9, '7', 'blah7', 111, 'nz'),
(10, '8', 'blah8', 111, 'nz'),
(11, '9', 'blah9', 111, 'nz'),
(12, '10', 'blah10', 111, 'nz'),
(13, '1', 'blah11', 111, 'nz')
) src (Id, UserId, Email, PostCode, Country)

-- show the records, for debug purposes
SELECT *FROM @src

-- declare table variables
DECLARE @Ids TABLE (Id int)
DECLARE @Emails TABLE (Email varchar(50),Searched bit)
DECLARE @UserIds TABLE (UserId varchar(20),Searched bit)
DECLARE @Results TABLE (Id int,
UserId varchar(20),
Email varchar(50),
PostCode int,
Country nvarchar(50)
)

-- insert Initial UserId
IF (@UserId IS NOT NULL)
INSERT INTO @UserIds (UserId, Searched) VALUES (@UserId, 0);

-- insert Initial Email
IF (@Email IS NOT NULL)
INSERT INTO Emails(Email, Searched) VALUES (t.Email, 0);

-- while both variables have something in them, there are potentially more matches
WHILE (@UserId IS NOT NULL OR @Email IS NOT NULL)
BEGIN

-- clear results
DELETE FROM @Results

-- Main search query
INSERT INTO @Results SELECT Id,UserId,Email,PostCode,Country
FROM @src
WHERE (@UserId IS NOT NULL AND userId = @UserId)
OR (@Email IS NOT NULL AND Email = @Email)

-- if results are found merge
IF (@@ROWCOUNT > 0)
BEGIN

-- merge new ids
MERGE INTO @Ids i
USING (SELECT Id FROM @Results) t ON t.Id = i.Id
WHEN NOT MATCHED THEN
INSERT (Id) VALUES (t.Id);

-- merge new userIds
MERGE INTO @UserIds u
USING (SELECT UserId FROM @Results) t ON t.UserId = u.UserId
WHEN NOT MATCHED THEN
INSERT (UserId, Searched) VALUES (t.UserId, 0);

-- merge new emails
MERGE INTO @Emails u
USING (SELECT Email FROM @Results) t ON t.Email = u.Email
WHEN NOT MATCHED THEN
INSERT (Email, Searched) VALUES (t.Email, 0);

END

-- mark variables as searched in thier respective tables
UPDATE @UserIds SET Searched = 1 WHERE UserId = @UserId
UPDATE @Emails SET Searched = 1 WHERE Email = @Email

-- clear variables
SET @UserId = NULL;
SET @Email = NULL;

-- reset variables to the next unsearched value
SELECT TOP 1 @UserId = UserId
FROM @UserIds
WHERE Searched = 0;

SELECT TOP 1 @Email = Email
FROM @Emails
WHERE Searched = 0;

END

-- display related records
SELECT * FROM @Ids




Aucun commentaire:

Enregistrer un commentaire