2017-03-09 87 views
1

我的人(CarOwners)一臺,以及各種他們自己查找組相匹配的行

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

給一個名字的車,我要如何找到其他人的確切同一汽車行駛?例如,如果我的目標是馬克,沒有其他人只有思域,所以查詢將不會返回任何內容。如果我的目標是Lisa,查詢將返回

+-------+-------+ 
| Name | Model | 
+-------+-------+ 
| Kevin | Civic | 
| Kevin | Focus | 
+-------+-------+ 

因爲凱文和麗莎的車完全一樣。如果我定位凱文,查詢將返回麗莎。

我創建了一個包含我的目標人車的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' 

此查詢將返回所有具有思域或焦點的人,這不是我要找的。

+-------+-------+ 
| Name | Model | 
+-------+-------+ 
| Bob | Civic | 
| Kevin | Civic | 
| Kevin | Focus | 
| Mark | Civic | 
+-------+-------+ 
+1

所以你想要完全匹配,而不是部分?即只有當一個人擁有與被查詢人完全相同的汽車(不多不少於)時纔是這樣。 – scsimon

+0

Yah。我的示例查詢已經提供了部分匹配。 –

回答

1

這與count()over()行計數爲每個name使用common table expression(CTE)的數量。

然後,matches cte使用自加入,其中名稱不匹配,模型匹配,每個名稱的模型計數匹配,其中一個名稱爲'Lisa'having子句可確保匹配行數(count(*))與name所具有的模型數相匹配。

matches本身只會返回每個人的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 

rextester演示:http://rextester.com/SUKP78304

回報:

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

我們也可以把它寫不熱膨脹係數,但它使得它有點難以遵循:

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 
+0

這工作,謝謝。 –

+0

@EricGuan樂於助人! – SqlZim

+0

有沒有辦法將'和x.name ='Lisa''更改爲一個名稱列表,然後接收由完全相同的汽車所有權劃分的結果集?現在,我使用Java中的循環調用此查詢,並傳遞每個名稱​​。這感覺非常低效。 –

0

一種方法是比較每個名稱的有序連接模型值。

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' 

如果您的SQL Server版本支持STRING_AGG,查詢可以簡化爲

with cte as (
    select name,model, 
     STRING_AGG(model,',') WITHIN GROUP(ORDER BY model) as 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' 
0

Tr Ÿ它

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 
0

既然你想你的對手是確切,我們應該添加的每個人擁有的汽車數量作爲附加字段。假設你的表名是「#owners」下面的查詢

select * 
     , (select COUNT(*) 
      from #owners o2 
      where o2.name = o1.name) as num 
    from #owners o1 

給我們

+-------+-------+-----+ 
| 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,以便它讀得更好。下面的查詢

; 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 
0

試試這個結果,我認爲這是非常容易和短代碼只有一個分區功能。

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

我現在編輯了我的答案。 – KumarHarsh