2017-03-15 55 views
3

这是前一个问题的延续:Find groups with matching rows如何执行语句的每一行,并返回整个结果

我有一个包含人,他们拥有

+-------+-------+ 
| Name | Model | 
+-------+-------+ 
| Bob | Camry | 
| Bob | Civic | 
| Bob | Prius | 
| John | Camry | 
| John | Civic | 
| John | Prius | 
| Kevin | Civic | 
| Kevin | Focus | 
| Mark | Civic | 
| Lisa | Focus | 
| Lisa | Civic | 
+-------+-------+ 

这个查询汽车的表给我所有与Lisa完全相同的车以及Lisa自己,这很好。

;with cte as (
    select * 
    , cnt = count(*) over (partition by name) 
    from t 
) 
, matches as (
    select x2.name 
    from cte as x 
    inner join cte as x2 
     on x.model = x2.model 
     and x.cnt = x2.cnt 
     and x.name = 'Lisa' 
    group by x2.name, x.cnt 
    having count(*) = x.cnt 
) 
select t.* 
from t 
    inner join matches m 
    on t.name = m.name 

结果:

+-------+-------+ 
| name | model | 
+-------+-------+ 
| Lisa | Civic | 
| Lisa | Focus | 
| Kevin | Civic | 
| Kevin | Focus | 
+-------+-------+ 

如果我想找出谁拥有相同的车作为Bob所有的人,我重新运行查询和结果应该给我John

现在,我有一个在Java名称列表,并为每个名称,我运行此查询。它真的很慢。无论如何,要找到所有拥有相同汽车的人,并在单个数据库调用中将结果划分为多个组?

例如,使用第一个表。我可以运行一个查询来分组名称。注意Mark已经消失了,因为他不像其他人一样拥有完全相同的汽车,而只是一个子集。

+-------+-------+-------+ 
| Name | Model | Group | 
+-------+-------+-------+ 
| Bob | Camry |  1 | 
| Bob | Civic |  1 | 
| Bob | Prius |  1 | 
| John | Camry |  1 | 
| John | Civic |  1 | 
| John | Prius |  1 | 
| Kevin | Civic |  2 | 
| Kevin | Focus |  2 | 
| Lisa | Focus |  2 | 
| Lisa | Civic |  2 | 
+-------+-------+-------+ 

这个结果集也很好,我只需要知道谁属于哪个组,我可以晚点取车。

+-------+-------+ 
| Name | Group | 
+-------+-------+ 
| Bob |  1 | 
| John |  1 | 
| Kevin |  2 | 
| Lisa |  2 | 
+-------+-------+ 

我需要以某种方式遍历名称的列表,找到谁拥有相同的车所有的人,然后将其全部合并成一个结果集。

回答

2

你可以做这两个方法。一种方法是做复杂的连接。另一种方式是捷径。只需将车辆聚合成一个字符串并比较字符串即可。

with nc as (
     select n.name, 
      stuff((select ',' + t.model 
        from t 
        where t.name = n.name 
        order by t.model 
        for xml path ('') 
        ), 1, 1, '') as cars 
     from (select distinct name from t) n 
    ) 
select nc.name, nc.cars, dense_rank() over (order by nc.cars) 
from nc 
order by nc.cars; 

这会创建一个名单和汽车列表作为逗号分隔列表。如果你喜欢,你可以回到原始表格来获取原始行。

+0

您的回答和@SqlZim返回了相同的结果,除了您的查询有一个更简单的执行计划,所以我只是接受了这一点。 –

1

使用像VKP对前一个问题的答案拼接的方法将在这里工作,以及如果我们增加dense_rank()

with cte as (
select 
    name 
    , models = stuff((
     select 
     ',' + i.model 
     from t i 
     where i.name=t.name 
     order by 1 
     for xml path(''), type).value('.','varchar(max)') 
     ,1,1,'') 
from t 
group by name 
) 
select 
    name 
    , models 
    , dr = dense_rank() over (order by models) 
from cte 

rextester:http://rextester.com/GTT11495

结果:

+-------+-------------------+----+ 
| name |  models  | dr | 
+-------+-------------------+----+ 
| Bob | Camry,Civic,Prius | 1 | 
| Mark | Civic    | 2 | 
| Kevin | Civic,Focus  | 3 | 
| Lisa | Civic,Focus  | 3 | 
+-------+-------------------+----+ 
-1
Select *, row_number() Over(partition by Model order by name) as Group 
from t 
where model in(Select Model from t 
       Where name in('Bob', 'Lisa')) 
0

用othar样本数据试试这个。它可以在所有情况下工作。

declare @t table(Name varchar(50),Model varchar(50)) 
insert into @t values 
('Bob','Camry') 
,('Bob','Civic') 
,('Bob','Prius') 
,('Kevin','Civic') 
,('Kevin','Focus') 
,('Mark','Civic') 
,('Lisa','Focus') 
,('Lisa','Civic') 
,('John','Camry') 
,('John','Civic') 
,('John','Prius') 
declare @input varchar(50)='Bob' 



    ;with 
CTE1 AS 
(
select name,model,ROW_NUMBER()over(order by name) rn 
from @t 
where [email protected] 
) 
,cte2 as 
(
select t.name,t.Model 
,ROW_NUMBER()over(partition by t.name order by t.name) rn3 
from @t t 
inner JOIN 
cte1 c on t.Model=c.model 
where t.Name [email protected] 
) 
select * from cte2 c 
where exists(select rn3 from cte2 c1 
where c1.name=c.name and c1.rn3=(select max(rn) from cte1) 
) 
+0

@EricGuan,看我编辑的答案。 – KumarHarsh