2013-06-21 3 views
1

Предположим, у меня есть две таблицы.Поиск из таблицы с использованием значений столбца другой таблицы

table one: 

| col1 | 
- - - - - 
| do | 
| big | 
| gone | 

table two 

| col1 | col2 | col3 | col4 | 
- - - - - - - - - - - - - - - 
| do | blah | blah | big | 
| big | do | blah | gone | 
| blah | blah | blah | blah | 

как я поиск от table two таким образом, что строки, которые отображаются содержат все значения col1 из table one

например, для. результат для данной ситуации должен быть

| col1 | col2 | col3 | col4 | 
- - - - - - - - - - - - - - - 
| big | do | blah | gone | 
+0

Запись в таблице 2 должна соответствовать каждой записи в таблице 1 для хотя бы одного из ее столбцов? – rkpasia

+0

Да, как показано на примере. –

+0

@ KaranJ.S. , , , Какую базу данных вы используете? –

ответ

1

Насти проблема ...

SELECT two.* 
    FROM two 
WHERE (SELECT COUNT(*) FROM one) = 
     (CASE WHEN col1 IN (SELECT * FROM one) THEN 1 ELSE 0 END + 
     CASE WHEN col2 IN (SELECT * FROM one) THEN 1 ELSE 0 END + 
     CASE WHEN col3 IN (SELECT * FROM one) THEN 1 ELSE 0 END + 
     CASE WHEN col4 IN (SELECT * FROM one) THEN 1 ELSE 0 END 
     ) 

Термин «эффективность» не следует упоминать в связи с этим запросом.

+2

. , Это не работает. Если строка содержала значение «большой» в трех столбцах, оно удовлетворяло бы условию. См. Мой ответ ниже. –

+2

Пожалуйста, не принимайте этот ответ, поэтому я могу его удалить, если вы не можете гарантировать, что ни первая, ни третья строка в данных примера никогда не появятся, потому что разные столбцы будут содержать разные значения (то есть значения в col1..col4 все будет отчетливо). –

1

Возможно, самой сложной частью этого является то, что все столбцы покрыты второй таблицей. Это не просто достаточно, чтобы считать их, вы также должны быть уверены, что все множество:

select t.* 
from two t left outer join 
    one o1 
    on o1.col1 = t.col1 left outer join 
    one o2 
    on o2.col1 = t.col2 and o2.col1 not in (coalesce(t.col1, '')) left outer join 
    one o3 
    on o3.col1 = t.col3 and o3.col1 not in (coalesce(t.col1, ''), coalesce(t.col2, '')) left outer join 
    one o4 
    on o4.col1 = t.col4 and o4.col1 not in (coalesce(t.col1, ''), coalesce(t.col2, ''), coalesce(t.col3, '')) cross join 
    (select count(*) as cnt from one) const 
where const.cnt = ((case when o1.col1 is not null then 1 else 0 end) + 
        (case when o2.col1 is not null then 1 else 0 end) + 
        (case when o3.col1 is not null then 1 else 0 end) + 
        (case when o4.col1 is not null then 1 else 0 end) 
       ) 

Это смотрит каждое значение в one таблице, при условии, что значение не видели раньше. Если в таблице one есть дубликаты, возникает вопрос о том, как их обрабатывать. Значит ли это, что значение должно появиться много раз?

+0

Понадобилось время, чтобы понять, как это работает, но я, наконец, понял. Но есть проблема, если в таблице 1 есть повторяющиеся значения. Как написано, он не может вернуть то, что должно соответствовать совпадающим строкам. Переход на 'count (distinct col1)' возвращает дубликаты совпадающих строк. – dbenham

+0

Повторяющиеся значения в одном случае не являются проблемой, если вы выберете из CTE, который выбирает отдельные значения из одного. – dbenham

+0

@dbenham. , , Это легко фиксируется путем изменения 'count (*) как cnt' для' count (distinct col1) 'as cnt' в подзапросе. –

0

Вы не указали, какой SQL-движок вы используете - это может иметь значение.

Я предоставил решение, требующее поддержки функции row_number(). Я считаю, что по крайней мере Oracle, DB2 и SQLServer поддерживают строку row_number().

Проблема довольно прямолинейная, как только отдельные значения из одной таблицы поворачиваются в одну строку. Не может быть никаких совпадений, если в таблице 1 существует более 4 различных значений. Кажется, должен быть лучший способ сделать поворот, но я знаю, что это решение работает.

Я приложил все усилия, чтобы убедиться, что ответ возвращает все строки из двух, если один пуст, а дублированные строки в одном игнорируются.

with 
uniqueOne as ( 
    select distinct col1 from one 
), 
ranked as (
    select col1, row_number() over (order by col1) seq from uniqueOne 
), 
vals as (
    select t1.col1 val1, 
     t2.col1 val2, 
     t3.col1 val3, 
     t4.col1 val4 
    from (select 1 dummy) dummy 
    left join ranked t1 on t1.seq=1 
    left join ranked t2 on t2.seq=2 
    left join ranked t3 on t3.seq=3 
    left join ranked t4 on t4.seq=4 
    left join ranked t5 on t5.seq=5 
    where t5.seq is null 
) 
select two.* 
    from two 
