Reputation: 177
I currently have 2 SQL tables that look like this:
and...
I need to write a SELECT statement that retrieves all products from the DataTable that contain rows that match the FilterTable.
So based on my example tables above, if I were to run the query, it would return the following result:
I recently found a question that kind of attempts this: SQL query where ALL records in a join match a condition? but have been unsuccessful in implementing something similar
Note - I am using Microsoft SQL Server 2008
Upvotes: 4
Views: 40009
Reputation: 911
Here is an option using a couple of PIVOTs
DECLARE @Data table ([ID] INT, [Name] VARCHAR(12), [Value] VARCHAR(2) )
DECLARE @Filter TABLE ( [Name] VARCHAR(12), [Value] VARCHAR(2) )
INSERT INTO @Data ([ID], [Name], [Value])
VALUES (1, 'productname', 'A')
,(1, 'cost', '20')
,(1, 'active', 'Y')
,(2, 'productname', 'A')
,(2, 'cost', '20')
,(2, 'active', 'N')
,(3, 'productname', 'B')
,(3, 'cost', '20')
,(3, 'active', 'Y')
,(4, 'productname', 'A')
,(4, 'cost', '20')
,(4, 'active', 'Y')
INSERT INTO @Filter ([Name], [Value])
VALUES ('productname', 'A')
,('active', 'Y');
SELECT *
FROM ( SELECT *
FROM (select [ID], [Name], [value] from @Data) as s
PIVOT
( MAX([value]) FOR [name] in ( [productname], [active])
) as pvt) B
INNER JOIN
( SELECT *
FROM (select [name], [value] from @Filter) as f
PIVOT
( MAX([value]) for [Name] IN ([productname], [active])
) AS fpvt
) F
ON F.active = b.active and f.productname = b.productname
By doing a PIVOT on the DATA table and then on the FILTER table, it allows them to be lined up for an inner join. This returns the records that match within both,
Upvotes: 0
Reputation: 43636
In case you can use sp_executesql (you are using procedure).
SET NOCOUNT ON
GO
CREATE TABLE Data
(
[ID] INT
,[Name] VARCHAR(12)
,[Value] VARCHAR(2)
)
CREATE TABLE Filter
(
[Name] VARCHAR(12)
,[Value] VARCHAR(2)
)
INSERT INTO Data ([ID], [Name], [Value])
VALUES (1, 'productname', 'A')
,(1, 'cost', '20')
,(1, 'active', 'Y')
,(2, 'productname', 'A')
,(2, 'cost', '20')
,(2, 'active', 'N')
,(3, 'productname', 'B')
,(3, 'cost', '20')
,(3, 'active', 'Y')
,(4, 'productname', 'A')
,(4, 'cost', '20')
,(4, 'active', 'Y')
INSERT INTO Filter ([Name], [Value])
VALUES ('productname', 'A')
,('active', 'Y')
DECLARE @SQLColumns NVARCHAR(MAX) = SUBSTRING((SELECT DISTINCT ',[' +[Name] +']' FROM Data FOR XML PATH('')),2,4000)
DECLARE @SQLFilterColumns NVARCHAR(MAX) = SUBSTRING((SELECT 'AND [' +[Name] +'] = ''' + [Value] + ''' ' FROM Filter FOR XML PATH('')),4,4000)
DECLARE @SQLStatement NVARCHAR(MAX) = N'
;WITH DataSource ([ID]) AS
(
SELECT [ID]
FROM
(
SELECT [ID]
,[Name]
,[Value]
FROM Data
) DataSource
PIVOT
(
MAX([Value]) FOR [Name] IN (' + @SQLColumns+ ')
) PVT
WHERE ' + @SQLFilterColumns + '
)
SELECT DT.[ID]
,DT.[Name]
,DT.[Value]
FROM Data DT
INNER JOIN DataSource DS
ON DT.[ID] = DS.[ID]
'
EXECUTE sp_executesql @SQLStatement
DROP TABLE Data
DROP TABLE Filter
SET NOCOUNT OFF
GO
Upvotes: 0
Reputation: 633
This will work:
SELECT * FROM Data WHERE ID NOT IN (
SELECT ID FROM Data JOIN Filter
on Data.Name = Filter.Name and Data.Value <> Filter.Value
)
I set up a SQL Fiddle if you want to try other things: http://sqlfiddle.com/#!3/38b87/6
EDIT:
Better answer:
SELECT *
FROM DATA
WHERE ID NOT IN (
SELECT ID
FROM DATA
JOIN Filter ON DATA.Name = Filter.Name
AND DATA.Value <> Filter.Value
) AND ID IN
(
SELECT ID
FROM DATA
JOIN Filter ON DATA.Name = Filter.Name
)
This now fits where there is at least one filter that matches, and none that don't.
Upvotes: 2
Reputation: 62841
This is a little complicated, but here is one solution. Basically you need to check to see how many records from the datatable match all the records from the filtertable. This uses a subquery to do that:
SELECT *
FROM DataTable
WHERE ID IN (
SELECT DT.ID
FROM DataTable DT
JOIN FilterTable FT ON FT.Name = DT.Name
AND FT.Value = DT.VALUE
GROUP BY DT.ID
HAVING COUNT(*) = (SELECT COUNT(*) FROM FilterTable)
)
Upvotes: 17