SQL WHERE .. IN cláusula varias columnas

173

Necesito implementar la siguiente consulta en SQL Server:

select *
from table1
WHERE  (CM_PLAN_ID,Individual_ID)
IN
(
 Select CM_PLAN_ID, Individual_ID
 From CRM_VCM_CURRENT_LEAD_STATUS
 Where Lead_Key = :_Lead_Key
)

Pero la cláusula WHERE..IN permite solo 1 columna. ¿Cómo puedo comparar 2 o más columnas con otro SELECT interno?

ala
fuente
Traté de proporcionar una descripción general de las soluciones relevantes, con las precauciones necesarias aquí: stackoverflow.com/a/54389589/983722
Dennis Jaheruddin

Respuestas:

110

Puede hacer una tabla derivada de la subconsulta y unir table1 a esta tabla derivada:

select * from table1 LEFT JOIN 
(
   Select CM_PLAN_ID, Individual_ID
   From CRM_VCM_CURRENT_LEAD_STATUS
   Where Lead_Key = :_Lead_Key
) table2
ON 
   table1.CM_PLAN_ID=table2.CM_PLAN_ID
   AND table1.Individual=table2.Individual
WHERE table2.CM_PLAN_ID IS NOT NULL
sleske
fuente
77
o más generalmente SELECT * FROM table INNER JOIN otherTable ON (table.x = otherTable.a AND table.y = otherTable.b)
ala
44
¿Qué pasa con las múltiples filas que existirían si la tabla 2 es hija de la tabla 1? ¿Y por qué IZQUIERDA UNIRSE?
gbn
1
Sí, INNER JOIN sería más eficiente aquí. Hacer una IZQUIERDA IZQUIERDA y filtrar los valores nulos de la tabla 2 es solo una forma detallada de usar una
UNIÓN INTERNA
Incorrecto, esto entrega la fila varias veces, suponiendo que la tabla unida se pueda unir varias veces ... de lo contrario, haga una unión interna y podrá ahorrar el dónde.
Stefan Steiger
123

En su lugar, querrá usar la sintaxis WHERE EXISTS.

SELECT *
FROM table1
WHERE EXISTS (SELECT *
              FROM table2
              WHERE Lead_Key = @Lead_Key
                        AND table1.CM_PLAN_ID = table2.CM_PLAN_ID
                        AND table1.Individual_ID = table2.Individual_ID)
