2008-09-19 11 views
5

se tenga lo siguiente:T-SQL: ¿Cómo obtengo las filas de una tabla cuyos valores coinciden por completo con los valores de otra tabla?

declare @a table 
(
    pkid int, 
    value int 
) 

declare @b table 
(
    otherID int, 
    value int 
) 


insert into @a values (1, 1000) 
insert into @a values (1, 1001) 
insert into @a values (2, 1000) 
insert into @a values (2, 1001) 
insert into @a values (2, 1002) 

insert into @b values (-1, 1000) 
insert into @b values (-1, 1001) 
insert into @b values (-1, 1002) 

¿Cómo la consulta para todos los valores de @ un concuerdan completamente con @b?

{@a.pkid = 1, @b.otherID = -1} no serían devueltos (sólo 2 de los 3 valores de concordancia)

{@a.pkid = 2, @b.otherID = -1} sería devuelto (3 de 3) valores partido

tablas refactoración, puede ser una opción.

EDIT: que he tenido éxito con las respuestas de James y Tom H.

Cuando agrego otro caso en @b, caen un poco corto.

insert into @b values (-2, 1000) 

Suponiendo que esta debe devolver dos filas adicionales ({@a.pkid = 1, @b.otherID = -2} y {@a.pkid = 2, @b.otherID = -2}, no funciona. Sin embargo, para mi proyecto esto no es un problema.

+0

he modificado la mina para dar cuenta de los duplicados – James

Respuesta

6

Probablemente no es la forma más barata de hacerlo:

SELECT a.pkId,b.otherId FROM 
    (SELECT a.pkId,CHECKSUM_AGG(DISTINCT a.value) as 'ValueHash' FROM @a a GROUP BY a.pkId) a 
    INNER JOIN (SELECT b.otherId,CHECKSUM_AGG(DISTINCT b.value) as 'ValueHash' FROM @b b GROUP BY b.otherId) b 
ON a.ValueHash = b.ValueHash 

se puede ver, básicamente, estoy creando un nuevo conjunto de resultados para cada uno valor que representa para el conjunto de valores en cada mesa de cada Id y unirse sólo cuando coinciden.

+0

Debo decir que su solución es un enfoque interesante. No estoy seguro de que confíe en un valor hash sin usar otro método de doble comprobación. Pero de todos modos es muy interesante de leer, así que solo quería decir que es genial. – Cervo

+0

Pensé que esta era una solución muy hábil, así que tuve que agregar mi voto. –

+0

Gracias. Sí, sería un poco receloso de confiar solo en un solo valor hash si hay una gran cantidad de datos. Puede ganar mucha confianza sin duplicar el gasto haciendo un segundo hash después de agregar algún valor fijo a cada uno de los valores a y b. Me gusta CHECKSUM_AGG (a.value + 'aaa') – James

0

Varias maneras de hacer esto, pero una simple es crear un punto de vista sindical como

create view qryMyUinion as 
select * from table1 
union all 
select * from table2 

tenga cuidado al utilizar la unión de todo, no una unión simple como que omitiremos los duplicados

º en hacer esto

select count(*), [field list here] 
from qryMyUnion 
group by [field list here] 
having count(*) > 1 

la Unión y recibirán declaraciones tienden a ser los más olvidados parte del estándar SQL, pero pueden resolver muchos problemas difíciles que de otro modo requiere el código de procedimiento

+0

¿Puedes publicar algún código que muestre esto? –

0

Si está tratando de devolver solo conjuntos completos de registros, puede intentar esto. Definitivamente recomendaría usar alias significativos ...

Cervo tiene razón, necesitamos una verificación adicional para asegurarnos de que a es una coincidencia exacta de b y no un superconjunto de b. Esta es una solución más difícil de manejar en este punto, por lo que esto solo sería razonable en contextos donde las funciones analíticas en las otras soluciones no funcionan.

select 
    a.pkid, 
    a.value 
from 
    @a a 
where 
    a.pkid in 
    (
    select 
     pkid 
    from 
     (
     select 
      c.pkid, 
      c.otherid, 
      count(*) matching_count 
     from 
      (
      select 
       a.pkid, 
       a.value, 
       b.otherid 
      from 
       @a a inner join @b b 
       on a.value = b.value 
      ) c 
     group by 
      c.pkid, 
      c.otherid 
     ) d 
     inner join 
     (
     select 
      b.otherid, 
      count(*) b_record_count 
     from 
      @b b 
     group by 
      b.otherid 
     ) e 
     on d.otherid = e.otherid 
     and d.matching_count = e.b_record_count 
     inner join 
     (
     select 
      a.pkid match_pkid, 
      count(*) a_record_count 
     from 
      @a a 
     group by 
      a.pkid 
     ) f 
     on d.pkid = f.match_pkid 
     and d.matching_count = f.a_record_count 
    ) 
+0

No creo que esto funcione. Tomé su código y lo corté y pegué con el código anterior y agregué el inserto de línea en los valores @ a (2, 1003). El problema es que tampoco verifica el recuento en @a. Entonces, si B coincide con todo en A, dices que es correcto. Esto ignora que A tenga más entradas que b. – Cervo

1

Obras para tu ejemplo, y creo que va a trabajar para todos los casos, pero no he probado a fondo:

SELECT 
    SQ1.pkid 
FROM 
    (
     SELECT 
      a.pkid, COUNT(*) AS cnt 
     FROM 
      @a AS a 
     GROUP BY 
      a.pkid 
    ) SQ1 
INNER JOIN 
    (
     SELECT 
      a1.pkid, b1.otherID, COUNT(*) AS cnt 
     FROM 
      @a AS a1 
     INNER JOIN @b AS b1 ON b1.value = a1.value 
     GROUP BY 
      a1.pkid, b1.otherID 
    ) SQ2 ON 
     SQ2.pkid = SQ1.pkid AND 
     SQ2.cnt = SQ1.cnt 
INNER JOIN 
    (
     SELECT 
      b2.otherID, COUNT(*) AS cnt 
     FROM 
      @b AS b2 
     GROUP BY 
      b2.otherID 
    ) SQ3 ON 
     SQ3.otherID = SQ2.otherID AND 
     SQ3.cnt = SQ1.cnt 
-1

Como dice CQ, una sencilla combinación interna es todo lo que necesita .

Select * -- all columns but only from #a 
from #a 
inner join #b 
on #a.value = #b.value -- only return matching rows 
where #a.pkid = 2 
1
 
-- Note, only works as long as no duplicate values are allowed in either table 
DECLARE @validcomparisons TABLE (
    pkid INT, 
    otherid INT, 
    num INT 
) 

INSERT INTO @validcomparisons (pkid, otherid, num) 
SELECT a.pkid, b.otherid, A.cnt 
FROM (select pkid, count(*) as cnt FROM @a group by pkid) a 
INNER JOIN (select otherid, count(*) as cnt from @b group by otherid) b 
    ON b.cnt = a.cnt 

DECLARE @comparison TABLE (
    pkid INT, 
    otherid INT, 
    same INT) 

insert into @comparison(pkid, otherid, same) 
SELECT a.pkid, b.otherid, count(*) 
FROM @a a 
INNER JOIN @b b 
    ON a.value = b.value 
GROUP BY a.pkid, b.otherid 

SELECT COMP.PKID, COMP.OTHERID 
FROM @comparison comp 
INNER JOIN @validcomparisons val 
    ON comp.pkid = val.pkid 
    AND comp.otherid = val.otherid 
    AND comp.same = val.num 
2

La siguiente consulta le da los resultados solicitados:

select A.pkid, B.otherId 
    from @a A, @b B 
    where A.value = B.value 
    group by A.pkid, B.otherId 
    having count(B.value) = (
     select count(*) from @b BB where B.otherId = BB.otherId) 
0

para recorrer el punto más:

select a.* 
from @a a 
inner join @b b on a.value = b.value 

Esto devolverá todos los valores de @a que coincidan @b

1

He añadido algunas más Casos de prueba. Puede cambiar su manejo duplicado cambiando la forma en que usa palabras clave distintas en sus agregados. Básicamente, obtengo un recuento de coincidencias y lo comparo con un recuento de coincidencias requeridas en cada @a y @b.

declare @a table 
(
    pkid int, 
    value int 
) 

declare @b table 
(
    otherID int, 
    value int 
) 


insert into @a values (1, 1000) 
insert into @a values (1, 1001) 

insert into @a values (2, 1000) 
insert into @a values (2, 1001) 
insert into @a values (2, 1002) 

insert into @a values (3, 1000) 
insert into @a values (3, 1001) 
insert into @a values (3, 1001) 

insert into @a values (4, 1000) 
insert into @a values (4, 1000) 
insert into @a values (4, 1001) 


insert into @b values (-1, 1000) 
insert into @b values (-1, 1001) 
insert into @b values (-1, 1002) 

insert into @b values (-2, 1001) 
insert into @b values (-2, 1002) 

insert into @b values (-3, 1000) 
insert into @b values (-3, 1001) 
insert into @b values (-3, 1001) 



SELECT Matches.pkid, Matches.otherId 
FROM 
(
    SELECT a.pkid, b.otherId, n = COUNT(*) 
    FROM @a a 
    INNER JOIN @b b 
     ON a.Value = b.Value 
    GROUP BY a.pkid, b.otherId 
) AS Matches 

INNER JOIN 
(
    SELECT 
     pkid, 
     n = COUNT(DISTINCT value) 
    FROM @a 
    GROUP BY pkid 
) AS ACount 
ON Matches.pkid = ACount.pkid 

INNER JOIN 
(
    SELECT 
     otherId, 
     n = COUNT(DISTINCT value) 
    FROM @b 
    GROUP BY otherId 
) AS BCount 
    ON Matches.otherId = BCount.otherId 

WHERE Matches.n = ACount.n AND Matches.n = BCount.n 
0

1) Asumo que usted no tiene ID duplicado

