Фильтрация T-SQL по динамическим парам имя-значение - PullRequest
0 голосов
/ 29 мая 2009

Я опишу, чего я пытаюсь достичь:

Я передаю в SP XML-файл с парами имя-значение, которые я помещаю в табличную переменную, скажем, @nameValuePairs. Мне нужно получить список идентификаторов для выражений (таблица) с точным соответствием пар имя-значение (атрибуты, другая таблица), связанных.

Это моя схема:

Таблица выражений -> (expressionId, attributeId)

Таблица атрибутов -> (attributeId, attributeName, attributeValue)

После того, как я попробовал сложные вещи с динамическим SQL и злыми курсорами (что работает, но мучительно медленно), вот что я получил сейчас:

--do the magic plz!

-- retrieve number of name-value pairs
SET @noOfAttributes = select count(*) from @nameValuePairs

select distinct
    e.expressionId, a.attributeName, a.attributeValue
into 
    #temp
from 
    expressions e
join
    attributes a
on
    e.attributeId = a.attributeId
join --> this join does the filtering
    @nameValuePairs nvp
on 
    a.attributeName = nvp.name and a.attributeValue = nvp.value
group by
    e.expressionId, a.attributeName, a.attributeValue

-- now select the IDs I need
-- since I did a select distinct above if the number of matches
-- for a given ID is the same as noOfAttributes then BINGO!
select distinct 
    expressionId
from 
    #temp
group by expressionId
having count(*) = @noOfAttributes 

Могут ли люди просмотреть и посмотреть, могут ли они обнаружить какие-либо проблемы? Есть ли лучший способ сделать это?

Любая помощь приветствуется!

Ответы [ 3 ]

1 голос
/ 29 мая 2009

Я вижу одну ошибку: у вас нет таблицы с псевдонимом b, но вы используете: a.attributeId = b.attributeId.

Попробуйте исправить это и посмотрите, работает ли оно, если я что-то не упустил.

РЕДАКТИРОВАТЬ: я думаю, что вы только что исправили это в своем редактировании, но это должен быть a.attributeId = e.attributeId?

1 голос
/ 29 мая 2009

Я верю, что это удовлетворит требование, которое вы пытаетесь выполнить. Я не уверен, насколько он красивее, но он должен работать и не потребовать временную таблицу:

SET @noOfAttributes = select count(*) from @nameValuePairs

SELECT e.expressionid
FROM expression e
LEFT JOIN (
           SELECT attributeid
           FROM attributes a
           JOIN @nameValuePairs nvp ON nvp.name = a.Name AND nvp.Value = a.value 
           ) t ON t.attributeid = e.attributeid
GROUP BY e.expressionid
HAVING SUM(CASE WHEN t.attributeid IS NULL THEN (@noOfAttributes + 1) ELSE 1 END) = @noOfAttributes

РЕДАКТИРОВАТЬ: После некоторой дополнительной оценки я обнаружил проблему, в которую были бы включены определенные выражения, которых не должно было быть. Я изменил свой запрос, чтобы учесть это.

1 голос
/ 29 мая 2009

Это неплохой подход, в зависимости от размеров и индексов таблиц, включая @nameValuePairs. Если число этих строк велико или оно становится медленным, лучше вместо этого поместить @namValuePairs во временную таблицу, добавить соответствующие индексы и использовать один запрос вместо двух отдельных.

Я заметил, что вы помещаете столбцы в #temp, который вы не используете, будет быстрее исключить их (хотя это будет означать дублирование строк в #temp). Кроме того, во втором запросе столбцы «различаются» и «группируются по» в одних и тех же столбцах. Вам не нужно и то и другое, поэтому я бы отбросил «отчетливый» (вероятно, это не повлияет на производительность, потому что оптимизатор уже понял это).

Наконец, #temp, вероятно, будет быстрее с кластеризованным неуникальным индексом на expressionid (я предполагаю, что это SQL 2005). Вы можете добавить его после SELECT..INTO, но обычно это так же быстро или быстро добавить его перед загрузкой. Это потребует от вас сначала СОЗДАТЬ #temp, добавить кластеризованный, а затем использовать INSERT..SELECT, чтобы загрузить его.

Я добавлю пример объединения запросов за несколько минут ... Хорошо, вот один из способов объединить их в один запрос (это также должно быть совместимо с 2000):

-- retrieve number of name-value pairs
SET @noOfAttributes = select count(*) from @nameValuePairs

-- now select the IDs I need
-- since I did a select distinct above if the number of matches
-- for a given ID is the same as noOfAttributes then BINGO!
select
    expressionId
from 
    (
        select distinct
            e.expressionId, a.attributeName, a.attributeValue
        from 
            expressions e
        join
            attributes a
        on
            e.attributeId = a.attributeId
        join --> this join does the filtering
            @nameValuePairs nvp
        on 
            a.attributeName = nvp.name and a.attributeValue = nvp.value
    ) as Temp
group by expressionId
having count(*) = @noOfAttributes
...