mrdenny
fuente
55
Si bien esto funcionaría, convierte la consulta no correlacionada en la pregunta en una consulta correlacionada. A menos que el optimizador de consultas es inteligente, esto podría darle O (n ^ 2) el rendimiento :-( Pero tal vez estoy subestimando el optimizador ....
sleske
1
Uso sintaxis como esta todo el tiempo sin problemas. A menos que esté utilizando un optimizador anterior (6.5, 7, 8, etc.) no debería tener problemas con esta sintaxis.
mrdenny el
1
@sleske: EXISTE es mucho mejor: vea mis comentarios en mi respuesta. Y pruébalo primero. @mrdenny: Al principio leí mal su respuesta, también usaría EXISTS
gbn
66
Esto es más eficiente, +1. Vea este artículo en mi blog para comparar el rendimiento: explainextended.com/2009/06/17/efficient-exists
Quassnoi
1
Incluso SQL 2000 podría manejar la mayoría de las subconsultas correlacionadas sin convertir la consulta en una O (n ^ 2). Podría haber sido un problema en 6.5.
GilaMonster
14

ADVERTENCIA SOBRE SOLUCIONES:

MUCHAS SOLUCIONES EXISTENTES PROPORCIONARÁN LA SALIDA INCORRECTA SI LAS FILAS NO SON ÚNICAS

Si usted es la única persona que crea tablas, esto puede no ser relevante, pero varias soluciones le darán un número diferente de filas de salida del código en cuestión, cuando una de las tablas puede no contener filas únicas.

ADVERTENCIA SOBRE LA DECLARACIÓN DEL PROBLEMA:

CON MÚLTIPLES COLUMNAS NO EXISTE, PIENSA CUIDADOSAMENTE LO QUE QUIERES

Cuando veo una entrada con dos columnas, puedo imaginar que significa dos cosas:

  1. El valor de la columna a y la columna b aparecen en la otra tabla independientemente
  2. Los valores de la columna a y la columna b aparecen juntos en la otra tabla en la misma fila

El escenario 1 es bastante trivial, simplemente use dos declaraciones IN.

En línea con la mayoría de las respuestas existentes, por la presente proporciono una visión general de los enfoques mencionados y adicionales para el Escenario 2 (y un breve juicio):

EXISTE (Seguro, recomendado para SQL Server)

Según lo provisto por @mrdenny, EXISTS suena exactamente como lo que está buscando, aquí está su ejemplo:

SELECT * FROM T1
WHERE EXISTS
(SELECT * FROM T2 
 WHERE T1.a=T2.a and T1.b=T2.b)

IZQUIERDA SEMI UNIR (Seguro, recomendado para dialectos que lo soporten)

Esta es una forma muy concisa de unirse, pero desafortunadamente la mayoría de los dialectos de SQL, incluido el servidor SQL, actualmente no lo admiten.

SELECT * FROM T1
LEFT SEMI JOIN T2 ON T1.a=T2.a and T1.b=T2.b

Múltiples declaraciones IN (seguro, pero tenga cuidado con la duplicación de código)

Como mencionó @cataclysm, usar dos declaraciones IN también puede ser útil, tal vez incluso supere a las otras soluciones. Sin embargo, debe tener mucho cuidado con la duplicación de código. Si alguna vez desea seleccionar de una tabla diferente o cambiar la instrucción where, es un riesgo mayor que cree inconsistencias en su lógica.

Solución básica

SELECT * from T1
WHERE a IN (SELECT a FROM T2 WHERE something)
AND b IN (SELECT b FROM T2 WHERE something)

Solución sin duplicación de código (creo que esto no funciona en consultas regulares de SQL Server)

WITH mytmp AS (SELECT a, b FROM T2 WHERE something);
SELECT * from T1 
WHERE a IN (SELECT a FROM mytmp)
AND b IN (SELECT b FROM mytmp)

UNIÓN INTERNA (técnicamente se puede hacer seguro, pero a menudo esto no se hace)

La razón por la que no recomiendo usar una unión interna como filtro es porque, en la práctica, las personas a menudo permiten que los duplicados en la tabla derecha causen duplicados en la tabla izquierda. Y para empeorar las cosas, a veces hacen que el resultado final sea distinto, mientras que la tabla de la izquierda en realidad puede no necesitar ser única (o no única en las columnas que seleccione). Además, le da la oportunidad de seleccionar realmente una columna que no existe en la tabla de la izquierda.

SELECT T1.* FROM T1
INNER JOIN 
(SELECT DISTINCT a, b FROM T2) AS T2sub
ON T1.a=T2sub.a AND T1.b=T2sub.b

Errores más comunes:

  1. Unirse directamente en T2, sin una subconsulta segura. Resultando en el riesgo de duplicación)
  2. SELECT * (Guaranateed para obtener columnas de T2)
  3. SELECCIONAR c (no garantiza que su columna venga y siempre venga de T1)
  4. No DISTINCT o DISTINCT en el lugar equivocado

CONCATENACIÓN DE COLUMNAS CON SEPARADOR (No muy seguro, rendimiento horrible)

El problema funcional es que si usa un separador que puede aparecer en una columna, se vuelve difícil asegurarse de que el resultado sea 100% exacto. El problema técnico es que este método a menudo incurre en conversiones de tipos e ignora completamente los índices, lo que resulta en un rendimiento posiblemente horrible. A pesar de estos problemas, tengo que admitir que a veces todavía lo uso para consultas ad-hoc en pequeños conjuntos de datos.

SELECT * FROM T1
WHERE CONCAT(a,"_",b) IN 
(SELECT CONCAT(a,"_",b) FROM T2)

Tenga en cuenta que si sus columnas son numéricas, algunos dialectos SQL requerirán que los convierta primero en cadenas. Creo que el servidor SQL hará esto automáticamente.


Para concluir: Como es habitual, hay muchas maneras de hacer esto en SQL, usar opciones seguras evitará sorpresas y le ahorrará tiempo y dolores de cabeza a largo plazo.