2) obtener la llave con el mismo número de valor

3) la fila con el número de clave de valor igual a el número de igual valor es el objetivo

espero que sea lo que ha buscado (no buscar el rendimiento no es así?)

declare @a table( pkid int, value int) 
declare @b table( otherID int, value int) 

insert into @a values (1, 1000) 
insert into @a values (1, 1001) 
insert into @a values (2, 1000) 
insert into @a values (2, 1001) 
insert into @a values (2, 1002) 
insert into @a values (3, 1000) 
insert into @a values (3, 1001) 
insert into @a values (4, 1000) 
insert into @a values (4, 1001) 
insert into @b values (-1, 1000) 
insert into @b values (-1, 1001) 
insert into @b values (-1, 1002) 
insert into @b values (-2, 1001) 
insert into @b values (-2, 1002) 
insert into @b values (-3, 1000) 
insert into @b values (-3, 1001) 

    select cntok.cntid1 as cntid1, cntok.cntid2 as cntid2 
    from 
(select cnt.cnt, cnt.cntid1, cnt.cntid2 from 
    (select acnt.cnt as cnt, acnt.cntid as cntid1, bcnt.cntid as cntid2 from 
      (select count(pkid) as cnt, pkid as cntid from @a group by pkid) 
      as acnt 
       full join 
       (select count(otherID) as cnt, otherID as cntid from @b group by otherID) 
       as bcnt 
        on acnt.cnt = bcnt.cnt) 
    as cnt 
    where cntid1 is not null and cntid2 is not null) 
    as cntok 