cross join vals 
where (vals.val1 is null or vals.val1 in (two.col1, two.col2, two.col3, two.col4)) 
    and (vals.val2 is null or vals.val2 in (two.col1, two.col2, two.col3, two.col4)) 
    and (vals.val3 is null or vals.val3 in (two.col1, two.col2, two.col3, two.col4)) 
    and (vals.val4 is null or vals.val4 in (two.col1, two.col2, two.col3, two.col4)) 
; 


Вот live demo of the solution


Боже мой, я думаю, я должен прочитать свой ответ и сделать некоторые исследования чаще. SQLServer имеет оператор Pivot, который делает решение очень эффективным. Oracle также имеет Pivot, но использует другой синтаксис.

Адрес working demo of the SQLServer Pivot solution. Взгляните на план сладкого исполнения.

А вот SQLServer запрос:

with 
uniqueOne as ( 
    select distinct col1 from one 
), 
ranked as (
    select col1, row_number() over (order by col1) seq from uniqueOne 
), 
vals as (
    select [1] val1, [2] val2, [3] val3, [4] val4, [5] val5 
    from ranked 
    pivot (min(col1) for seq in ([1],[2],[3],[4],[5])) PivotTable 
) 
select two.* 
    from two 
    join vals on val5 is null 
where (vals.val1 is null or vals.val1 in (two.col1, two.col2, two.col3, two.col4)) 
    and (vals.val2 is null or vals.val2 in (two.col1, two.col2, two.col3, two.col4)) 
    and (vals.val3 is null or vals.val3 in (two.col1, two.col2, two.col3, two.col4)) 
    and (vals.val4 is null or vals.val4 in (two.col1, two.col2, two.col3, two.col4)) 
; 
1

Это предполагает КТР, и bitoperations (leftshift и OR), имеющихся в наличии в Postgres (может присутствовать в других DBMSses тоже)

WITH rnk AS (
    SELECT col1, (rank() OVER (ORDER BY col1))::integer AS rnk 
    FROM one 
    ) 
, five AS (
    SELECT t.* 
      , 0::integer 
      | COALESCE(1<< o1.rnk, 0) 
      | COALESCE(1<< o2.rnk, 0) 
      | COALESCE(1<< o3.rnk, 0) 
      | COALESCE(1<< o4.rnk, 0) 
      AS mask 
    FROM two t 
    LEFT JOIN rnk o1 ON o1.col1 = t.col1 
    LEFT JOIN rnk o2 ON o2.col1 = t.col2 
    LEFT JOIN rnk o3 ON o3.col1 = t.col3 
    LEFT JOIN rnk o4 ON o4.col1 = t.col4 
    ) 
SELECT * FROM five f5 
WHERE f5.mask IN (14) 
    ; 

Обновление: это может быть немного чище, так как скрывает бит-брейк внутри CTE.

WITH xrnk AS (
    SELECT col1, 1::integer << (rank() OVER (ORDER BY col1))::integer AS xrnk 
    FROM one 
    ) 
, five AS (
    SELECT t.* 
     , (COALESCE(o1.xrnk, 0) 
      | COALESCE(o2.xrnk, 0) 
      | COALESCE(o3.xrnk, 0) 
      | COALESCE(o4.xrnk, 0) 
     ) >> 1 
     AS mask 
    FROM two t 
    LEFT JOIN xrnk o1 ON o1.col1 = t.col1 
    LEFT JOIN xrnk o2 ON o2.col1 = t.col2 
    LEFT JOIN xrnk o3 ON o3.col1 = t.col3 
    LEFT JOIN xrnk o4 ON o4.col1 = t.col4 
    ) 
SELECT * FROM five f5 
WHERE f5.mask IN (7) 
    ; 

Самое простое решение всегда лучше:

SELECT * FROM two t 
WHERE NOT EXISTS (
     SELECT * FROM one o 
     WHERE o.col1 <> t.col1 AND o.col1 <> t.col2 
      AND o.col1 <> t.col3 AND o.col1 <> t.col4 
     ) 
     ; 

UPDATE: (спасибо @dbenham) простой запрос весьма чувствителен к значению NULL в two таблицы, которая должна быть обработана связкой от COALESCE() обертки. Литерал «XxxX» не должен совпадать, очевидно:

SELECT * FROM two t 
WHERE NOT EXISTS (
     SELECT * FROM one o 
     WHERE o.col1 <> COALESCE(t.col1, 'XxxX') 
      AND o.col1 <> COALESCE(t.col2, 'XxxX') 
      AND o.col1 <> COALESCE(t.col3, 'XxxX') 
      AND o.col1 <> COALESCE(t.col4, 'XxxX') 
     ) 
     ; 
+1

+1 - ooh, мне нравится последний вариант, если ни один из столбцов таблицы не равен NULL. Стратегическое использование COALESCE может решить проблему NULL. SQLServer делает отличную работу по оптимизации запроса в левом анти-полу-соединении. Примечание. Предложение where может быть упрощено до 'where o.col1 не в (t.col1, t.col2, t.col3, t.col4)'. Опять же, значения NULL потребуют добавления коалесценции. – dbenham

+0

Вы правы насчет NULLS. «IN (a, b, c, d)» тоже будет работать. Найти righ-конструкцию с использованием COALESCE() сложно; вызванное двойным отрицанием. BTW: первые 2 запроса не страдают от проблемы NULL. – wildplasser

+0

Вы можете использовать что-то вроде 'coalesce (t.col1, 'x' || o.col1)', чтобы гарантировать отсутствие соответствия, если t.col1 имеет значение null. – dbenham