Dennis Jaheruddin
fuente
13
select * from tab1 where (col1,col2) in (select col1,col2 from tab2)

Nota:
Oracle ignora las filas donde una o más de las columnas seleccionadas son NULL. En estos casos, es probable que desee hacer uso de la NVL -Funktion para asignar NULL a un valor especial (que no debería ser en los valores);

select * from tab1
where (col1, NVL(col2, '---') in (select col1, NVL(col2, '---') from tab2)
tommes-pommes
fuente
2
Postgres admite, where (colA,colB) in (... some list of tuples...)pero no estoy seguro de qué otras bases de datos hacen lo mismo. Me interesaría saberlo.
Max Murphy
2
Esta sintaxis también es compatible con Oracle y DB2 / 400 (probablemente también con DB2). Deseo que SQL Server lo soporte.
CrazyIvan1974
DB2 lo admite.
Telmo Marques el
Incluso SQLite lo admite.
Holger Jakobs
13

Una simple cláusula EXISTS es más limpia

select *
from table1 t1
WHERE
EXISTS
(
 Select * --or 1. No difference...
 From CRM_VCM_CURRENT_LEAD_STATUS Ex
 Where Lead_Key = :_Lead_Key
-- correlation here...
AND
t1.CM_PLAN_ID = Ex.CM_PLAN_ID AND t1.CM_PLAN_ID =  Ex.Individual_ID
)

Si tiene varias filas en la correlación, entonces JOIN proporciona varias filas en la salida, por lo que necesitaría distintas. Lo que generalmente hace que los EXISTS sean más eficientes.

La nota SELECT *con JOIN también incluiría columnas de las tablas de limitación de filas

gbn
fuente
2

¿Por qué usar WHERE EXISTS o DERIVED TABLES cuando puedes hacer una unión interna normal?

SELECT t.*
FROM table1 t
INNER JOIN CRM_VCM_CURRENT_LEAD_STATUS s
    ON t.CM_PLAN_ID = s.CM_PLAN_ID
    AND t.Individual_ID = s.Individual_ID
WHERE s.Lead_Key = :_Lead_Key

Si el par de (CM_PLAN_ID, Individual_ID) no es único en la tabla de estado, es posible que necesite un SELECT DISTINCT t. * En su lugar.

BradC
fuente
3
Y el DISTINCT por lo general significa que existe es una más eficiente
GBN
0
Postgres SQL  : version 9.6
Total records on tables : mjr_agent = 145, mjr_transaction_item = 91800

1.Uso con EXISTS[Tiempo promedio de consulta: 1.42s]

SELECT count(txi.id) 
FROM 
mjr_transaction_item txi
WHERE 
EXISTS ( SELECT 1 FROM mjr_agent agnt WHERE agnt.agent_group = 0 AND (txi.src_id = agnt.code OR txi.dest_id = agnt.code) ) 

2.Uso con dos líneas INCláusula [Tiempo promedio de consulta: 0.37s]

SELECT count(txi.id) FROM mjr_transaction_item txi
WHERE 
txi.src_id IN ( SELECT agnt.code FROM mjr_agent agnt WHERE agnt.agent_group = 0 ) 
OR txi.dest_id IN ( SELECT agnt.code FROM mjr_agent agnt WHERE agnt.agent_group = 0 )

3.Utilizando con INNNER JOINpatrón [Tiempo promedio de consulta: 2.9s]

SELECT count(DISTINCT(txi.id)) FROM mjr_transaction_item txi
INNER JOIN mjr_agent agnt ON agnt.code = txi.src_id OR agnt.code = txi.dest_id
WHERE 
agnt.agent_group = 0

Entonces, elegí la segunda opción.

Cataclismo
fuente
Advertencia para futuros lectores: de acuerdo con la pregunta, es probable que desee utilizar ANDdeclaraciones en lugar de ORdeclaraciones.
Dennis Jaheruddin
@DennisJaheruddin .. Gracias por tu comentario y muy buenas explicaciones detalladas de tu respuesta. Tienes razón, la ORdeclaración probablemente genere duplicaciones. En mi caso, no hay filas que contengan lo mismo src_idy dest_iden una sola fila. Entonces, las duplicaciones no sucederán en mi caso.
Cataclismo el
-2

Si desea una tabla, use la siguiente consulta

SELECT S.* 
FROM Student_info S
  INNER JOIN Student_info UT
    ON S.id = UT.id
    AND S.studentName = UT.studentName
where S.id in (1,2) and S.studentName in ('a','b')

y datos de la tabla de la siguiente manera

id|name|adde|city
1   a   ad  ca
2   b   bd  bd
3   a   ad  ad
4   b   bd  bd
5   c   cd  cd

Luego salida como sigue

id|name|adde|city
1   a   ad  ca
2   b   bd  bd
Somnath Kadam
fuente
id in (1,2) and studentName in ('a','b')es totalmente no el mismo que (id, studentName) in ((1,'a'),(2,'b')). Solo piense en un registro que tenga id = 2 y name = 'a'. Por supuesto, si la ID es única, entonces el efecto disminuye, pero luego, si la ID es única, no necesitamos filtrar los nombres en absoluto.
quetzalcoatl
-2

Simplemente podemos hacer esto.

   select *
   from 
    table1 t, CRM_VCM_CURRENT_LEAD_STATUS c
    WHERE  t.CM_PLAN_ID = c.CRM_VCM_CURRENT_LEAD_STATUS
    and t.Individual_ID = c.Individual_ID
Rpant
fuente
-2

Concatenar las columnas juntas de alguna forma es un "truco", pero cuando el producto no admite semiuniones para más de una columna, a veces no tiene otra opción.

Ejemplo de dónde la solución de unión interna / externa no funcionaría:

select * from T1 
 where <boolean expression>
   and (<boolean expression> OR (ColA, ColB) in (select A, B ...))
   and <boolean expression>
   ...

Cuando las consultas no son triviales por naturaleza, a veces no tiene acceso a la tabla base configurada para realizar uniones internas / externas regulares.

Si utiliza este "pirateo", cuando combine campos, asegúrese de agregar suficiente delimitador entre ellos para evitar interpretaciones erróneas, p. Ej. ColA + ":-:" + ColB

John K
fuente
Esta respuesta parece ser inconsistente (menciona la concatenación y luego proporciona un ejemplo diferente). Además, en una nota más clara: siempre tenemos una opción ;-) Agregué el ejemplo de concatenación a mi descripción general aquí, con las notas al pie relevantes: stackoverflow.com/a/54389589/983722
Dennis Jaheruddin
-3