inner join 
(select count(1) as cnt, cnta.cntid1 as cntid1, cnta.cntid2 as cntid2 
from 
    (select cnt, cntid1, cntid2, a.value as value1 
    from 
     (select cnt.cnt, cnt.cntid1, cnt.cntid2 from 
      (select acnt.cnt as cnt, acnt.cntid as cntid1, bcnt.cntid as cntid2 from 
        (select count(pkid) as cnt, pkid as cntid from @a group by pkid) 
        as acnt 
         full join 
         (select count(otherID) as cnt, otherID as cntid from @b group by otherID) 
         as bcnt 
          on acnt.cnt = bcnt.cnt) 
      as cnt 
      where cntid1 is not null and cntid2 is not null) 
     as cntok 
      inner join @a as a on a.pkid = cntok.cntid1) 
     as cnta 
     inner join 

      (select cnt, cntid1, cntid2, b.value as value2 
      from 
      (select cnt.cnt, cnt.cntid1, cnt.cntid2 from 
        (select acnt.cnt as cnt, acnt.cntid as cntid1, bcnt.cntid as cntid2 from 
          (select count(pkid) as cnt, pkid as cntid from @a group by pkid) 
          as acnt 
           full join 
           (select count(otherID) as cnt, otherID as cntid from @b group by otherID) 
           as bcnt 
            on acnt.cnt = bcnt.cnt) 
        as cnt 
        where cntid1 is not null and cntid2 is not null) 
       as cntok 
        inner join @b as b on b.otherid = cntok.cntid2) 
       as cntb 
       on cnta.cntid1 = cntb.cntid1 and cnta.cntid2 = cntb.cntid2 and cnta.value1 = cntb.value2 
     group by cnta.cntid1, cnta.cntid2) 
    as cntequals 
    on cntok.cnt = cntequals.cnt and cntok.cntid1 = cntequals.cntid1 and cntok.cntid2 = cntequals.cntid2 
