Sql 查找具有匹配行的组
我有一张关于人、车主和他们拥有的汽车类型的表格Sql 查找具有匹配行的组,sql,sql-server,Sql,Sql Server,我有一张关于人、车主和他们拥有的汽车类型的表格 +-------+-------+ | Name | Model | +-------+-------+ | Bob | Camry | | Bob | Civic | | Bob | Prius | | Kevin | Civic | | Kevin | Focus | | Mark | Civic | | Lisa | Focus | | Lisa | Civic | +-------+-------+ 如果给我一个名字,
+-------+-------+
| Name | Model |
+-------+-------+
| Bob | Camry |
| Bob | Civic |
| Bob | Prius |
| Kevin | Civic |
| Kevin | Focus |
| Mark | Civic |
| Lisa | Focus |
| Lisa | Civic |
+-------+-------+
如果给我一个名字,我如何找到其他拥有完全相同汽车的人?例如,如果我以Mark为目标,没有其他人只有Civic,因此查询将不返回任何结果。如果我以Lisa为目标,查询将返回
+-------+-------+
| Name | Model |
+-------+-------+
| Kevin | Civic |
| Kevin | Focus |
+-------+-------+
因为凯文和丽莎的车一模一样。如果我以Kevin为目标,查询将返回Lisa
我创建了一个包含我的目标人物汽车的cte,但我不确定如何实现确切的匹配要求。我所有的尝试都会返回子集匹配的结果
with LisaCars as (
SELECT Model FROM CarOwners WHERE Name = 'Lisa'
)
SELECT Name, Model
FROM CarOwners
WHERE Model in (SELECT * FROM LisaCars) AND Name != 'Lisa'
这个查询将返回所有拥有Civic或Focus的人,这不是我想要的
+-------+-------+
| Name | Model |
+-------+-------+
| Bob | Civic |
| Kevin | Civic |
| Kevin | Focus |
| Mark | Civic |
+-------+-------+
一种方法是比较每个名称的有序连接模型值
with cte as (
select name,model,
STUFF((
SELECT ',' + t2.model
FROM t t2
WHERE t1.name=t2.name
ORDER BY model
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '') concat_value
from t t1
)
select distinct x2.name,x2.model
from cte x1
join cte x2 on x1.concat_value=x2.concat_value and x1.name<>x2.name
where x1.name='Kevin'
这将使用带有count的cte统计每个名称的行数 然后matches cte使用自联接,其中名称不匹配,模型匹配,每个名称匹配的模型数,其中一个名称是“Lisa”。having子句确保匹配行计数count*与name拥有的模型数匹配 匹配本身只返回每个人的姓名,因此我们返回源表t以获得每个匹配的完整模型列表
;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.name <> x2.name
and 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
我们也可以在没有ctes的情况下编写,但这样做会让我们更难理解:
select t.*
from t
inner join (
select x2.Name
from (
select *, cnt = count(*) over (partition by name)
from t
where name='Lisa'
) as x
inner join (
select *, cnt = count(*) over (partition by name)
from t
) as x2
on x.name <> x2.name
and x.model = x2.model
and x.cnt = x2.cnt
group by x2.name, x.cnt
having count(*) = x.cnt
) as m
on t.name = m.name
试试这个
if object_id('tempdb.dbo.#temp') is not null
drop table #temp
create table #temp (name varchar(100),model varchar(100))
insert into #temp values('Bob','Camry')
insert into #temp values('Bob','Civic')
insert into #temp values('Bob','Prius')
insert into #temp values('Kevin','Focus')
insert into #temp values('Kevin','Civic')
insert into #temp values('Mark','Civic')
insert into #temp values('Lisa','Focus')
insert into #temp values('Lisa','Civic')
select * from (
select row_number() over(partition by name order by (select null)) as n,
row_number() over(partition by model order by (select null)) as m,*
from #temp) as a
where n = m
order by name
由于您希望您的匹配准确无误,我们应该将每个人拥有的汽车数量添加为一个附加字段。假设您的表名为“owner”,则执行以下查询
select *
, (select COUNT(*)
from #owners o2
where o2.name = o1.name) as num
from #owners o1
; with
OwnedCount as (
select *
, (select COUNT(*)
from #owners o2
where o2.name = o1.name) as num
from #owners o1
)
select *
from OwnedCount o1
inner join OwnedCount o2
on o1.model = o2.model
and o1.num = o2.num
给我们桌子
+-------+-------+-----+
| Name | Model | num |
+-------+-------+-----+
| Bob | Camry | 3 |
| Bob | Civic | 3 |
| Bob | Prius | 3 |
| Kevin | Civic | 2 |
| Kevin | Focus | 2 |
| Mark | Civic | 1 |
| Lisa | Focus | 2 |
| Lisa | Civic | 2 |
+-------+-------+-----+
然后我们要将这个表连接到匹配模型和计数的表本身。我们使用CTE以便它读起来更好。下面的查询
select *
, (select COUNT(*)
from #owners o2
where o2.name = o1.name) as num
from #owners o1
; with
OwnedCount as (
select *
, (select COUNT(*)
from #owners o2
where o2.name = o1.name) as num
from #owners o1
)
select *
from OwnedCount o1
inner join OwnedCount o2
on o1.model = o2.model
and o1.num = o2.num
给我们这张桌子
+-------+-------+-----+-------+-------+-----+
| Name | Model | num | Name | Model | num |
+-------+-------+-----+-------+-------+-----+
| Bob | Camry | 3 | Bob | Camry | 3 |
| Bob | Civic | 3 | Bob | Civic | 3 |
| Bob | Prius | 3 | Bob | Prius | 3 |
| Kevin | Civic | 2 | Kevin | Civic | 2 |
| Kevin | Civic | 2 | Lisa | Civic | 2 |
| Kevin | Focus | 2 | Kevin | Focus | 2 |
| Kevin | Focus | 2 | Lisa | Focus | 2 |
| Mark | Civic | 1 | Mark | Civic | 1 |
| Lisa | Civic | 2 | Kevin | Civic | 2 |
| Lisa | Civic | 2 | Lisa | Civic | 2 |
| Lisa | Focus | 2 | Kevin | Focus | 2 |
| Lisa | Focus | 2 | Lisa | Focus | 2 |
+-------+-------+-----+-------+-------+-----+
最后,按所需名称筛选结果
declare @given_name varchar(32) = 'Lisa'
; with
OwnedCount as (
select *
, (select COUNT(*)
from #owners o2
where o2.name = o1.name) as num
from #owners o1
)
select o2.name, o2.model
from OwnedCount o1
inner join OwnedCount o2
on o1.model = o2.model
and o1.num = o2.num
where o1.name = @given_name
and o2.name <> @given_name
试试这个,我认为只需一个分区函数,代码就简单多了
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')
declare @input varchar(50)='Lisa'
;with
CTE1 AS
(
select name,model,ROW_NUMBER()over( order by name) rn
from @t
where name=@input
)
,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 !=@input
)
select * from cte2 c
where exists(select rn3 from cte2 c1
where c1.name=c.name and c1.rn3=(select max(rn) from cte1)
)
所以你想要完全匹配,而不是部分匹配?i、 e.只有当一个人拥有与被询问人完全相同的汽车时才可以?耶。我的示例查询已经提供了部分匹配。@很乐意提供帮助!是否仍然需要将x.name='Lisa'更改为名称列表,然后接收按完全相同的汽车所有权划分的结果集?现在,我用Java中的循环调用这个查询,传递每个名称。感觉效率太低了。嗨,Zim,我在这里发布了一个新问题和我想要的结果,希望你能继续拯救我的一天。我也会查看你的链接。