Funcioné más fácil de esta manera

Select * 
from table1 
WHERE  (convert(VARCHAR,CM_PLAN_ID) + convert(VARCHAR,Individual_ID)) 
IN 
(
 Select convert(VARCHAR,CM_PLAN_ID) + convert(VARCHAR,Individual_ID)
 From CRM_VCM_CURRENT_LEAD_STATUS 
 Where Lead_Key = :_Lead_Key 
) 

Espero que esto ayude :)

Lisandro Acosta
fuente
9
Ouch, aquí no se usa el índice para la cadena concat.
mrdenny 03 de
9
¡He rechazado esto porque es simplemente peligroso! Si CM_PLAN_ID = 45y Individual_ID = 3luego la concatenación resulta 453, lo cual es indistinguible del caso donde CM_PLAN_ID = 4y Individual_ID = 53... pidiendo problemas, habría pensado
El Ronnoco
55
... por supuesto, podría concatenar con un carácter especial arbitrario, por ejemplo, 45_3o 45:3pero todavía no es una buena solución y, por supuesto, como @mrdenny dice que los índices no se utilizarán ahora que se ha producido una transformación en las columnas.
El Ronnoco
1
También voté en contra, ya que esta solución es realmente un "truco" rápido solamente. Es lento y, como dijo El Ronnoco, puede provocar errores.
-4

La forma simple e incorrecta sería combinar dos columnas usando + o concatenar y hacer una columna.

Select *
from XX
where col1+col2 in (Select col1+col2 from YY)

Esto sería, por supuesto, bastante lento. No se puede usar en la programación, pero si está en el caso de que solo esté buscando verificar, se puede usar algo.

Vijay
fuente
10
De hecho, y puede conducir a errores, ya que, por ejemplo, 'ab' + 'c' = 'a' + 'bc'