1

¿Cómo consulto todos los valores en @a que coinciden por completo con @b?

Me temo que esta definición no es del todo clara. Parece en su ejemplo adicional que desea todos los pares de a.pkid, b.otherID para los cuales cada b.valor para el b.otherID dado también es un a.valor para el a.pkid dado.

En otras palabras, desea los pkids en @a que tienen al menos todos los valores para otherIDs en b. Los valores adicionales en @a parecen estar bien. Nuevamente, este es un razonamiento basado en su ejemplo adicional, y la suposición de que (1, -2) y (2, -2) serían resultados válidos. En ambos casos, los valores a.value para el pkid dado son más que los valores de b.value para el otherID dado.

Entonces, con esto en mente:

select 
    matches.pkid 
    ,matches.otherID 
from 
(
    select 
     a.pkid 
     ,b.otherID 
     ,count(1) as cnt 
    from @a a 
    inner join @b b 
     on b.value = a.value 
    group by 
     a.pkid 
     ,b.otherID 
) as matches 
inner join 
(
    select 
     otherID 
     ,count(1) as cnt 
    from @b 
    group by otherID 
) as b_counts 
on b_counts.otherID = matches.otherID 
where matches.cnt = b_counts.cnt 
7

Esto es más eficiente (que utiliza TOP 1 en lugar de COUNT), y trabaja con (-2, 1000):

SELECT * 
FROM (
     SELECT ab.pkid, ab.otherID, 
       (
       SELECT TOP 1 COALESCE(ai.value, bi.value) 
       FROM (
         SELECT * 
         FROM @a aii 
         WHERE aii.pkid = ab.pkid 
         ) ai 
       FULL OUTER JOIN 
         (
         SELECT * 
         FROM @b bii 
         WHERE bii.otherID = ab.otherID 
         ) bi 
       ON  ai.value = bi.value 
       WHERE ai.pkid IS NULL OR bi.otherID IS NULL 
       ) unmatch 
     FROM 
       (
       SELECT DISTINCT pkid, otherid 
       FROM @a a , @b b 
       ) ab 
     ) q 
WHERE unmatch IS NOT NULL 
+0

Me he encontrado con un problema similar y he decidido usar su solución en lugar de la de James (creo que su solución es buena pero resbaladiza). +1 para ti. – rafek

Cuestiones relacionadas