Search code examples
sql-servert-sqldynamic-datafiltering

T-SQL filtering on dynamic name-value pairs


I'll describe what I am trying to achieve:

I am passing down to a SP an xml with name value pairs that I put into a table variable, let's say @nameValuePairs. I need to retrieve a list of IDs for expressions (a table) with those exact match of name-value pairs (attributes, another table) associated.

This is my schema:

Expressions table --> (expressionId, attributeId)

Attributes table --> (attributeId, attributeName, attributeValue)

After trying complicated stuff with dynamic SQL and evil cursors (which works but it's painfully slow) this is what I've got now:

--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 

Can people please review and see if they can spot any problems? Is there a better way of doing this?

Any help appreciated!


Solution

  • I belive that this would satisfy the requirement you're trying to meet. I'm not sure how much prettier it is, but it should work and wouldn't require a temp table:

    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
    

    EDIT: After doing some more evaluation, I found an issue where certain expressions would be included that shouldn't have been. I've modified my query to take that in to account.