ВЫБЕРИТЕ с вычисляемым столбцом, который зависит от корреляции

StackOverflow https://stackoverflow.com/questions/1290681

Вопрос

Я не так уж много работаю с SQL, и большую часть времени я выполняю операции CRUD.Иногда у меня получается что-то немного более сложное.Итак, этот вопрос может быть вопросом новичка, но я готов.Я просто пытался разобраться в этом в течение нескольких часов, и это было бесполезно.

Итак, представьте себе следующую структуру таблицы:

> | ID | Col1 | Col2 | Col3 | .. | Col8 |

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

Итак, из этого примера данных:

> | 1 | 'a' | 'b' | 1 | 2 |  
> | 2 | 'b' | 'c' | 1 | 2 |  
> | 3 | 'b' | 'c' | 4 | 5 |  
> | 4 | 'x' | 'x' | 9 | 9 |  

Я хочу запросить Col1 = 'a' ИЛИ Col2 = 'c' ИЛИ Col3 = 1 ИЛИ Col4 = 5, где вычисленный результат > 1 и результирующий набор выглядит следующим образом:

> | ID | Cal |
> | 1  |  2  |
> | 2  |  2  |
> | 3  |  2  |

Я использую T-SQL и SQL Server 2005, если это имеет значение, и я не могу изменить схему БД.

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

Это было полезно?

Решение

Этот ответ будет работать с SQL 2005, используя CTE для небольшой очистки производной таблицы.

WITH Matches AS
(
    SELECT ID, CASE WHEN Col1 = 'a' THEN 1 ELSE 0 END + 
                CASE WHEN Col2 = 'c' THEN 1 ELSE 0 END +
                CASE WHEN Col3 = 1  THEN 1 ELSE 0 END +
                CASE WHEN Col4 = 5  THEN 1 ELSE 0 END AS Result
    FROM Table1
    WHERE Col1 = 'a' OR Col2 = 'c' OR Col3 = 1 OR Col4 = 5 
)
SELECT ID, Result
FROM Matches
WHERE Result > 1 

Другие советы

Вот решение, которое использует тот факт, что логическое сравнение возвращает целые числа 1 или 0:

SELECT * FROM (
  SELECT ID, (Col1='a') + (Col2='c') + (Col3=1) + (Col4=5) AS calculated
  FROM MyTable
) q
WHERE calculated > 1; 

Обратите внимание, что вы должны заключить логические сравнения в скобки, потому что + имеет более высокий приоритет, чем =.Кроме того, вы должны поместить все это в подзапрос, потому что обычно вы не можете использовать псевдоним столбца в WHERE предложение того же запроса.

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


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

SELECT * FROM (
  SELECT ID, 
      CASE WHEN Col1='a' THEN 1 ELSE 0 END
    + CASE WHEN Col2='c' THEN 1 ELSE 0 END 
    + CASE WHEN Col3=1   THEN 1 ELSE 0 END
    + CASE WHEN Col4=5   THEN 1 ELSE 0 END AS calculated
  FROM MyTable
) q
WHERE calculated > 1;

Этот запрос более удобен для индексации:

SELECT  id, SUM(match)
FROM    (
        SELECT  id, 1 AS match
        FROM    mytable
        WHERE   col1 = 'a'
        UNION ALL
        SELECT  id, 1 AS match
        FROM    mytable
        WHERE   col2 = 'c'
        UNION ALL
        SELECT  id, 1 AS match
        FROM    mytable
        WHERE   col3 = 1
        UNION ALL
        SELECT  id, 1 AS match
        FROM    mytable
        WHERE   col4 = 5
        ) q
GROUP BY
        id
HAVING  SUM(match) > 1

Это будет эффективно только в том случае, если ВСЕ столбцы, которые вы ищете, во-первых, проиндексированы и, во-вторых, имеют большое количество элементов (множество различных значений).

Смотрите эту статью в моем блоге для получения подробной информации о производительности:

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top