I am working with a set of what is essentially Attribute/Value pairs (there's actually quite a bit more to this, but I'm simplifying for the sake of this question). Effectively you can think of the tables as such:
Entities (EntityID,AttributeName,AttributeValue) PK=EntityID,AttributeName
Targets (TargetID,AttributeName,AttributeValue) PK=TargetID,AttributeName
How would you query with SQL the set of EntityID,TargetID for which an Entity has all the attributes for a target as well as the corresponding value?
EDIT (DDL as requested):
CREATE TABLE Entities(
EntityID INTEGER NOT NULL,
AttributeName CHAR(50) NOT NULL,
AttributeValue CHAR(50) NOT NULL,
CONSTRAINT EntitiesPK PRIMARY KEY (EntityID,AttributeName)
);
CREATE TABLE Targets(
TargetID INTEGER NOT NULL,
AttributeName CHAR(50) NOT NULL,
AttributeValue CHAR(50) NOT NULL,
CONSTRAINT TargetsPK PRIMARY KEY (TargetID,AttributeName)
);
Okay, I think after several tries and edits, this solution finally works:
SELECT e1.EntityID, t1.TargetID
FROM Entities e1
JOIN Entities e2 ON (e1.EntityID = e2.EntityID)
CROSS JOIN Targets t1
LEFT OUTER JOIN Targets t2 ON (t1.TargetID = t2.TargetID
AND e2.AttributeName = t2.AttributeName
AND e2.AttributeValue = t2.AttributeValue)
GROUP BY e1.EntityID, t1.TargetID
HAVING COUNT(e2.AttributeValue) = COUNT(t2.AttributeValue);
Test data:
INSERT INTO Entities VALUES
-- exact same attributes, should match
(1, 'Foo1', '123'),
(1, 'Bar1', '123'),
-- same attributes but different values, should not match
(2, 'Foo2', '456'),
(2, 'Bar2', '456'),
-- more columns in Entities, should not match
(3, 'Foo3', '789'),
(3, 'Bar3', '789'),
(3, 'Baz3', '789'),
-- fewer columns in Entities, should match
(4, 'Foo4', '012'),
(4, 'Bar4', '012'),
-- same as case 1, should match Target 1
(5, 'Foo1', '123'),
(5, 'Bar1', '123'),
-- one attribute with different value, should not match
(6, 'A', 'one'),
(6, 'B', 'two');
INSERT INTO Targets VALUES
(1, 'Foo1', '123'),
(1, 'Bar1', '123'),
(2, 'Foo2', 'abc'),
(2, 'Bar2', 'abc'),
(3, 'Foo3', '789'),
(3, 'Bar3', '789'),
(4, 'Foo4', '012'),
(4, 'Bar4', '012'),
(4, 'Baz4', '012'),
(6, 'A', 'one'),
(6, 'B', 'twox');
Test results:
+----------+----------+
| EntityID | TargetID |
+----------+----------+
| 1 | 1 |
| 4 | 4 |
| 5 | 1 |
+----------+----------+
To respond to your comment, here is a query with the tables reversed:
SELECT e1.EntityID, t1.TargetID
FROM Targets t1
JOIN Targets t2 ON (t1.TargetID = t2.TargetID)
CROSS JOIN Entities e1
LEFT OUTER JOIN Entities e2 ON (e1.EntityID = e2.EntityID
AND t2.AttributeName = e2.AttributeName
AND t2.AttributeValue = e2.AttributeValue)
GROUP BY e1.EntityID, t1.TargetID
HAVING COUNT(e2.AttributeValue) = COUNT(t2.AttributeValue);
And here's the output, given the same input data above.
+----------+----------+
| EntityID | TargetID |
+----------+----------+
| 1 | 1 |
| 3 | 3 |
| 5 | 1 |
+----------+----------+