Найти список значений в списке значений

Я пытаюсь написать sql с предложением where, которое проверяет, находится ли какой-либо элемент в списке в другом списке. Есть ли более короткий способ сделать это, чем проверять каждого члена первого списка?

SELECT * from FOO
WHERE FOO.A IN ('2','3', '5', '7','11','13','17','19') OR
  FOO.B IN ('2','3', '5', '7','11','13','17','19') OR
  FOO.C IN ('2','3', '5', '7','11','13','17','19') OR
  FOO.D IN ('2','3', '5', '7','11','13','17','19') OR
  FOO.E IN ('2','3', '5', '7','11','13','17','19') OR
  FOO.F IN ('2','3', '5', '7','11','13','17','19')

Это упрощенный sql.

Старался сильно не мутить воду, но раз вы спрашиваете:

В конечном счете, я пытаюсь здесь выбрать строки из FOO, в которых есть столбцы, соответствующие различным критериям. Эти критерии хранятся во второй таблице (назовем ее BAR), в основном db, имя, тип должны совпадать, а флаг должен быть равен 1. Планировал построить список IN из BAR, сравнивая их с именами столбцов в INFORMATION_SCHEMA.COLUMNS, содержащих FOO

ФОО:

+--------+--------+---------+---------+--------+-------+
|   DB   | Name   | Type    | Col1    |  Col2  | Col3  |
+--------+--------+---------+---------+--------+-------+
|   4    | AC1    | LO      | 1       |  10    | 2     |
|   4    | AC1    | HI      | 2       |  20    | 4     |
|   1    | DC2    | HI-HI   | 11      |  5     | 2     |
|   1    | DC2    | HI      | 22      |  10    | 4     |
|   1    | DC2    | LO      | 33      |  15    | 6     |
+--------+--------+---------+---------+--------+-------+

БАР:

+--------+--------+---------+---------+--------+
|   DB   | Name   | Type    | Field   |  Flag  |
+--------+--------+---------+---------+--------+
|   4    | AC1    | LO      | Col1    |  1     |
|   4    | AC1    | HI      | Col1    |  1     |
|   1    | DC2    | HI-HI   | Col1    |  1     |
|   1    | DC2    | HI      | Col1    |  1     |
|   1    | DC2    | LO      | Col1    |  1     |
|   4    | AC1    | LO      | Col2    |  0     |
|   4    | AC1    | HI      | Col2    |  0     |
|   1    | DC2    | LO      | Col2    |  0     |
|   1    | DC2    | HI-HI   | Col2    |  0     |
|   1    | DC2    | HI      | Col2    |  0     |
|   4    | AC1    | LO      | Col3    |  0     |
|   4    | AC1    | HI      | Col3    |  0     |
|   1    | DC2    | LO      | Col3    |  0     |
|   1    | DC2    | HI-HI   | Col3    |  0     |
|   1    | DC2    | HI      | Col3    |  0     |
+--------+--------+---------+---------+--------+

person aggaton    schedule 19.04.2013    source источник
comment
какой диалект SQL вы используете? Если это тот, где вы можете определить переменные, то это должно быть выполнимо. Впрочем, что за диалект?   -  person vlad-ardelean    schedule 19.04.2013
comment
В настоящее время я пытаюсь заставить это работать с ms sql   -  person aggaton    schedule 19.04.2013
comment
Можете ли вы опубликовать структуру таблицы для Foo? Вам нужны другие столбцы в таблице?   -  person Taryn    schedule 19.04.2013


Ответы (3)


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

CREATE TABLE FOOVAL
{
    ID int, -- References FOO.ID
    TYPE char, -- A, B, C, D, E, F
    VAL int
}

SELECT * FROM FOO WHERE FOO.ID IN
(SELECT DISTINCT FOOVAL.ID WHERE FOOVAL.VAL IN ('2','3', '5', '7','11','13','17','19'))
person David Smith    schedule 19.04.2013

Ваш метод, вероятно, работает лучше всего. Вот альтернатива, которая требует создания списка только один раз. Он использует CTE для создания списка значений, а затем предложение exists для проверки совпадения каких-либо значений:

with vals as (
      select '2' as p union all
      select '3' union all
      select '5' union all
      select '7' union all
      select '11' union all
      select '13' union all
      select '17' union all
      select '19'
     )
select *
from foo
where exists (select 1 from vals where vals.p in (foo.A, foo.B, foo.C, foo.D, foo.E, foo.F))

Если вы используете базу данных, которая не поддерживает CTE, вы можете просто поместить код в предложение where:

select 8
from foo
where exists (select 1
              from (select '2' as p union all
                    select '3' union all
                    select '5' union all
                    select '7' union all
                    select '11' union all
                    select '13' union all
                    select '17' union all
                    select '19'
                   ) t
              where vals.p in (foo.A, foo.B, foo.C, foo.D, foo.E, foo.F)
             )

Если вы используете Oracle, вам нужно добавить from dual в операторах после строковых констант. В противном случае, я думаю, тот или иной должен работать в любой базе данных SQL.

person Gordon Linoff    schedule 19.04.2013
comment
Что, если ОП использует mysql? - person Kermit; 20.04.2013
comment
@FreshPrinceOfSO . . . Если OP не указывает базу данных, то я думаю, что ANSI SQL подходит для решения. В любом случае я добавил эквивалентный оператор в MySQL. - person Gordon Linoff; 20.04.2013

Хотя не совсем понятно, что вы хотите делать с данными, поскольку вы используете SQL Server, я предлагаю использовать функцию UNPIVOT для преобразования столбцов col1, col2 и col3 в строки, что упростит фильтрацию данных. :

select db, name, type, col, value
from foo
unpivot
(
  value
  for col in (Col1, Col2, Col3)
) unpiv;

См. SQL Fiddle с демонстрацией. Это дает данные в следующем формате:

| DB | NAME |  TYPE |  COL | VALUE |
------------------------------------
|  4 |  AC1 |    LO | Col1 |     1 |
|  4 |  AC1 |    LO | Col2 |    10 |
|  4 |  AC1 |    LO | Col3 |     2 |
|  4 |  AC1 |    HI | Col1 |     2 |

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

person Taryn    schedule 19.04.2013