SQL Query以递归方式查找基于2个字段的关系记录

时间:2014-10-30 02:18:25

标签: sql-server recursion common-table-expression

鉴于以下数据

OrderId UserId  Email   PostCode    Country
1       1       blah1   111         au
2       1       blah2   111         au
3       1       blah3   111         au
4       2       blah3   111         au
5       3       blah4   111         nz
6       3       blah4   111         nz
7       4       blah3   111         au
8       4       blah5   111         au

此输入UserID = 1Email = "blah1"

我需要编写一个sql查询,它为我提供了所有唯一的记录,这些记录递归地包含UserId或Email上的匹配

示例结果

OrderId UserId  Email   PostCode    Country
1       1       blah1   111         au
2       1       blah2   111         au
3       1       blah3   111         au
4       2       blah3   111         au
7       4       blah3   111         au
8       4       blah5   111         au

E.g

第一遍将根据UserID = 1 Email = "blah1"生成以下内容

OrderId UserId  Email   PostCode    Country
1       1       blah1   111         au
2       1       blah2   111         au
3       1       blah3   111         au

后续传递UserID = 1 Email = "blah3"会产生

OrderId UserId  Email   PostCode    Country
3       1       blah3   111         au
4       2       blah3   111         au
7       4       blah3   111         au

后续传递UserID = 4 Email = "blah3"会产生

OrderId UserId  Email   PostCode    Country
7       4       blah3   111         au
8       4       blah5   111         au

以及所有相关记录,以便生成上面显示的示例结果

这可能与CTE有关吗?

2 个答案:

答案 0 :(得分:0)

您不需要CTE,请查看子查询:

declare @src table (
    OrderId int
    , UserId int
    , Email nvarchar(50)
    , PostCode int
    , Country nvarchar(50)
    )

insert @src 
select * from (values 
(1,1,'blah1',111,'au'),
(2,1,'blah2',111,'au'),
(3,1,'blah3',111,'au'),
(4,2,'blah3',111,'au'),
(5,3,'blah3',111,'nz'),
(6,3,'blah3',111,'nz')) src(OrderId, UserId, Email, PostCode, Country)



select distinct *
from @src
where Email in (
        select Email
        from @src
        where UserId = 1
        ) or
        UserId in (
        select UserId
        from @src
        where Email = 'blah1'
        ) 

更新问题已更改,以下是新答案:

use guidion_test
go
if OBJECT_ID('demo_src') is not null drop table demo_src
create table demo_src (
    OrderId int
    , UserId int
    , Email nvarchar(50)
    , PostCode int
    , Country nvarchar(50)
    )

insert demo_src 
select * from (values 
(1,1,'blah1',111,'au'),
(2,1,'blah2',111,'au'),
(3,1,'blah3',111,'au'),
(4,2,'blah3',111,'au'),
(5,3,'blah3',111,'nz'),
(6,3,'blah3',111,'nz')) src(OrderId, UserId, Email, PostCode, Country);


declare @search_user int = 1;


with cte
as (
    select OrderId
        , UserId
        , Email
        , PostCode
        , Country
    from demo_src
    where UserId = @search_user

    union all

    select s.OrderId
        , s.UserId
        , s.Email
        , s.PostCode
        , s.Country
    from demo_src s
    join cte c
        on s.UserId = c.UserId
            or s.OrderId = c.OrderId
            or s.Email = c.Email
            or s.PostCode = c.PostCode
            or s.Country = c.Country
    )
select top 10  *
from cte
option (maxrecursion 100)

答案 1 :(得分:0)

经过大量的反复试验后,我认为我找到了最合适的解决方案

如果有人能想出更好的方法来达到相同的效果或优化我所做的事情,那就标记你的答案是正确的

USE [test]
GO
/****** Object:  StoredProcedure [dbo].[uspGetSimilar]    Script Date: 10/11/2014 12:49:03 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[uspGetSimilar] 
    @UserId varchar(20), 
    @Email varchar(50)
AS
BEGIN
    SET NOCOUNT ON;

    -- 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)

    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))

    -- Merge Initial UserId into UserIds table
    if(@UserId is not null)
    begin
       merge into @UserIds u
       using ( 
         select @UserId as UserId
       ) t on t.UserId = u.UserId
       when not matched then 
        insert (UserId,Searched) values (t.UserId,0);
    end

    -- Merge Initial Email into Emails table
    if(@Email is not null)
    begin
       merge into @Emails u
       using ( 
          select @Email as Email
       ) t on t.Email = u.Email
       when not matched then 
          insert (Email,Searched) values (t.Email,0);
    end

   -- 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 unserached value
      select top 1 @UserId = UserId from @UserIds where Searched = 0;
      select top 1 @Email = Email from @Emails where Searched = 0;

   end

   select * from @Ids

END